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,204 @@
|
|
|
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
|
+
import type { WebexMessage } from '../types'
|
|
8
|
+
|
|
9
|
+
export async function sendAction(
|
|
10
|
+
spaceId: string,
|
|
11
|
+
text: string,
|
|
12
|
+
options: { markdown?: boolean; pretty?: boolean },
|
|
13
|
+
): Promise<void> {
|
|
14
|
+
try {
|
|
15
|
+
const client = await new WebexClient().login()
|
|
16
|
+
const message = await client.sendMessage(spaceId, text, { markdown: options.markdown })
|
|
17
|
+
|
|
18
|
+
const output = {
|
|
19
|
+
id: message.id,
|
|
20
|
+
roomId: message.roomId,
|
|
21
|
+
text: message.text,
|
|
22
|
+
personEmail: message.personEmail,
|
|
23
|
+
created: message.created,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
console.log(formatOutput(output, options.pretty))
|
|
27
|
+
} catch (error) {
|
|
28
|
+
handleError(error as Error)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export async function listAction(
|
|
33
|
+
spaceId: string,
|
|
34
|
+
options: { limit?: number; pretty?: boolean },
|
|
35
|
+
): Promise<void> {
|
|
36
|
+
try {
|
|
37
|
+
const client = await new WebexClient().login()
|
|
38
|
+
const limit = options.limit ?? 50
|
|
39
|
+
const messages = await client.listMessages(spaceId, { max: limit })
|
|
40
|
+
|
|
41
|
+
const output = messages.map((msg: WebexMessage) => ({
|
|
42
|
+
id: msg.id,
|
|
43
|
+
roomId: msg.roomId,
|
|
44
|
+
text: msg.text,
|
|
45
|
+
personEmail: msg.personEmail,
|
|
46
|
+
created: msg.created,
|
|
47
|
+
}))
|
|
48
|
+
|
|
49
|
+
console.log(formatOutput(output, options.pretty))
|
|
50
|
+
} catch (error) {
|
|
51
|
+
handleError(error as Error)
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export async function getAction(
|
|
56
|
+
messageId: string,
|
|
57
|
+
options: { pretty?: boolean },
|
|
58
|
+
): Promise<void> {
|
|
59
|
+
try {
|
|
60
|
+
const client = await new WebexClient().login()
|
|
61
|
+
const message = await client.getMessage(messageId)
|
|
62
|
+
|
|
63
|
+
const output = {
|
|
64
|
+
id: message.id,
|
|
65
|
+
roomId: message.roomId,
|
|
66
|
+
text: message.text,
|
|
67
|
+
personEmail: message.personEmail,
|
|
68
|
+
created: message.created,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(formatOutput(output, options.pretty))
|
|
72
|
+
} catch (error) {
|
|
73
|
+
handleError(error as Error)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export async function deleteAction(
|
|
78
|
+
messageId: string,
|
|
79
|
+
options: { force?: boolean; pretty?: boolean },
|
|
80
|
+
): Promise<void> {
|
|
81
|
+
try {
|
|
82
|
+
if (!options.force) {
|
|
83
|
+
console.log(
|
|
84
|
+
formatOutput({ warning: 'Use --force to confirm deletion', messageId }, options.pretty),
|
|
85
|
+
)
|
|
86
|
+
process.exit(0)
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const client = await new WebexClient().login()
|
|
90
|
+
await client.deleteMessage(messageId)
|
|
91
|
+
|
|
92
|
+
console.log(formatOutput({ deleted: messageId }, options.pretty))
|
|
93
|
+
} catch (error) {
|
|
94
|
+
handleError(error as Error)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export async function editAction(
|
|
99
|
+
messageId: string,
|
|
100
|
+
spaceId: string,
|
|
101
|
+
text: string,
|
|
102
|
+
options: { markdown?: boolean; pretty?: boolean },
|
|
103
|
+
): Promise<void> {
|
|
104
|
+
try {
|
|
105
|
+
const client = await new WebexClient().login()
|
|
106
|
+
const message = await client.editMessage(messageId, spaceId, text, {
|
|
107
|
+
markdown: options.markdown,
|
|
108
|
+
})
|
|
109
|
+
|
|
110
|
+
const output = {
|
|
111
|
+
id: message.id,
|
|
112
|
+
roomId: message.roomId,
|
|
113
|
+
text: message.text,
|
|
114
|
+
personEmail: message.personEmail,
|
|
115
|
+
created: message.created,
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
console.log(formatOutput(output, options.pretty))
|
|
119
|
+
} catch (error) {
|
|
120
|
+
handleError(error as Error)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function dmAction(
|
|
125
|
+
email: string,
|
|
126
|
+
text: string,
|
|
127
|
+
options: { markdown?: boolean; pretty?: boolean },
|
|
128
|
+
): Promise<void> {
|
|
129
|
+
try {
|
|
130
|
+
const client = await new WebexClient().login()
|
|
131
|
+
const message = await client.sendDirectMessage(email, text, { markdown: options.markdown })
|
|
132
|
+
|
|
133
|
+
const output = {
|
|
134
|
+
id: message.id,
|
|
135
|
+
roomId: message.roomId,
|
|
136
|
+
text: message.text,
|
|
137
|
+
personEmail: message.personEmail,
|
|
138
|
+
created: message.created,
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(formatOutput(output, options.pretty))
|
|
142
|
+
} catch (error) {
|
|
143
|
+
handleError(error as Error)
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export const messageCommand = new Command('message')
|
|
148
|
+
.description('Message commands')
|
|
149
|
+
.addCommand(
|
|
150
|
+
new Command('send')
|
|
151
|
+
.description('Send message to a space')
|
|
152
|
+
.argument('<space-id>', 'Space/Room ID')
|
|
153
|
+
.argument('<text>', 'Message text')
|
|
154
|
+
.option('--markdown', 'Send as markdown')
|
|
155
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
156
|
+
.action(sendAction),
|
|
157
|
+
)
|
|
158
|
+
.addCommand(
|
|
159
|
+
new Command('dm')
|
|
160
|
+
.description('Send a direct message by email')
|
|
161
|
+
.argument('<email>', 'Recipient email address')
|
|
162
|
+
.argument('<text>', 'Message text')
|
|
163
|
+
.option('--markdown', 'Send as markdown')
|
|
164
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
165
|
+
.action(dmAction),
|
|
166
|
+
)
|
|
167
|
+
.addCommand(
|
|
168
|
+
new Command('list')
|
|
169
|
+
.description('List messages from a space')
|
|
170
|
+
.argument('<space-id>', 'Space/Room ID')
|
|
171
|
+
.option('--limit <n>', 'Number of messages to retrieve', '50')
|
|
172
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
173
|
+
.action((spaceId: string, options: { limit: string; pretty?: boolean }) => {
|
|
174
|
+
return listAction(spaceId, {
|
|
175
|
+
limit: parseInt(options.limit, 10),
|
|
176
|
+
pretty: options.pretty,
|
|
177
|
+
})
|
|
178
|
+
}),
|
|
179
|
+
)
|
|
180
|
+
.addCommand(
|
|
181
|
+
new Command('get')
|
|
182
|
+
.description('Get a single message by ID')
|
|
183
|
+
.argument('<message-id>', 'Message ID')
|
|
184
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
185
|
+
.action(getAction),
|
|
186
|
+
)
|
|
187
|
+
.addCommand(
|
|
188
|
+
new Command('delete')
|
|
189
|
+
.description('Delete a message')
|
|
190
|
+
.argument('<message-id>', 'Message ID')
|
|
191
|
+
.option('--force', 'Skip confirmation')
|
|
192
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
193
|
+
.action(deleteAction),
|
|
194
|
+
)
|
|
195
|
+
.addCommand(
|
|
196
|
+
new Command('edit')
|
|
197
|
+
.description('Edit a message')
|
|
198
|
+
.argument('<message-id>', 'Message ID')
|
|
199
|
+
.argument('<space-id>', 'Space/Room ID (required by Webex API)')
|
|
200
|
+
.argument('<text>', 'New message text')
|
|
201
|
+
.option('--markdown', 'Send as markdown')
|
|
202
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
203
|
+
.action(editAction),
|
|
204
|
+
)
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
+
import { WebexClient } from '../client'
|
|
3
|
+
import { WebexError } from '../types'
|
|
4
|
+
import { snapshotAction } from './snapshot'
|
|
5
|
+
|
|
6
|
+
describe('snapshot command', () => {
|
|
7
|
+
let consoleSpy: ReturnType<typeof spyOn>
|
|
8
|
+
let consoleErrorSpy: ReturnType<typeof spyOn>
|
|
9
|
+
let stderrOutput: string
|
|
10
|
+
let origStderrWrite: typeof process.stderr.write
|
|
11
|
+
|
|
12
|
+
const mockSpaces = [
|
|
13
|
+
{ id: 'space-1', title: 'General', type: 'group', isLocked: false, lastActivity: '2024-01-15T00:00:00.000Z', created: '2024-01-01T00:00:00.000Z', creatorId: 'person-1' },
|
|
14
|
+
]
|
|
15
|
+
|
|
16
|
+
const mockMessages = [
|
|
17
|
+
{ id: 'msg-1', roomId: 'space-1', roomType: 'group', text: 'Hello', personId: 'person-1', personEmail: 'alice@example.com', created: '2024-01-15T00:00:00.000Z' },
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
const mockMembers = [
|
|
21
|
+
{ id: 'mem-1', roomId: 'space-1', personId: 'person-1', personEmail: 'alice@example.com', personDisplayName: 'Alice', isModerator: true, created: '2024-01-01T00:00:00.000Z' },
|
|
22
|
+
]
|
|
23
|
+
|
|
24
|
+
beforeEach(() => {
|
|
25
|
+
consoleSpy = spyOn(console, 'log').mockImplementation(() => {})
|
|
26
|
+
consoleErrorSpy = spyOn(console, 'error').mockImplementation(() => {})
|
|
27
|
+
stderrOutput = ''
|
|
28
|
+
origStderrWrite = process.stderr.write
|
|
29
|
+
process.stderr.write = ((chunk: string | Uint8Array) => {
|
|
30
|
+
stderrOutput += typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk)
|
|
31
|
+
return true
|
|
32
|
+
}) as typeof process.stderr.write
|
|
33
|
+
spyOn(WebexClient.prototype, 'login').mockResolvedValue(new WebexClient() as any)
|
|
34
|
+
spyOn(WebexClient.prototype, 'listSpaces').mockResolvedValue(mockSpaces as any)
|
|
35
|
+
spyOn(WebexClient.prototype, 'listMessages').mockResolvedValue(mockMessages as any)
|
|
36
|
+
spyOn(WebexClient.prototype, 'listMemberships').mockResolvedValue(mockMembers as any)
|
|
37
|
+
})
|
|
38
|
+
|
|
39
|
+
afterEach(() => {
|
|
40
|
+
process.stderr.write = origStderrWrite
|
|
41
|
+
mock.restore()
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
test('full snapshot includes spaces, recent_messages, members', async () => {
|
|
45
|
+
await snapshotAction({})
|
|
46
|
+
|
|
47
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
48
|
+
const output = JSON.parse(consoleSpy.mock.calls[consoleSpy.mock.calls.length - 1][0])
|
|
49
|
+
expect(output.spaces).toBeDefined()
|
|
50
|
+
expect(output.spaces[0].id).toBe('space-1')
|
|
51
|
+
expect(output.spaces[0].title).toBe('General')
|
|
52
|
+
expect(output.recent_messages).toBeDefined()
|
|
53
|
+
expect(output.recent_messages[0].id).toBe('msg-1')
|
|
54
|
+
expect(output.recent_messages[0].author).toBe('alice@example.com')
|
|
55
|
+
expect(output.members).toBeDefined()
|
|
56
|
+
expect(output.members[0].personEmail).toBe('alice@example.com')
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('--spaces-only includes only spaces (no messages, no members)', async () => {
|
|
60
|
+
await snapshotAction({ spacesOnly: true })
|
|
61
|
+
|
|
62
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
63
|
+
const output = JSON.parse(consoleSpy.mock.calls[consoleSpy.mock.calls.length - 1][0])
|
|
64
|
+
expect(output.spaces).toBeDefined()
|
|
65
|
+
expect(output.recent_messages).toBeUndefined()
|
|
66
|
+
expect(output.members).toBeUndefined()
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('--members-only includes only members (no spaces, no messages)', async () => {
|
|
70
|
+
await snapshotAction({ membersOnly: true })
|
|
71
|
+
|
|
72
|
+
expect(consoleSpy).toHaveBeenCalled()
|
|
73
|
+
const output = JSON.parse(consoleSpy.mock.calls[consoleSpy.mock.calls.length - 1][0])
|
|
74
|
+
expect(output.spaces).toBeUndefined()
|
|
75
|
+
expect(output.recent_messages).toBeUndefined()
|
|
76
|
+
expect(output.members).toBeDefined()
|
|
77
|
+
expect(output.members[0].personEmail).toBe('alice@example.com')
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
test('not authenticated outputs error', async () => {
|
|
81
|
+
spyOn(WebexClient.prototype, 'login').mockRejectedValue(
|
|
82
|
+
new WebexError('No Webex credentials found.', 'no_credentials'),
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
const originalExit = process.exit
|
|
86
|
+
process.exit = mock((_code?: number) => { throw new Error('process.exit called') }) as never
|
|
87
|
+
|
|
88
|
+
try {
|
|
89
|
+
await snapshotAction({})
|
|
90
|
+
} catch {
|
|
91
|
+
} finally {
|
|
92
|
+
process.exit = originalExit
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
expect(stderrOutput).toContain('No Webex credentials found')
|
|
96
|
+
})
|
|
97
|
+
|
|
98
|
+
test('passes limit option to listMessages', async () => {
|
|
99
|
+
const listMessagesSpy = spyOn(WebexClient.prototype, 'listMessages').mockResolvedValue(mockMessages as any)
|
|
100
|
+
|
|
101
|
+
await snapshotAction({ limit: 5 })
|
|
102
|
+
|
|
103
|
+
expect(listMessagesSpy).toHaveBeenCalledWith('space-1', { max: 5 })
|
|
104
|
+
})
|
|
105
|
+
})
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { parallelMap } from '@/shared/utils/concurrency'
|
|
3
|
+
import { handleError } from '@/shared/utils/error-handler'
|
|
4
|
+
import { formatOutput } from '@/shared/utils/output'
|
|
5
|
+
import { WebexClient } from '../client'
|
|
6
|
+
import type { WebexSpace } from '../types'
|
|
7
|
+
|
|
8
|
+
export async function snapshotAction(options: {
|
|
9
|
+
spacesOnly?: boolean
|
|
10
|
+
membersOnly?: boolean
|
|
11
|
+
limit?: number
|
|
12
|
+
pretty?: boolean
|
|
13
|
+
}): Promise<void> {
|
|
14
|
+
try {
|
|
15
|
+
const client = await new WebexClient().login()
|
|
16
|
+
const messageLimit = options.limit || 20
|
|
17
|
+
const snapshot: Record<string, unknown> = {}
|
|
18
|
+
|
|
19
|
+
if (!options.membersOnly) {
|
|
20
|
+
const spaces = await client.listSpaces({ max: 50 })
|
|
21
|
+
snapshot.spaces = spaces.map((s) => ({
|
|
22
|
+
id: s.id,
|
|
23
|
+
title: s.title,
|
|
24
|
+
type: s.type,
|
|
25
|
+
lastActivity: s.lastActivity,
|
|
26
|
+
}))
|
|
27
|
+
|
|
28
|
+
if (!options.spacesOnly) {
|
|
29
|
+
const spaceMessages = await parallelMap(
|
|
30
|
+
spaces,
|
|
31
|
+
async (space: WebexSpace) => {
|
|
32
|
+
const messages = await client.listMessages(space.id, { max: messageLimit })
|
|
33
|
+
return messages.map((msg) => ({
|
|
34
|
+
...msg,
|
|
35
|
+
space_title: space.title,
|
|
36
|
+
}))
|
|
37
|
+
},
|
|
38
|
+
5,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
snapshot.recent_messages = spaceMessages.flat().map((msg) => ({
|
|
42
|
+
space_id: msg.roomId,
|
|
43
|
+
space_title: msg.space_title,
|
|
44
|
+
id: msg.id,
|
|
45
|
+
author: msg.personEmail,
|
|
46
|
+
text: msg.text || msg.markdown || '',
|
|
47
|
+
created: msg.created,
|
|
48
|
+
}))
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (!options.spacesOnly) {
|
|
53
|
+
// Get members for the first few spaces (avoid massive API calls)
|
|
54
|
+
const spaces = await client.listSpaces({ max: 10 })
|
|
55
|
+
const spaceMembers = await parallelMap(
|
|
56
|
+
spaces,
|
|
57
|
+
async (space: WebexSpace) => {
|
|
58
|
+
const members = await client.listMemberships(space.id, { max: 100 })
|
|
59
|
+
return members.map((m) => ({
|
|
60
|
+
space_id: space.id,
|
|
61
|
+
space_title: space.title,
|
|
62
|
+
personEmail: m.personEmail,
|
|
63
|
+
personDisplayName: m.personDisplayName,
|
|
64
|
+
isModerator: m.isModerator,
|
|
65
|
+
}))
|
|
66
|
+
},
|
|
67
|
+
5,
|
|
68
|
+
)
|
|
69
|
+
snapshot.members = spaceMembers.flat()
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
console.log(formatOutput(snapshot, options.pretty))
|
|
73
|
+
} catch (error) {
|
|
74
|
+
handleError(error as Error)
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export const snapshotCommand = new Command('snapshot')
|
|
79
|
+
.description('Get comprehensive workspace state for AI agents')
|
|
80
|
+
.option('--spaces-only', 'Include only spaces (exclude messages and members)')
|
|
81
|
+
.option('--members-only', 'Include only members (exclude spaces and messages)')
|
|
82
|
+
.option('--limit <n>', 'Number of recent messages per space (default: 20)', '20')
|
|
83
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
84
|
+
.action(async (options) => {
|
|
85
|
+
await snapshotAction({
|
|
86
|
+
spacesOnly: options.spacesOnly,
|
|
87
|
+
membersOnly: options.membersOnly,
|
|
88
|
+
limit: parseInt(options.limit, 10),
|
|
89
|
+
pretty: options.pretty,
|
|
90
|
+
})
|
|
91
|
+
})
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, spyOn, test } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { WebexClient } from '../client'
|
|
4
|
+
import { WebexError } from '../types'
|
|
5
|
+
import { infoAction, listAction } from './space'
|
|
6
|
+
|
|
7
|
+
const mockSpaces = [
|
|
8
|
+
{
|
|
9
|
+
id: 'space-1',
|
|
10
|
+
title: 'General',
|
|
11
|
+
type: 'group' as const,
|
|
12
|
+
isLocked: false,
|
|
13
|
+
lastActivity: '2024-01-02T00:00:00.000Z',
|
|
14
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
15
|
+
creatorId: 'person-1',
|
|
16
|
+
},
|
|
17
|
+
{
|
|
18
|
+
id: 'space-2',
|
|
19
|
+
title: 'Direct with Alice',
|
|
20
|
+
type: 'direct' as const,
|
|
21
|
+
isLocked: false,
|
|
22
|
+
lastActivity: '2024-01-03T00:00:00.000Z',
|
|
23
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
24
|
+
creatorId: 'person-2',
|
|
25
|
+
},
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
const mockSpace = {
|
|
29
|
+
id: 'space-1',
|
|
30
|
+
title: 'General',
|
|
31
|
+
type: 'group' as const,
|
|
32
|
+
isLocked: false,
|
|
33
|
+
teamId: 'team-abc',
|
|
34
|
+
lastActivity: '2024-01-02T00:00:00.000Z',
|
|
35
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
36
|
+
creatorId: 'person-1',
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
let clientLoginSpy: ReturnType<typeof spyOn>
|
|
40
|
+
let clientListSpacesSpy: ReturnType<typeof spyOn>
|
|
41
|
+
let clientGetSpaceSpy: ReturnType<typeof spyOn>
|
|
42
|
+
let consoleLogSpy: ReturnType<typeof spyOn>
|
|
43
|
+
let consoleErrorSpy: ReturnType<typeof spyOn>
|
|
44
|
+
let processExitSpy: ReturnType<typeof spyOn>
|
|
45
|
+
let stderrOutput: string
|
|
46
|
+
let origStderrWrite: typeof process.stderr.write
|
|
47
|
+
|
|
48
|
+
beforeEach(() => {
|
|
49
|
+
clientLoginSpy = spyOn(WebexClient.prototype, 'login').mockResolvedValue(
|
|
50
|
+
new WebexClient() as InstanceType<typeof WebexClient>,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
clientListSpacesSpy = spyOn(WebexClient.prototype, 'listSpaces').mockResolvedValue(mockSpaces)
|
|
54
|
+
|
|
55
|
+
clientGetSpaceSpy = spyOn(WebexClient.prototype, 'getSpace').mockResolvedValue(mockSpace)
|
|
56
|
+
|
|
57
|
+
consoleLogSpy = spyOn(console, 'log').mockImplementation(() => {})
|
|
58
|
+
consoleErrorSpy = spyOn(console, 'error').mockImplementation(() => {})
|
|
59
|
+
|
|
60
|
+
processExitSpy = spyOn(process, 'exit').mockImplementation((_code?: number) => {
|
|
61
|
+
throw new Error(`process.exit(${_code})`)
|
|
62
|
+
})
|
|
63
|
+
|
|
64
|
+
stderrOutput = ''
|
|
65
|
+
origStderrWrite = process.stderr.write
|
|
66
|
+
process.stderr.write = ((chunk: string | Uint8Array) => {
|
|
67
|
+
stderrOutput += typeof chunk === 'string' ? chunk : new TextDecoder().decode(chunk)
|
|
68
|
+
return true
|
|
69
|
+
}) as typeof process.stderr.write
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
afterEach(() => {
|
|
73
|
+
process.stderr.write = origStderrWrite
|
|
74
|
+
clientLoginSpy?.mockRestore()
|
|
75
|
+
clientListSpacesSpy?.mockRestore()
|
|
76
|
+
clientGetSpaceSpy?.mockRestore()
|
|
77
|
+
consoleLogSpy?.mockRestore()
|
|
78
|
+
consoleErrorSpy?.mockRestore()
|
|
79
|
+
processExitSpy?.mockRestore()
|
|
80
|
+
})
|
|
81
|
+
|
|
82
|
+
describe('listAction', () => {
|
|
83
|
+
test('calls listSpaces and outputs mapped array', async () => {
|
|
84
|
+
await listAction({})
|
|
85
|
+
|
|
86
|
+
expect(clientListSpacesSpy).toHaveBeenCalled()
|
|
87
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
88
|
+
JSON.stringify([
|
|
89
|
+
{
|
|
90
|
+
id: 'space-1',
|
|
91
|
+
title: 'General',
|
|
92
|
+
type: 'group',
|
|
93
|
+
lastActivity: '2024-01-02T00:00:00.000Z',
|
|
94
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
95
|
+
},
|
|
96
|
+
{
|
|
97
|
+
id: 'space-2',
|
|
98
|
+
title: 'Direct with Alice',
|
|
99
|
+
type: 'direct',
|
|
100
|
+
lastActivity: '2024-01-03T00:00:00.000Z',
|
|
101
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
102
|
+
},
|
|
103
|
+
]),
|
|
104
|
+
)
|
|
105
|
+
})
|
|
106
|
+
|
|
107
|
+
test('passes type and limit options to listSpaces', async () => {
|
|
108
|
+
await listAction({ type: 'group', limit: 10 })
|
|
109
|
+
|
|
110
|
+
expect(clientListSpacesSpy).toHaveBeenCalledWith({ type: 'group', max: 10 })
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('passes undefined type and limit when not provided', async () => {
|
|
114
|
+
await listAction({})
|
|
115
|
+
|
|
116
|
+
expect(clientListSpacesSpy).toHaveBeenCalledWith({ type: undefined, max: undefined })
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
test('outputs pretty-printed JSON when pretty is true', async () => {
|
|
120
|
+
await listAction({ pretty: true })
|
|
121
|
+
|
|
122
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
123
|
+
JSON.stringify(
|
|
124
|
+
[
|
|
125
|
+
{
|
|
126
|
+
id: 'space-1',
|
|
127
|
+
title: 'General',
|
|
128
|
+
type: 'group',
|
|
129
|
+
lastActivity: '2024-01-02T00:00:00.000Z',
|
|
130
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
id: 'space-2',
|
|
134
|
+
title: 'Direct with Alice',
|
|
135
|
+
type: 'direct',
|
|
136
|
+
lastActivity: '2024-01-03T00:00:00.000Z',
|
|
137
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
null,
|
|
141
|
+
2,
|
|
142
|
+
),
|
|
143
|
+
)
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
test('not authenticated: outputs error and exits', async () => {
|
|
147
|
+
clientLoginSpy.mockRejectedValue(new WebexError('No Webex credentials found.', 'no_credentials'))
|
|
148
|
+
|
|
149
|
+
await expect(listAction({})).rejects.toThrow('process.exit(1)')
|
|
150
|
+
|
|
151
|
+
expect(clientListSpacesSpy).not.toHaveBeenCalled()
|
|
152
|
+
expect(stderrOutput).toContain('No Webex credentials found')
|
|
153
|
+
})
|
|
154
|
+
})
|
|
155
|
+
|
|
156
|
+
describe('infoAction', () => {
|
|
157
|
+
test('calls getSpace with spaceId and outputs space details', async () => {
|
|
158
|
+
await infoAction('space-1', {})
|
|
159
|
+
|
|
160
|
+
expect(clientGetSpaceSpy).toHaveBeenCalledWith('space-1')
|
|
161
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
162
|
+
JSON.stringify({
|
|
163
|
+
id: 'space-1',
|
|
164
|
+
title: 'General',
|
|
165
|
+
type: 'group',
|
|
166
|
+
isLocked: false,
|
|
167
|
+
teamId: 'team-abc',
|
|
168
|
+
lastActivity: '2024-01-02T00:00:00.000Z',
|
|
169
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
170
|
+
creatorId: 'person-1',
|
|
171
|
+
}),
|
|
172
|
+
)
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
test('outputs null for teamId when not present', async () => {
|
|
176
|
+
const spaceWithoutTeam = { ...mockSpace, teamId: undefined }
|
|
177
|
+
clientGetSpaceSpy.mockResolvedValue(spaceWithoutTeam)
|
|
178
|
+
|
|
179
|
+
await infoAction('space-1', {})
|
|
180
|
+
|
|
181
|
+
const output = JSON.parse(consoleLogSpy.mock.calls[0][0] as string) as {
|
|
182
|
+
teamId: null
|
|
183
|
+
}
|
|
184
|
+
expect(output.teamId).toBeNull()
|
|
185
|
+
})
|
|
186
|
+
|
|
187
|
+
test('outputs pretty-printed JSON when pretty is true', async () => {
|
|
188
|
+
await infoAction('space-1', { pretty: true })
|
|
189
|
+
|
|
190
|
+
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
191
|
+
JSON.stringify(
|
|
192
|
+
{
|
|
193
|
+
id: 'space-1',
|
|
194
|
+
title: 'General',
|
|
195
|
+
type: 'group',
|
|
196
|
+
isLocked: false,
|
|
197
|
+
teamId: 'team-abc',
|
|
198
|
+
lastActivity: '2024-01-02T00:00:00.000Z',
|
|
199
|
+
created: '2024-01-01T00:00:00.000Z',
|
|
200
|
+
creatorId: 'person-1',
|
|
201
|
+
},
|
|
202
|
+
null,
|
|
203
|
+
2,
|
|
204
|
+
),
|
|
205
|
+
)
|
|
206
|
+
})
|
|
207
|
+
|
|
208
|
+
test('not authenticated: outputs error and exits', async () => {
|
|
209
|
+
clientLoginSpy.mockRejectedValue(new WebexError('No Webex credentials found.', 'no_credentials'))
|
|
210
|
+
|
|
211
|
+
await expect(infoAction('space-1', {})).rejects.toThrow('process.exit(1)')
|
|
212
|
+
|
|
213
|
+
expect(clientGetSpaceSpy).not.toHaveBeenCalled()
|
|
214
|
+
expect(stderrOutput).toContain('No Webex credentials found')
|
|
215
|
+
})
|
|
216
|
+
})
|
|
@@ -0,0 +1,74 @@
|
|
|
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(options: {
|
|
9
|
+
type?: string
|
|
10
|
+
limit?: number
|
|
11
|
+
pretty?: boolean
|
|
12
|
+
}): Promise<void> {
|
|
13
|
+
try {
|
|
14
|
+
const client = await new WebexClient().login()
|
|
15
|
+
const spaces = await client.listSpaces({ type: options.type, max: options.limit })
|
|
16
|
+
const output = spaces.map((s) => ({
|
|
17
|
+
id: s.id,
|
|
18
|
+
title: s.title,
|
|
19
|
+
type: s.type,
|
|
20
|
+
lastActivity: s.lastActivity,
|
|
21
|
+
created: s.created,
|
|
22
|
+
}))
|
|
23
|
+
console.log(formatOutput(output, options.pretty))
|
|
24
|
+
} catch (error) {
|
|
25
|
+
handleError(error as Error)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export async function infoAction(
|
|
30
|
+
spaceId: string,
|
|
31
|
+
options: { pretty?: boolean },
|
|
32
|
+
): Promise<void> {
|
|
33
|
+
try {
|
|
34
|
+
const client = await new WebexClient().login()
|
|
35
|
+
const space = await client.getSpace(spaceId)
|
|
36
|
+
const output = {
|
|
37
|
+
id: space.id,
|
|
38
|
+
title: space.title,
|
|
39
|
+
type: space.type,
|
|
40
|
+
isLocked: space.isLocked,
|
|
41
|
+
teamId: space.teamId || null,
|
|
42
|
+
lastActivity: space.lastActivity,
|
|
43
|
+
created: space.created,
|
|
44
|
+
creatorId: space.creatorId,
|
|
45
|
+
}
|
|
46
|
+
console.log(formatOutput(output, options.pretty))
|
|
47
|
+
} catch (error) {
|
|
48
|
+
handleError(error as Error)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export const spaceCommand = new Command('space')
|
|
53
|
+
.description('Space commands')
|
|
54
|
+
.addCommand(
|
|
55
|
+
new Command('list')
|
|
56
|
+
.description('List spaces')
|
|
57
|
+
.option('--type <type>', 'Filter by type (group or direct)')
|
|
58
|
+
.option('--limit <n>', 'Number of spaces to retrieve', '50')
|
|
59
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
60
|
+
.action((options) =>
|
|
61
|
+
listAction({
|
|
62
|
+
type: options.type,
|
|
63
|
+
limit: parseInt(options.limit, 10),
|
|
64
|
+
pretty: options.pretty,
|
|
65
|
+
}),
|
|
66
|
+
),
|
|
67
|
+
)
|
|
68
|
+
.addCommand(
|
|
69
|
+
new Command('info')
|
|
70
|
+
.description('Get space details')
|
|
71
|
+
.argument('<space-id>', 'Space ID')
|
|
72
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
73
|
+
.action(infoAction),
|
|
74
|
+
)
|