agent-messenger 2.0.0 → 2.2.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/marketplace.json +14 -1
- package/.claude-plugin/plugin.json +4 -2
- package/.env.template +35 -17
- package/README.md +37 -33
- package/bun.lock +6 -6
- package/dist/package.json +11 -3
- 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/channeltalk/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/commands/auth.js +35 -28
- package/dist/src/platforms/channeltalk/commands/auth.js.map +1 -1
- package/dist/src/platforms/channeltalk/ensure-auth.js +6 -6
- package/dist/src/platforms/channeltalk/ensure-auth.js.map +1 -1
- package/dist/src/platforms/channeltalk/token-extractor.d.ts +23 -1
- package/dist/src/platforms/channeltalk/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/channeltalk/token-extractor.js +299 -29
- package/dist/src/platforms/channeltalk/token-extractor.js.map +1 -1
- package/dist/src/platforms/discord/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/auth.js +57 -49
- package/dist/src/platforms/discord/commands/auth.js.map +1 -1
- package/dist/src/platforms/discord/ensure-auth.js +3 -3
- package/dist/src/platforms/discord/ensure-auth.js.map +1 -1
- package/dist/src/platforms/discord/token-extractor.d.ts +6 -1
- package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/discord/token-extractor.js +167 -14
- package/dist/src/platforms/discord/token-extractor.js.map +1 -1
- package/dist/src/platforms/instagram/client.d.ts +2 -0
- package/dist/src/platforms/instagram/client.d.ts.map +1 -1
- package/dist/src/platforms/instagram/client.js +2 -2
- package/dist/src/platforms/instagram/client.js.map +1 -1
- package/dist/src/platforms/instagram/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/instagram/commands/auth.js +107 -14
- package/dist/src/platforms/instagram/commands/auth.js.map +1 -1
- package/dist/src/platforms/instagram/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/instagram/ensure-auth.js +57 -11
- package/dist/src/platforms/instagram/ensure-auth.js.map +1 -1
- package/dist/src/platforms/instagram/index.d.ts +1 -0
- package/dist/src/platforms/instagram/index.d.ts.map +1 -1
- package/dist/src/platforms/instagram/index.js +1 -0
- package/dist/src/platforms/instagram/index.js.map +1 -1
- package/dist/src/platforms/instagram/token-extractor.d.ts +44 -0
- package/dist/src/platforms/instagram/token-extractor.d.ts.map +1 -0
- package/dist/src/platforms/instagram/token-extractor.js +407 -0
- package/dist/src/platforms/instagram/token-extractor.js.map +1 -0
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +2 -1
- package/dist/src/platforms/kakaotalk/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/auth.js +14 -13
- package/dist/src/platforms/kakaotalk/commands/auth.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/connection.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/connection.js +2 -1
- package/dist/src/platforms/kakaotalk/protocol/connection.js.map +1 -1
- package/dist/src/platforms/line/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/line/commands/auth.js +6 -5
- package/dist/src/platforms/line/commands/auth.js.map +1 -1
- package/dist/src/platforms/slack/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/auth.js +11 -10
- package/dist/src/platforms/slack/commands/auth.js.map +1 -1
- package/dist/src/platforms/slack/token-extractor.d.ts +9 -0
- package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/slack/token-extractor.js +300 -23
- package/dist/src/platforms/slack/token-extractor.js.map +1 -1
- package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/auth.js +9 -8
- package/dist/src/platforms/teams/commands/auth.js.map +1 -1
- package/dist/src/platforms/teams/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/teams/ensure-auth.js +2 -1
- package/dist/src/platforms/teams/ensure-auth.js.map +1 -1
- package/dist/src/platforms/teams/token-extractor.d.ts +5 -0
- package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/teams/token-extractor.js +161 -29
- package/dist/src/platforms/teams/token-extractor.js.map +1 -1
- package/dist/src/platforms/telegram/client.d.ts.map +1 -1
- package/dist/src/platforms/telegram/client.js +25 -7
- package/dist/src/platforms/telegram/client.js.map +1 -1
- package/dist/src/platforms/telegram/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/telegram/commands/auth.js +6 -5
- package/dist/src/platforms/telegram/commands/auth.js.map +1 -1
- package/dist/src/platforms/webex/app-config.d.ts +7 -0
- package/dist/src/platforms/webex/app-config.d.ts.map +1 -0
- package/dist/src/platforms/webex/app-config.js +20 -0
- package/dist/src/platforms/webex/app-config.js.map +1 -0
- package/dist/src/platforms/webex/cli.d.ts +5 -0
- package/dist/src/platforms/webex/cli.d.ts.map +1 -0
- package/dist/src/platforms/webex/cli.js +32 -0
- package/dist/src/platforms/webex/cli.js.map +1 -0
- package/dist/src/platforms/webex/client.d.ts +55 -0
- package/dist/src/platforms/webex/client.d.ts.map +1 -0
- package/dist/src/platforms/webex/client.js +299 -0
- package/dist/src/platforms/webex/client.js.map +1 -0
- package/dist/src/platforms/webex/commands/auth.d.ts +19 -0
- package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/auth.js +166 -0
- package/dist/src/platforms/webex/commands/auth.js.map +1 -0
- package/dist/src/platforms/webex/commands/index.d.ts +6 -0
- package/dist/src/platforms/webex/commands/index.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/index.js +6 -0
- package/dist/src/platforms/webex/commands/index.js.map +1 -0
- package/dist/src/platforms/webex/commands/member.d.ts +7 -0
- package/dist/src/platforms/webex/commands/member.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/member.js +34 -0
- package/dist/src/platforms/webex/commands/member.js.map +1 -0
- package/dist/src/platforms/webex/commands/message.d.ts +26 -0
- package/dist/src/platforms/webex/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/message.js +153 -0
- package/dist/src/platforms/webex/commands/message.js.map +1 -0
- package/dist/src/platforms/webex/commands/snapshot.d.ts +9 -0
- package/dist/src/platforms/webex/commands/snapshot.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/snapshot.js +72 -0
- package/dist/src/platforms/webex/commands/snapshot.js.map +1 -0
- package/dist/src/platforms/webex/commands/space.d.ts +11 -0
- package/dist/src/platforms/webex/commands/space.d.ts.map +1 -0
- package/dist/src/platforms/webex/commands/space.js +59 -0
- package/dist/src/platforms/webex/commands/space.js.map +1 -0
- package/dist/src/platforms/webex/credential-manager.d.ts +23 -0
- package/dist/src/platforms/webex/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/webex/credential-manager.js +148 -0
- package/dist/src/platforms/webex/credential-manager.js.map +1 -0
- package/dist/src/platforms/webex/ensure-auth.d.ts +2 -0
- package/dist/src/platforms/webex/ensure-auth.d.ts.map +1 -0
- package/dist/src/platforms/webex/ensure-auth.js +36 -0
- package/dist/src/platforms/webex/ensure-auth.js.map +1 -0
- package/dist/src/platforms/webex/index.d.ts +8 -0
- package/dist/src/platforms/webex/index.d.ts.map +1 -0
- package/dist/src/platforms/webex/index.js +6 -0
- package/dist/src/platforms/webex/index.js.map +1 -0
- package/dist/src/platforms/webex/token-extractor.d.ts +28 -0
- package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -0
- package/dist/src/platforms/webex/token-extractor.js +344 -0
- package/dist/src/platforms/webex/token-extractor.js.map +1 -0
- package/dist/src/platforms/webex/types.d.ts +127 -0
- package/dist/src/platforms/webex/types.d.ts.map +1 -0
- package/dist/src/platforms/webex/types.js +64 -0
- package/dist/src/platforms/webex/types.js.map +1 -0
- package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/client.js +6 -2
- package/dist/src/platforms/whatsapp/client.js.map +1 -1
- package/dist/src/shared/utils/derived-key-cache.d.ts +1 -1
- package/dist/src/shared/utils/derived-key-cache.d.ts.map +1 -1
- package/dist/src/shared/utils/error-handler.d.ts +1 -1
- package/dist/src/shared/utils/error-handler.d.ts.map +1 -1
- package/dist/src/shared/utils/error-handler.js +3 -2
- package/dist/src/shared/utils/error-handler.js.map +1 -1
- package/dist/src/shared/utils/stderr.d.ts +5 -0
- package/dist/src/shared/utils/stderr.d.ts.map +1 -0
- package/dist/src/shared/utils/stderr.js +18 -0
- package/dist/src/shared/utils/stderr.js.map +1 -0
- package/dist/src/tui/adapters/webex-adapter.d.ts +14 -0
- package/dist/src/tui/adapters/webex-adapter.d.ts.map +1 -0
- package/dist/src/tui/adapters/webex-adapter.js +79 -0
- package/dist/src/tui/adapters/webex-adapter.js.map +1 -0
- package/dist/src/tui/app.d.ts.map +1 -1
- package/dist/src/tui/app.js +2 -0
- package/dist/src/tui/app.js.map +1 -1
- package/docs/content/docs/cli/channeltalk.mdx +7 -7
- package/docs/content/docs/cli/discord.mdx +3 -3
- package/docs/content/docs/cli/instagram.mdx +28 -6
- package/docs/content/docs/cli/meta.json +1 -0
- package/docs/content/docs/cli/slack.mdx +2 -2
- package/docs/content/docs/cli/teams.mdx +6 -4
- package/docs/content/docs/cli/webex.mdx +310 -0
- package/docs/content/docs/sdk/meta.json +1 -1
- package/docs/content/docs/sdk/webex.mdx +260 -0
- package/docs/content/docs/tui.mdx +4 -3
- package/docs/src/app/page.tsx +2 -2
- package/e2e/README.md +132 -8
- package/e2e/channeltalk.e2e.test.ts +2 -7
- package/e2e/channeltalkbot.e2e.test.ts +2 -6
- package/e2e/config.ts +172 -10
- package/e2e/helpers.ts +7 -0
- package/e2e/instagram.e2e.test.ts +97 -0
- package/e2e/kakaotalk.e2e.test.ts +74 -0
- package/e2e/line.e2e.test.ts +92 -0
- package/e2e/teams.e2e.test.ts +46 -1
- package/e2e/telegram.e2e.test.ts +84 -0
- package/e2e/webex.e2e.test.ts +190 -0
- package/e2e/whatsapp.e2e.test.ts +90 -0
- package/e2e/whatsappbot.e2e.test.ts +78 -0
- package/package.json +11 -3
- package/skills/agent-channeltalk/SKILL.md +9 -9
- package/skills/agent-channeltalk/references/authentication.md +21 -18
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +5 -5
- package/skills/agent-discord/references/authentication.md +8 -8
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-instagram/SKILL.md +51 -9
- package/skills/agent-instagram/references/authentication.md +35 -3
- package/skills/agent-kakaotalk/SKILL.md +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +5 -5
- package/skills/agent-slack/references/authentication.md +8 -8
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +6 -6
- package/skills/agent-teams/references/authentication.md +8 -8
- package/skills/agent-telegram/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +406 -0
- package/skills/agent-webex/references/authentication.md +371 -0
- package/skills/agent-webex/references/common-patterns.md +726 -0
- package/skills/agent-webex/templates/monitor-space.sh +165 -0
- package/skills/agent-webex/templates/post-message.sh +170 -0
- 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/channeltalk/commands/auth.test.ts +5 -5
- package/src/platforms/channeltalk/commands/auth.ts +38 -32
- package/src/platforms/channeltalk/ensure-auth.test.ts +6 -6
- package/src/platforms/channeltalk/ensure-auth.ts +6 -6
- package/src/platforms/channeltalk/token-extractor.test.ts +182 -15
- package/src/platforms/channeltalk/token-extractor.ts +344 -30
- package/src/platforms/discord/commands/auth.test.ts +3 -3
- package/src/platforms/discord/commands/auth.ts +58 -54
- package/src/platforms/discord/ensure-auth.test.ts +3 -3
- package/src/platforms/discord/ensure-auth.ts +3 -3
- package/src/platforms/discord/token-extractor.test.ts +199 -27
- package/src/platforms/discord/token-extractor.ts +190 -17
- package/src/platforms/instagram/client.ts +2 -2
- package/src/platforms/instagram/commands/auth.ts +133 -14
- package/src/platforms/instagram/ensure-auth.ts +63 -12
- package/src/platforms/instagram/index.ts +1 -0
- package/src/platforms/instagram/token-extractor.test.ts +424 -0
- package/src/platforms/instagram/token-extractor.ts +478 -0
- package/src/platforms/kakaotalk/client.ts +3 -1
- package/src/platforms/kakaotalk/commands/auth.ts +14 -13
- package/src/platforms/kakaotalk/protocol/connection.ts +3 -1
- package/src/platforms/line/commands/auth.ts +7 -6
- package/src/platforms/slack/cli.test.ts +6 -5
- package/src/platforms/slack/commands/auth.test.ts +11 -7
- package/src/platforms/slack/commands/auth.ts +11 -10
- package/src/platforms/slack/token-extractor.test.ts +98 -1
- package/src/platforms/slack/token-extractor.ts +338 -26
- package/src/platforms/teams/commands/auth.ts +9 -8
- package/src/platforms/teams/ensure-auth.ts +3 -1
- package/src/platforms/teams/token-extractor.test.ts +136 -17
- package/src/platforms/teams/token-extractor.ts +182 -31
- package/src/platforms/telegram/client.test.ts +134 -0
- package/src/platforms/telegram/client.ts +27 -6
- package/src/platforms/telegram/commands/auth.ts +6 -5
- package/src/platforms/webex/app-config.test.ts +98 -0
- package/src/platforms/webex/app-config.ts +31 -0
- package/src/platforms/webex/cli.test.ts +58 -0
- package/src/platforms/webex/cli.ts +39 -0
- package/src/platforms/webex/client.test.ts +743 -0
- package/src/platforms/webex/client.ts +405 -0
- package/src/platforms/webex/commands/auth.test.ts +222 -0
- package/src/platforms/webex/commands/auth.ts +243 -0
- package/src/platforms/webex/commands/index.ts +5 -0
- package/src/platforms/webex/commands/member.test.ts +112 -0
- package/src/platforms/webex/commands/member.ts +45 -0
- package/src/platforms/webex/commands/message.test.ts +235 -0
- package/src/platforms/webex/commands/message.ts +204 -0
- package/src/platforms/webex/commands/snapshot.test.ts +105 -0
- package/src/platforms/webex/commands/snapshot.ts +91 -0
- package/src/platforms/webex/commands/space.test.ts +216 -0
- package/src/platforms/webex/commands/space.ts +74 -0
- package/src/platforms/webex/credential-manager.test.ts +314 -0
- package/src/platforms/webex/credential-manager.ts +197 -0
- package/src/platforms/webex/ensure-auth.test.ts +89 -0
- package/src/platforms/webex/ensure-auth.ts +38 -0
- package/src/platforms/webex/index.test.ts +25 -0
- package/src/platforms/webex/index.ts +19 -0
- package/src/platforms/webex/token-extractor.test.ts +327 -0
- package/src/platforms/webex/token-extractor.ts +393 -0
- package/src/platforms/webex/types.test.ts +307 -0
- package/src/platforms/webex/types.ts +129 -0
- package/src/platforms/whatsapp/client.ts +11 -7
- package/src/shared/utils/derived-key-cache.ts +1 -1
- package/src/shared/utils/error-handler.ts +4 -2
- package/src/shared/utils/stderr.ts +22 -0
- package/src/tui/adapters/webex-adapter.ts +103 -0
- package/src/tui/app.ts +2 -0
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
|
|
3
|
+
import { handleError } from '@/shared/utils/error-handler'
|
|
4
|
+
import { formatOutput } from '@/shared/utils/output'
|
|
5
|
+
import { info, debug } from '@/shared/utils/stderr'
|
|
6
|
+
|
|
7
|
+
import { getWebexAppCredentials } from '../app-config'
|
|
8
|
+
import { WebexClient } from '../client'
|
|
9
|
+
import { WebexCredentialManager } from '../credential-manager'
|
|
10
|
+
import { WebexTokenExtractor } from '../token-extractor'
|
|
11
|
+
|
|
12
|
+
interface ResolvedCredentials {
|
|
13
|
+
clientId: string
|
|
14
|
+
clientSecret: string
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function openBrowser(url: string): Promise<void> {
|
|
18
|
+
const { exec } = await import('node:child_process')
|
|
19
|
+
const command =
|
|
20
|
+
process.platform === 'darwin'
|
|
21
|
+
? `open "${url}"`
|
|
22
|
+
: process.platform === 'win32'
|
|
23
|
+
? `start "" "${url}"`
|
|
24
|
+
: `xdg-open "${url}"`
|
|
25
|
+
exec(command)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function resolveClientCredentials(options: {
|
|
29
|
+
clientId?: string
|
|
30
|
+
clientSecret?: string
|
|
31
|
+
}): Promise<ResolvedCredentials> {
|
|
32
|
+
// 1. CLI flags
|
|
33
|
+
if (options.clientId || options.clientSecret) {
|
|
34
|
+
if (!options.clientId || !options.clientSecret) {
|
|
35
|
+
throw new Error('Both --client-id and --client-secret must be provided together.')
|
|
36
|
+
}
|
|
37
|
+
return { clientId: options.clientId, clientSecret: options.clientSecret }
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// 2. Env vars → 3. Built-in defaults (always resolves)
|
|
41
|
+
return getWebexAppCredentials()
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export async function loginAction(options: { token?: string; clientId?: string; clientSecret?: string; pretty?: boolean }): Promise<void> {
|
|
45
|
+
try {
|
|
46
|
+
const credManager = new WebexCredentialManager()
|
|
47
|
+
|
|
48
|
+
if (options.token) {
|
|
49
|
+
const client = await new WebexClient().login({ token: options.token })
|
|
50
|
+
const person = await client.testAuth()
|
|
51
|
+
await credManager.saveConfig({
|
|
52
|
+
accessToken: options.token,
|
|
53
|
+
refreshToken: '',
|
|
54
|
+
expiresAt: 0,
|
|
55
|
+
tokenType: 'manual',
|
|
56
|
+
})
|
|
57
|
+
console.log(
|
|
58
|
+
formatOutput(
|
|
59
|
+
{
|
|
60
|
+
user: { id: person.id, displayName: person.displayName, emails: person.emails },
|
|
61
|
+
authenticated: true,
|
|
62
|
+
},
|
|
63
|
+
options.pretty,
|
|
64
|
+
),
|
|
65
|
+
)
|
|
66
|
+
return
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const { clientId, clientSecret } = await resolveClientCredentials(options)
|
|
70
|
+
|
|
71
|
+
const device = await credManager.requestDeviceCode(clientId)
|
|
72
|
+
|
|
73
|
+
info(`Open this URL and enter the code: ${device.verificationUri}`)
|
|
74
|
+
info(`Code: ${device.userCode}`)
|
|
75
|
+
info('')
|
|
76
|
+
await openBrowser(device.verificationUriComplete)
|
|
77
|
+
info('Waiting for authorization...')
|
|
78
|
+
|
|
79
|
+
const config = await credManager.pollDeviceToken(
|
|
80
|
+
device.deviceCode,
|
|
81
|
+
device.interval,
|
|
82
|
+
device.expiresIn,
|
|
83
|
+
clientId,
|
|
84
|
+
clientSecret,
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
await credManager.saveConfig({ ...config, clientId, clientSecret, tokenType: 'oauth' })
|
|
88
|
+
|
|
89
|
+
const client = await new WebexClient().login({ token: config.accessToken })
|
|
90
|
+
const person = await client.testAuth()
|
|
91
|
+
|
|
92
|
+
console.log(
|
|
93
|
+
formatOutput(
|
|
94
|
+
{
|
|
95
|
+
user: { id: person.id, displayName: person.displayName, emails: person.emails },
|
|
96
|
+
authenticated: true,
|
|
97
|
+
},
|
|
98
|
+
options.pretty,
|
|
99
|
+
),
|
|
100
|
+
)
|
|
101
|
+
} catch (error) {
|
|
102
|
+
handleError(error as Error)
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export async function statusAction(options: { pretty?: boolean }): Promise<void> {
|
|
107
|
+
try {
|
|
108
|
+
const credManager = new WebexCredentialManager()
|
|
109
|
+
const config = await credManager.loadConfig()
|
|
110
|
+
const token = await credManager.getToken(config?.clientId, config?.clientSecret)
|
|
111
|
+
|
|
112
|
+
if (!token) {
|
|
113
|
+
console.log(
|
|
114
|
+
formatOutput({ error: 'Not authenticated. Run "auth login" first.' }, options.pretty),
|
|
115
|
+
)
|
|
116
|
+
process.exit(1)
|
|
117
|
+
return
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
const client = await new WebexClient().login({ token })
|
|
122
|
+
const person = await client.testAuth()
|
|
123
|
+
console.log(
|
|
124
|
+
formatOutput(
|
|
125
|
+
{
|
|
126
|
+
authenticated: true,
|
|
127
|
+
user: { id: person.id, displayName: person.displayName, emails: person.emails },
|
|
128
|
+
},
|
|
129
|
+
options.pretty,
|
|
130
|
+
),
|
|
131
|
+
)
|
|
132
|
+
} catch {
|
|
133
|
+
console.log(formatOutput({ authenticated: false, user: null }, options.pretty))
|
|
134
|
+
}
|
|
135
|
+
} catch (error) {
|
|
136
|
+
handleError(error as Error)
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export async function extractAction(options: { pretty?: boolean; debug?: boolean }): Promise<void> {
|
|
141
|
+
try {
|
|
142
|
+
const extractor = new WebexTokenExtractor(
|
|
143
|
+
undefined,
|
|
144
|
+
options.debug ? (msg) => debug(`[debug] ${msg}`) : undefined,
|
|
145
|
+
)
|
|
146
|
+
|
|
147
|
+
if (options.debug) {
|
|
148
|
+
debug('[debug] Searching browser profiles for Webex tokens...')
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
const extracted = await extractor.extract()
|
|
152
|
+
|
|
153
|
+
if (!extracted) {
|
|
154
|
+
console.log(
|
|
155
|
+
formatOutput(
|
|
156
|
+
{
|
|
157
|
+
error: 'No Webex token found in any browser. Make sure you are logged in to web.webex.com in Chrome, Edge, Arc, or Brave.',
|
|
158
|
+
hint: 'Run "auth login" for OAuth Device Grant flow, or --debug for more info.',
|
|
159
|
+
},
|
|
160
|
+
options.pretty,
|
|
161
|
+
),
|
|
162
|
+
)
|
|
163
|
+
process.exit(1)
|
|
164
|
+
return
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const client = await new WebexClient().login({ token: extracted.accessToken })
|
|
168
|
+
const person = await client.testAuth()
|
|
169
|
+
|
|
170
|
+
const credManager = new WebexCredentialManager()
|
|
171
|
+
await credManager.saveConfig({
|
|
172
|
+
accessToken: extracted.accessToken,
|
|
173
|
+
refreshToken: extracted.refreshToken ?? '',
|
|
174
|
+
expiresAt: extracted.expiresAt ?? 0,
|
|
175
|
+
tokenType: 'extracted',
|
|
176
|
+
deviceUrl: extracted.deviceUrl,
|
|
177
|
+
})
|
|
178
|
+
|
|
179
|
+
console.log(
|
|
180
|
+
formatOutput(
|
|
181
|
+
{
|
|
182
|
+
user: { id: person.id, displayName: person.displayName, emails: person.emails },
|
|
183
|
+
authenticated: true,
|
|
184
|
+
tokenType: 'extracted',
|
|
185
|
+
},
|
|
186
|
+
options.pretty,
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
} catch (error) {
|
|
190
|
+
handleError(error as Error)
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
export async function logoutAction(options: { pretty?: boolean }): Promise<void> {
|
|
195
|
+
try {
|
|
196
|
+
const credManager = new WebexCredentialManager()
|
|
197
|
+
const config = await credManager.loadConfig()
|
|
198
|
+
|
|
199
|
+
if (!config) {
|
|
200
|
+
console.log(
|
|
201
|
+
formatOutput({ error: 'Not authenticated. Run "auth login" first.' }, options.pretty),
|
|
202
|
+
)
|
|
203
|
+
process.exit(1)
|
|
204
|
+
return
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
await credManager.clearCredentials()
|
|
208
|
+
console.log(formatOutput({ removed: 'webex', success: true }, options.pretty))
|
|
209
|
+
} catch (error) {
|
|
210
|
+
handleError(error as Error)
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export const authCommand = new Command('auth')
|
|
215
|
+
.description('Authentication commands')
|
|
216
|
+
.addCommand(
|
|
217
|
+
new Command('login')
|
|
218
|
+
.description('Login to Webex')
|
|
219
|
+
.option('--token <token>', 'Use a bot token or personal access token directly')
|
|
220
|
+
.option('--client-id <id>', 'Webex Integration client ID')
|
|
221
|
+
.option('--client-secret <secret>', 'Webex Integration client secret')
|
|
222
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
223
|
+
.action(loginAction),
|
|
224
|
+
)
|
|
225
|
+
.addCommand(
|
|
226
|
+
new Command('extract')
|
|
227
|
+
.description('Extract Webex token from browser (Chrome, Edge, Arc, Brave)')
|
|
228
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
229
|
+
.option('--debug', 'Show debug output')
|
|
230
|
+
.action(extractAction),
|
|
231
|
+
)
|
|
232
|
+
.addCommand(
|
|
233
|
+
new Command('status')
|
|
234
|
+
.description('Show authentication status')
|
|
235
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
236
|
+
.action(statusAction),
|
|
237
|
+
)
|
|
238
|
+
.addCommand(
|
|
239
|
+
new Command('logout')
|
|
240
|
+
.description('Logout from Webex')
|
|
241
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
242
|
+
.action(logoutAction),
|
|
243
|
+
)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { WebexClient } from '../client'
|
|
4
|
+
import { WebexError } from '../types'
|
|
5
|
+
import { listAction } from './member'
|
|
6
|
+
|
|
7
|
+
describe('member commands', () => {
|
|
8
|
+
let consoleSpy: ReturnType<typeof spyOn>
|
|
9
|
+
let consoleErrorSpy: ReturnType<typeof spyOn>
|
|
10
|
+
let processExitSpy: ReturnType<typeof spyOn>
|
|
11
|
+
let stderrOutput: string
|
|
12
|
+
let origStderrWrite: typeof process.stderr.write
|
|
13
|
+
const mockMembers = [
|
|
14
|
+
{
|
|
15
|
+
id: 'mem-1',
|
|
16
|
+
roomId: 'room-1',
|
|
17
|
+
personId: 'person-1',
|
|
18
|
+
personEmail: 'alice@example.com',
|
|
19
|
+
personDisplayName: 'Alice',
|
|
20
|
+
isModerator: true,
|
|
21
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'mem-2',
|
|
25
|
+
roomId: 'room-1',
|
|
26
|
+
personId: 'person-2',
|
|
27
|
+
personEmail: 'bob@example.com',
|
|
28
|
+
personDisplayName: 'Bob',
|
|
29
|
+
isModerator: false,
|
|
30
|
+
created: '2024-01-02T00:00:00.000Z',
|
|
31
|
+
},
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
beforeEach(() => {
|
|
35
|
+
consoleSpy = spyOn(console, 'log').mockImplementation(() => {})
|
|
36
|
+
consoleErrorSpy = spyOn(console, 'error').mockImplementation(() => {})
|
|
37
|
+
processExitSpy = spyOn(process, 'exit').mockImplementation((_code?: number) => {
|
|
38
|
+
throw new Error(`process.exit(${_code})`)
|
|
39
|
+
})
|
|
40
|
+
stderrOutput = ''
|
|
41
|
+
origStderrWrite = process.stderr.write
|
|
42
|
+
process.stderr.write = ((chunk: string | Uint8Array) => {
|
|
43
|
+
stderrOutput += typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk)
|
|
44
|
+
return true
|
|
45
|
+
}) as typeof process.stderr.write
|
|
46
|
+
spyOn(WebexClient.prototype, 'login').mockResolvedValue(new WebexClient() as any)
|
|
47
|
+
spyOn(WebexClient.prototype, 'listMemberships').mockResolvedValue(mockMembers)
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
process.stderr.write = origStderrWrite
|
|
52
|
+
mock.restore()
|
|
53
|
+
})
|
|
54
|
+
|
|
55
|
+
test('listAction calls listMemberships with spaceId and outputs mapped members', async () => {
|
|
56
|
+
const listMembershipsSpy = spyOn(WebexClient.prototype, 'listMemberships').mockResolvedValue(
|
|
57
|
+
mockMembers,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
await listAction('room-1', {})
|
|
61
|
+
|
|
62
|
+
expect(listMembershipsSpy).toHaveBeenCalledWith('room-1', { max: undefined })
|
|
63
|
+
expect(consoleSpy).toHaveBeenCalledWith(
|
|
64
|
+
JSON.stringify([
|
|
65
|
+
{
|
|
66
|
+
id: 'mem-1',
|
|
67
|
+
personId: 'person-1',
|
|
68
|
+
personEmail: 'alice@example.com',
|
|
69
|
+
personDisplayName: 'Alice',
|
|
70
|
+
isModerator: true,
|
|
71
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
id: 'mem-2',
|
|
75
|
+
personId: 'person-2',
|
|
76
|
+
personEmail: 'bob@example.com',
|
|
77
|
+
personDisplayName: 'Bob',
|
|
78
|
+
isModerator: false,
|
|
79
|
+
created: '2024-01-02T00:00:00.000Z',
|
|
80
|
+
},
|
|
81
|
+
]),
|
|
82
|
+
)
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
test('listAction passes limit option to listMemberships', async () => {
|
|
86
|
+
const listMembershipsSpy = spyOn(WebexClient.prototype, 'listMemberships').mockResolvedValue(
|
|
87
|
+
mockMembers,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
await listAction('room-1', { limit: 25 })
|
|
91
|
+
|
|
92
|
+
expect(listMembershipsSpy).toHaveBeenCalledWith('room-1', { max: 25 })
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
test('listAction handles not-authenticated case', async () => {
|
|
96
|
+
spyOn(WebexClient.prototype, 'login').mockRejectedValue(
|
|
97
|
+
new WebexError('No Webex credentials found.', 'no_credentials'),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
await expect(listAction('room-1', {})).rejects.toThrow('process.exit(1)')
|
|
101
|
+
|
|
102
|
+
expect(stderrOutput).toContain('No Webex credentials found')
|
|
103
|
+
})
|
|
104
|
+
|
|
105
|
+
test('listAction handles API error', async () => {
|
|
106
|
+
spyOn(WebexClient.prototype, 'listMemberships').mockRejectedValue(new Error('API failure'))
|
|
107
|
+
|
|
108
|
+
await expect(listAction('room-1', {})).rejects.toThrow('process.exit(1)')
|
|
109
|
+
|
|
110
|
+
expect(processExitSpy).toHaveBeenCalledWith(1)
|
|
111
|
+
})
|
|
112
|
+
})
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
|
|
3
|
+
import { handleError } from '@/shared/utils/error-handler'
|
|
4
|
+
import { formatOutput } from '@/shared/utils/output'
|
|
5
|
+
|
|
6
|
+
import { WebexClient } from '../client'
|
|
7
|
+
|
|
8
|
+
export async function listAction(
|
|
9
|
+
spaceId: string,
|
|
10
|
+
options: { limit?: number; pretty?: boolean },
|
|
11
|
+
): Promise<void> {
|
|
12
|
+
try {
|
|
13
|
+
const client = await new WebexClient().login()
|
|
14
|
+
const members = await client.listMemberships(spaceId, { max: options.limit })
|
|
15
|
+
|
|
16
|
+
const output = members.map((m) => ({
|
|
17
|
+
id: m.id,
|
|
18
|
+
personId: m.personId,
|
|
19
|
+
personEmail: m.personEmail,
|
|
20
|
+
personDisplayName: m.personDisplayName,
|
|
21
|
+
isModerator: m.isModerator,
|
|
22
|
+
created: m.created,
|
|
23
|
+
}))
|
|
24
|
+
|
|
25
|
+
console.log(formatOutput(output, options.pretty))
|
|
26
|
+
} catch (error) {
|
|
27
|
+
handleError(error as Error)
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const memberCommand = new Command('member')
|
|
32
|
+
.description('Member commands')
|
|
33
|
+
.addCommand(
|
|
34
|
+
new Command('list')
|
|
35
|
+
.description('List members of a space')
|
|
36
|
+
.argument('<space-id>', 'Space ID')
|
|
37
|
+
.option('--limit <n>', 'Number of members to retrieve', '100')
|
|
38
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
39
|
+
.action((spaceId, options) =>
|
|
40
|
+
listAction(spaceId, {
|
|
41
|
+
limit: parseInt(options.limit, 10),
|
|
42
|
+
pretty: options.pretty,
|
|
43
|
+
}),
|
|
44
|
+
),
|
|
45
|
+
)
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { WebexClient } from '../client'
|
|
4
|
+
import { WebexError } from '../types'
|
|
5
|
+
import { deleteAction, dmAction, editAction, getAction, listAction, sendAction } from './message'
|
|
6
|
+
|
|
7
|
+
let clientSendMessageSpy: ReturnType<typeof spyOn>
|
|
8
|
+
let clientSendDirectMessageSpy: ReturnType<typeof spyOn>
|
|
9
|
+
let clientListMessagesSpy: ReturnType<typeof spyOn>
|
|
10
|
+
let clientGetMessageSpy: ReturnType<typeof spyOn>
|
|
11
|
+
let clientDeleteMessageSpy: ReturnType<typeof spyOn>
|
|
12
|
+
let clientEditMessageSpy: ReturnType<typeof spyOn>
|
|
13
|
+
let clientLoginSpy: ReturnType<typeof spyOn>
|
|
14
|
+
const originalConsoleLog = console.log
|
|
15
|
+
const originalConsoleError = console.error
|
|
16
|
+
|
|
17
|
+
const mockMessage = {
|
|
18
|
+
id: 'msg_123',
|
|
19
|
+
roomId: 'space_456',
|
|
20
|
+
roomType: 'group' as const,
|
|
21
|
+
text: 'Hello world',
|
|
22
|
+
personId: 'person_789',
|
|
23
|
+
personEmail: 'user@example.com',
|
|
24
|
+
created: '2025-01-29T10:00:00.000Z',
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const mockMessage2 = {
|
|
28
|
+
id: 'msg_124',
|
|
29
|
+
roomId: 'space_456',
|
|
30
|
+
roomType: 'group' as const,
|
|
31
|
+
text: 'Second message',
|
|
32
|
+
personId: 'person_789',
|
|
33
|
+
personEmail: 'user@example.com',
|
|
34
|
+
created: '2025-01-29T10:01:00.000Z',
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
beforeEach(() => {
|
|
38
|
+
clientLoginSpy = spyOn(WebexClient.prototype, 'login').mockResolvedValue(new WebexClient() as any)
|
|
39
|
+
|
|
40
|
+
clientSendMessageSpy = spyOn(WebexClient.prototype, 'sendMessage').mockResolvedValue(mockMessage)
|
|
41
|
+
|
|
42
|
+
clientSendDirectMessageSpy = spyOn(WebexClient.prototype, 'sendDirectMessage').mockResolvedValue(mockMessage)
|
|
43
|
+
|
|
44
|
+
clientListMessagesSpy = spyOn(WebexClient.prototype, 'listMessages').mockResolvedValue([
|
|
45
|
+
mockMessage,
|
|
46
|
+
mockMessage2,
|
|
47
|
+
])
|
|
48
|
+
|
|
49
|
+
clientGetMessageSpy = spyOn(WebexClient.prototype, 'getMessage').mockResolvedValue(mockMessage)
|
|
50
|
+
|
|
51
|
+
clientDeleteMessageSpy = spyOn(WebexClient.prototype, 'deleteMessage').mockResolvedValue(
|
|
52
|
+
undefined,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
clientEditMessageSpy = spyOn(WebexClient.prototype, 'editMessage').mockResolvedValue({
|
|
56
|
+
...mockMessage,
|
|
57
|
+
text: 'Updated message',
|
|
58
|
+
})
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
afterEach(() => {
|
|
62
|
+
clientLoginSpy?.mockRestore()
|
|
63
|
+
clientSendMessageSpy?.mockRestore()
|
|
64
|
+
clientSendDirectMessageSpy?.mockRestore()
|
|
65
|
+
clientListMessagesSpy?.mockRestore()
|
|
66
|
+
clientGetMessageSpy?.mockRestore()
|
|
67
|
+
clientDeleteMessageSpy?.mockRestore()
|
|
68
|
+
clientEditMessageSpy?.mockRestore()
|
|
69
|
+
console.log = originalConsoleLog
|
|
70
|
+
console.error = originalConsoleError
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
test('send: calls sendMessage with correct args and outputs result', async () => {
|
|
74
|
+
const consoleSpy = mock((_msg: string) => {})
|
|
75
|
+
console.log = consoleSpy
|
|
76
|
+
|
|
77
|
+
await sendAction('space_456', 'Hello world', { pretty: false })
|
|
78
|
+
|
|
79
|
+
expect(clientSendMessageSpy).toHaveBeenCalledWith('space_456', 'Hello world', {
|
|
80
|
+
markdown: undefined,
|
|
81
|
+
})
|
|
82
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
83
|
+
const output = consoleSpy.mock.calls[0][0]
|
|
84
|
+
expect(output).toContain('msg_123')
|
|
85
|
+
expect(output).toContain('space_456')
|
|
86
|
+
expect(output).toContain('user@example.com')
|
|
87
|
+
})
|
|
88
|
+
|
|
89
|
+
test('send: with --markdown passes markdown option', async () => {
|
|
90
|
+
const consoleSpy = mock((_msg: string) => {})
|
|
91
|
+
console.log = consoleSpy
|
|
92
|
+
|
|
93
|
+
await sendAction('space_456', '**bold**', { markdown: true, pretty: false })
|
|
94
|
+
|
|
95
|
+
expect(clientSendMessageSpy).toHaveBeenCalledWith('space_456', '**bold**', { markdown: true })
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('send: not authenticated shows error', async () => {
|
|
99
|
+
clientLoginSpy.mockRejectedValue(new WebexError('No Webex credentials found.', 'no_credentials'))
|
|
100
|
+
|
|
101
|
+
let stderrOutput = ''
|
|
102
|
+
const origWrite = process.stderr.write
|
|
103
|
+
process.stderr.write = ((chunk: string | Uint8Array) => {
|
|
104
|
+
stderrOutput += typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk)
|
|
105
|
+
return true
|
|
106
|
+
}) as typeof process.stderr.write
|
|
107
|
+
|
|
108
|
+
const originalExit = process.exit
|
|
109
|
+
process.exit = mock((_code?: number) => {
|
|
110
|
+
throw new Error('process.exit called')
|
|
111
|
+
}) as never
|
|
112
|
+
|
|
113
|
+
try {
|
|
114
|
+
await sendAction('space_456', 'Hello', { pretty: false })
|
|
115
|
+
} catch {
|
|
116
|
+
} finally {
|
|
117
|
+
process.exit = originalExit
|
|
118
|
+
process.stderr.write = origWrite
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
expect(stderrOutput).toContain('No Webex credentials found')
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
test('dm: calls sendDirectMessage with email and text', async () => {
|
|
125
|
+
const consoleSpy = mock((_msg: string) => {})
|
|
126
|
+
console.log = consoleSpy
|
|
127
|
+
|
|
128
|
+
await dmAction('alice@example.com', 'Hello!', { pretty: false })
|
|
129
|
+
|
|
130
|
+
expect(clientSendDirectMessageSpy).toHaveBeenCalledWith('alice@example.com', 'Hello!', {
|
|
131
|
+
markdown: undefined,
|
|
132
|
+
})
|
|
133
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
134
|
+
const output = consoleSpy.mock.calls[0][0]
|
|
135
|
+
expect(output).toContain('msg_123')
|
|
136
|
+
})
|
|
137
|
+
|
|
138
|
+
test('dm: with --markdown passes markdown option', async () => {
|
|
139
|
+
const consoleSpy = mock((_msg: string) => {})
|
|
140
|
+
console.log = consoleSpy
|
|
141
|
+
|
|
142
|
+
await dmAction('alice@example.com', '**bold**', { markdown: true, pretty: false })
|
|
143
|
+
|
|
144
|
+
expect(clientSendDirectMessageSpy).toHaveBeenCalledWith('alice@example.com', '**bold**', {
|
|
145
|
+
markdown: true,
|
|
146
|
+
})
|
|
147
|
+
})
|
|
148
|
+
|
|
149
|
+
test('list: calls listMessages with limit and outputs array', async () => {
|
|
150
|
+
const consoleSpy = mock((_msg: string) => {})
|
|
151
|
+
console.log = consoleSpy
|
|
152
|
+
|
|
153
|
+
await listAction('space_456', { limit: 50, pretty: false })
|
|
154
|
+
|
|
155
|
+
expect(clientListMessagesSpy).toHaveBeenCalledWith('space_456', { max: 50 })
|
|
156
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
157
|
+
const output = consoleSpy.mock.calls[0][0]
|
|
158
|
+
expect(output).toContain('msg_123')
|
|
159
|
+
expect(output).toContain('msg_124')
|
|
160
|
+
})
|
|
161
|
+
|
|
162
|
+
test('get: calls getMessage with correct id and outputs result', async () => {
|
|
163
|
+
const consoleSpy = mock((_msg: string) => {})
|
|
164
|
+
console.log = consoleSpy
|
|
165
|
+
|
|
166
|
+
await getAction('msg_123', { pretty: false })
|
|
167
|
+
|
|
168
|
+
expect(clientGetMessageSpy).toHaveBeenCalledWith('msg_123')
|
|
169
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
170
|
+
const output = consoleSpy.mock.calls[0][0]
|
|
171
|
+
expect(output).toContain('msg_123')
|
|
172
|
+
expect(output).toContain('user@example.com')
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test('delete: with --force calls deleteMessage and outputs deleted id', async () => {
|
|
176
|
+
const consoleSpy = mock((_msg: string) => {})
|
|
177
|
+
console.log = consoleSpy
|
|
178
|
+
|
|
179
|
+
await deleteAction('msg_123', { force: true, pretty: false })
|
|
180
|
+
|
|
181
|
+
expect(clientDeleteMessageSpy).toHaveBeenCalledWith('msg_123')
|
|
182
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
183
|
+
const output = consoleSpy.mock.calls[0][0]
|
|
184
|
+
expect(output).toContain('deleted')
|
|
185
|
+
expect(output).toContain('msg_123')
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
test('delete: without --force shows warning and does not delete', async () => {
|
|
189
|
+
const consoleSpy = mock((_msg: string) => {})
|
|
190
|
+
console.log = consoleSpy
|
|
191
|
+
|
|
192
|
+
const originalExit = process.exit
|
|
193
|
+
process.exit = mock((_code?: number) => {
|
|
194
|
+
throw new Error('process.exit called')
|
|
195
|
+
}) as never
|
|
196
|
+
|
|
197
|
+
try {
|
|
198
|
+
await deleteAction('msg_123', { force: false, pretty: false })
|
|
199
|
+
} catch {
|
|
200
|
+
} finally {
|
|
201
|
+
process.exit = originalExit
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
expect(clientDeleteMessageSpy).not.toHaveBeenCalled()
|
|
205
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
206
|
+
const output = consoleSpy.mock.calls[0][0]
|
|
207
|
+
expect(output).toContain('warning')
|
|
208
|
+
expect(output).toContain('--force')
|
|
209
|
+
})
|
|
210
|
+
|
|
211
|
+
test('edit: calls editMessage with roomId in args and outputs result', async () => {
|
|
212
|
+
const consoleSpy = mock((_msg: string) => {})
|
|
213
|
+
console.log = consoleSpy
|
|
214
|
+
|
|
215
|
+
await editAction('msg_123', 'space_456', 'Updated message', { pretty: false })
|
|
216
|
+
|
|
217
|
+
expect(clientEditMessageSpy).toHaveBeenCalledWith('msg_123', 'space_456', 'Updated message', {
|
|
218
|
+
markdown: undefined,
|
|
219
|
+
})
|
|
220
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
221
|
+
const output = consoleSpy.mock.calls[0][0]
|
|
222
|
+
expect(output).toContain('msg_123')
|
|
223
|
+
expect(output).toContain('Updated message')
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test('edit: with --markdown passes markdown option', async () => {
|
|
227
|
+
const consoleSpy = mock((_msg: string) => {})
|
|
228
|
+
console.log = consoleSpy
|
|
229
|
+
|
|
230
|
+
await editAction('msg_123', 'space_456', '**updated**', { markdown: true, pretty: false })
|
|
231
|
+
|
|
232
|
+
expect(clientEditMessageSpy).toHaveBeenCalledWith('msg_123', 'space_456', '**updated**', {
|
|
233
|
+
markdown: true,
|
|
234
|
+
})
|
|
235
|
+
})
|