agent-messenger 2.3.0 → 2.4.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/README.md +16 -16
- package/.claude-plugin/marketplace.json +29 -29
- package/.claude-plugin/plugin.json +5 -5
- package/CONTRIBUTING.md +1 -1
- package/README.md +8 -5
- package/bun.lock +70 -110
- package/bunfig.toml +3 -0
- package/dist/package.json +11 -3
- package/dist/src/platforms/discordbot/client.js +2 -2
- package/dist/src/platforms/discordbot/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/cli.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/cli.js +2 -1
- package/dist/src/platforms/kakaotalk/cli.js.map +1 -1
- package/dist/src/platforms/kakaotalk/client.d.ts +2 -1
- package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/client.js +52 -2
- package/dist/src/platforms/kakaotalk/client.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/index.d.ts +1 -0
- package/dist/src/platforms/kakaotalk/commands/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/index.js +1 -0
- package/dist/src/platforms/kakaotalk/commands/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/commands/profile.d.ts +3 -0
- package/dist/src/platforms/kakaotalk/commands/profile.d.ts.map +1 -0
- package/dist/src/platforms/kakaotalk/commands/profile.js +19 -0
- package/dist/src/platforms/kakaotalk/commands/profile.js.map +1 -0
- package/dist/src/platforms/kakaotalk/index.d.ts +2 -2
- package/dist/src/platforms/kakaotalk/index.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/index.js +1 -1
- package/dist/src/platforms/kakaotalk/index.js.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js +2 -1
- package/dist/src/platforms/kakaotalk/protocol/session.js.map +1 -1
- package/dist/src/platforms/kakaotalk/types.d.ts +16 -0
- package/dist/src/platforms/kakaotalk/types.d.ts.map +1 -1
- package/dist/src/platforms/kakaotalk/types.js +8 -0
- package/dist/src/platforms/kakaotalk/types.js.map +1 -1
- package/dist/src/platforms/line/client.d.ts.map +1 -1
- package/dist/src/platforms/line/client.js +9 -36
- package/dist/src/platforms/line/client.js.map +1 -1
- package/dist/src/platforms/line/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/line/commands/auth.js +32 -20
- package/dist/src/platforms/line/commands/auth.js.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/reaction.js +2 -0
- package/dist/src/platforms/teams/commands/reaction.js.map +1 -1
- package/dist/src/platforms/wechatbot/cli.d.ts +5 -0
- package/dist/src/platforms/wechatbot/cli.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/cli.js +18 -0
- package/dist/src/platforms/wechatbot/cli.js.map +1 -0
- package/dist/src/platforms/wechatbot/client.d.ts +36 -0
- package/dist/src/platforms/wechatbot/client.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/client.js +208 -0
- package/dist/src/platforms/wechatbot/client.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/auth.d.ts +28 -0
- package/dist/src/platforms/wechatbot/commands/auth.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/auth.js +164 -0
- package/dist/src/platforms/wechatbot/commands/auth.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/index.d.ts +5 -0
- package/dist/src/platforms/wechatbot/commands/index.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/index.js +5 -0
- package/dist/src/platforms/wechatbot/commands/index.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/message.d.ts +18 -0
- package/dist/src/platforms/wechatbot/commands/message.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/message.js +80 -0
- package/dist/src/platforms/wechatbot/commands/message.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/shared.d.ts +9 -0
- package/dist/src/platforms/wechatbot/commands/shared.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/shared.js +13 -0
- package/dist/src/platforms/wechatbot/commands/shared.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/template.d.ts +19 -0
- package/dist/src/platforms/wechatbot/commands/template.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/template.js +76 -0
- package/dist/src/platforms/wechatbot/commands/template.js.map +1 -0
- package/dist/src/platforms/wechatbot/commands/user.d.ts +20 -0
- package/dist/src/platforms/wechatbot/commands/user.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/commands/user.js +53 -0
- package/dist/src/platforms/wechatbot/commands/user.js.map +1 -0
- package/dist/src/platforms/wechatbot/credential-manager.d.ts +17 -0
- package/dist/src/platforms/wechatbot/credential-manager.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/credential-manager.js +121 -0
- package/dist/src/platforms/wechatbot/credential-manager.js.map +1 -0
- package/dist/src/platforms/wechatbot/index.d.ts +5 -0
- package/dist/src/platforms/wechatbot/index.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/index.js +4 -0
- package/dist/src/platforms/wechatbot/index.js.map +1 -0
- package/dist/src/platforms/wechatbot/types.d.ts +94 -0
- package/dist/src/platforms/wechatbot/types.d.ts.map +1 -0
- package/dist/src/platforms/wechatbot/types.js +54 -0
- package/dist/src/platforms/wechatbot/types.js.map +1 -0
- package/dist/src/platforms/whatsapp/client.d.ts +1 -0
- package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/client.js +27 -13
- package/dist/src/platforms/whatsapp/client.js.map +1 -1
- package/dist/src/platforms/whatsapp/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/commands/auth.js +21 -18
- package/dist/src/platforms/whatsapp/commands/auth.js.map +1 -1
- package/dist/src/platforms/whatsapp/credential-manager.d.ts.map +1 -1
- package/dist/src/platforms/whatsapp/credential-manager.js +14 -8
- package/dist/src/platforms/whatsapp/credential-manager.js.map +1 -1
- package/docs/content/docs/agent-skills.mdx +4 -4
- package/docs/content/docs/cli/channeltalk.mdx +1 -1
- package/docs/content/docs/cli/channeltalkbot.mdx +1 -1
- package/docs/content/docs/cli/discord.mdx +1 -1
- package/docs/content/docs/cli/discordbot.mdx +1 -1
- package/docs/content/docs/cli/instagram.mdx +1 -1
- package/docs/content/docs/cli/kakaotalk.mdx +1 -1
- package/docs/content/docs/cli/line.mdx +1 -1
- package/docs/content/docs/cli/meta.json +1 -0
- package/docs/content/docs/cli/slack.mdx +1 -1
- package/docs/content/docs/cli/slackbot.mdx +1 -1
- package/docs/content/docs/cli/teams.mdx +1 -1
- package/docs/content/docs/cli/webex.mdx +1 -1
- package/docs/content/docs/cli/wechatbot.mdx +179 -0
- package/docs/content/docs/cli/whatsapp.mdx +1 -1
- package/docs/content/docs/cli/whatsappbot.mdx +1 -1
- package/docs/content/docs/sdk/meta.json +1 -1
- package/docs/content/docs/sdk/wechatbot.mdx +282 -0
- package/docs/content/docs/tui.mdx +1 -1
- package/docs/src/app/page.tsx +5 -5
- package/package.json +11 -3
- 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 +24 -1
- package/skills/agent-line/SKILL.md +7 -11
- package/skills/agent-line/references/authentication.md +13 -4
- 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-webex/SKILL.md +1 -1
- package/skills/agent-wechatbot/SKILL.md +385 -0
- package/skills/agent-whatsapp/SKILL.md +12 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/discord/credential-manager.test.ts +18 -1
- package/src/platforms/discordbot/client.ts +2 -2
- package/src/platforms/instagram/commands/auth.test.ts +216 -0
- package/src/platforms/instagram/commands/chat.test.ts +127 -0
- package/src/platforms/instagram/commands/message.test.ts +178 -0
- package/src/platforms/kakaotalk/cli.ts +2 -1
- package/src/platforms/kakaotalk/client.test.ts +157 -0
- package/src/platforms/kakaotalk/client.ts +57 -3
- package/src/platforms/kakaotalk/commands/auth.test.ts +299 -0
- package/src/platforms/kakaotalk/commands/chat.test.ts +97 -0
- package/src/platforms/kakaotalk/commands/index.ts +1 -0
- package/src/platforms/kakaotalk/commands/message.test.ts +113 -0
- package/src/platforms/kakaotalk/commands/profile.test.ts +84 -0
- package/src/platforms/kakaotalk/commands/profile.ts +21 -0
- package/src/platforms/kakaotalk/index.test.ts +5 -0
- package/src/platforms/kakaotalk/index.ts +2 -0
- package/src/platforms/kakaotalk/protocol/session.ts +2 -0
- package/src/platforms/kakaotalk/types.ts +18 -0
- package/src/platforms/line/client.ts +14 -39
- package/src/platforms/line/commands/auth.test.ts +141 -0
- package/src/platforms/line/commands/auth.ts +28 -19
- package/src/platforms/line/commands/chat.test.ts +110 -0
- package/src/platforms/line/commands/friend.test.ts +98 -0
- package/src/platforms/line/commands/message.test.ts +119 -0
- package/src/platforms/line/commands/profile.test.ts +85 -0
- package/src/platforms/slackbot/commands/channel.test.ts +139 -0
- package/src/platforms/slackbot/commands/message.test.ts +226 -0
- package/src/platforms/slackbot/commands/reaction.test.ts +90 -0
- package/src/platforms/slackbot/commands/user.test.ts +143 -0
- package/src/platforms/teams/commands/reaction.test.ts +45 -61
- package/src/platforms/teams/commands/reaction.ts +2 -0
- package/src/platforms/telegram/commands/chat.test.ts +125 -0
- package/src/platforms/telegram/commands/message.test.ts +92 -0
- package/src/platforms/webex/commands/member.test.ts +65 -58
- package/src/platforms/webex/commands/message.test.ts +78 -121
- package/src/platforms/webex/commands/snapshot.test.ts +59 -46
- package/src/platforms/webex/commands/space.test.ts +49 -48
- package/src/platforms/wechatbot/cli.ts +24 -0
- package/src/platforms/wechatbot/client.test.ts +497 -0
- package/src/platforms/wechatbot/client.ts +268 -0
- package/src/platforms/wechatbot/commands/auth.test.ts +211 -0
- package/src/platforms/wechatbot/commands/auth.ts +203 -0
- package/src/platforms/wechatbot/commands/index.ts +4 -0
- package/src/platforms/wechatbot/commands/message.test.ts +155 -0
- package/src/platforms/wechatbot/commands/message.ts +104 -0
- package/src/platforms/wechatbot/commands/shared.ts +22 -0
- package/src/platforms/wechatbot/commands/template.test.ts +199 -0
- package/src/platforms/wechatbot/commands/template.ts +102 -0
- package/src/platforms/wechatbot/commands/user.test.ts +165 -0
- package/src/platforms/wechatbot/commands/user.ts +75 -0
- package/src/platforms/wechatbot/credential-manager.test.ts +255 -0
- package/src/platforms/wechatbot/credential-manager.ts +148 -0
- package/src/platforms/wechatbot/index.test.ts +49 -0
- package/src/platforms/wechatbot/index.ts +19 -0
- package/src/platforms/wechatbot/types.test.ts +223 -0
- package/src/platforms/wechatbot/types.ts +107 -0
- package/src/platforms/whatsapp/client.ts +24 -13
- package/src/platforms/whatsapp/commands/auth.test.ts +311 -0
- package/src/platforms/whatsapp/commands/auth.ts +21 -17
- package/src/platforms/whatsapp/commands/chat.test.ts +198 -0
- package/src/platforms/whatsapp/commands/message.test.ts +231 -0
- package/src/platforms/whatsapp/credential-manager.test.ts +20 -0
- package/src/platforms/whatsapp/credential-manager.ts +17 -8
- package/src/platforms/whatsappbot/commands/auth.test.ts +217 -0
- package/src/platforms/whatsappbot/commands/message.test.ts +198 -0
- package/src/platforms/whatsappbot/commands/template.test.ts +112 -0
|
@@ -1,18 +1,14 @@
|
|
|
1
|
-
import { afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
|
|
1
|
+
import { afterAll, afterEach, beforeEach, expect, mock, spyOn, test } from 'bun:test'
|
|
2
2
|
|
|
3
|
-
import { WebexClient } from '../client'
|
|
4
3
|
import { WebexError } from '../types'
|
|
5
|
-
import { deleteAction, dmAction, editAction, getAction, listAction, sendAction } from './message'
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
const originalConsoleLog = console.log
|
|
15
|
-
const originalConsoleError = console.error
|
|
5
|
+
const mockHandleError = mock((err: Error) => {
|
|
6
|
+
throw err
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
mock.module('@/shared/utils/error-handler', () => ({
|
|
10
|
+
handleError: mockHandleError,
|
|
11
|
+
}))
|
|
16
12
|
|
|
17
13
|
const mockMessage = {
|
|
18
14
|
id: 'msg_123',
|
|
@@ -34,202 +30,163 @@ const mockMessage2 = {
|
|
|
34
30
|
created: '2025-01-29T10:01:00.000Z',
|
|
35
31
|
}
|
|
36
32
|
|
|
37
|
-
|
|
38
|
-
|
|
33
|
+
const mockSendMessage = mock(() => Promise.resolve(mockMessage))
|
|
34
|
+
const mockSendDirectMessage = mock(() => Promise.resolve(mockMessage))
|
|
35
|
+
const mockListMessages = mock(() => Promise.resolve([mockMessage, mockMessage2]))
|
|
36
|
+
const mockGetMessage = mock(() => Promise.resolve(mockMessage))
|
|
37
|
+
const mockDeleteMessage = mock(() => Promise.resolve(undefined))
|
|
38
|
+
const mockEditMessage = mock(() => Promise.resolve({ ...mockMessage, text: 'Updated message' }))
|
|
39
|
+
|
|
40
|
+
const mockClient = {
|
|
41
|
+
sendMessage: mockSendMessage,
|
|
42
|
+
sendDirectMessage: mockSendDirectMessage,
|
|
43
|
+
listMessages: mockListMessages,
|
|
44
|
+
getMessage: mockGetMessage,
|
|
45
|
+
deleteMessage: mockDeleteMessage,
|
|
46
|
+
editMessage: mockEditMessage,
|
|
47
|
+
}
|
|
39
48
|
|
|
40
|
-
|
|
49
|
+
const mockLogin = mock(() => Promise.resolve(mockClient))
|
|
41
50
|
|
|
42
|
-
|
|
51
|
+
mock.module('../client', () => ({
|
|
52
|
+
WebexClient: class {
|
|
53
|
+
login = mockLogin
|
|
54
|
+
},
|
|
55
|
+
}))
|
|
43
56
|
|
|
44
|
-
|
|
45
|
-
mockMessage,
|
|
46
|
-
mockMessage2,
|
|
47
|
-
])
|
|
57
|
+
import { deleteAction, dmAction, editAction, getAction, listAction, sendAction } from './message'
|
|
48
58
|
|
|
49
|
-
|
|
59
|
+
afterAll(() => {
|
|
60
|
+
mock.restore()
|
|
61
|
+
})
|
|
50
62
|
|
|
51
|
-
|
|
52
|
-
undefined,
|
|
53
|
-
)
|
|
63
|
+
let consoleLogSpy: ReturnType<typeof spyOn>
|
|
54
64
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
65
|
+
beforeEach(() => {
|
|
66
|
+
mockSendMessage.mockReset().mockImplementation(() => Promise.resolve(mockMessage))
|
|
67
|
+
mockSendDirectMessage.mockReset().mockImplementation(() => Promise.resolve(mockMessage))
|
|
68
|
+
mockListMessages.mockReset().mockImplementation(() => Promise.resolve([mockMessage, mockMessage2]))
|
|
69
|
+
mockGetMessage.mockReset().mockImplementation(() => Promise.resolve(mockMessage))
|
|
70
|
+
mockDeleteMessage.mockReset().mockImplementation(() => Promise.resolve(undefined))
|
|
71
|
+
mockEditMessage.mockReset().mockImplementation(() => Promise.resolve({ ...mockMessage, text: 'Updated message' }))
|
|
72
|
+
mockLogin.mockReset().mockImplementation(() => Promise.resolve(mockClient))
|
|
73
|
+
mockHandleError.mockReset().mockImplementation((err: Error) => {
|
|
74
|
+
throw err
|
|
58
75
|
})
|
|
76
|
+
|
|
77
|
+
consoleLogSpy = spyOn(console, 'log').mockImplementation(() => {})
|
|
59
78
|
})
|
|
60
79
|
|
|
61
80
|
afterEach(() => {
|
|
62
|
-
|
|
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
|
|
81
|
+
consoleLogSpy.mockRestore()
|
|
71
82
|
})
|
|
72
83
|
|
|
73
84
|
test('send: calls sendMessage with correct args and outputs result', async () => {
|
|
74
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
75
|
-
console.log = consoleSpy
|
|
76
|
-
|
|
77
85
|
await sendAction('space_456', 'Hello world', { pretty: false })
|
|
78
86
|
|
|
79
|
-
expect(
|
|
87
|
+
expect(mockSendMessage).toHaveBeenCalledWith('space_456', 'Hello world', {
|
|
80
88
|
markdown: undefined,
|
|
81
89
|
})
|
|
82
|
-
expect(
|
|
83
|
-
const output =
|
|
90
|
+
expect(consoleLogSpy).toHaveBeenCalled()
|
|
91
|
+
const output = consoleLogSpy.mock.calls[0][0]
|
|
84
92
|
expect(output).toContain('msg_123')
|
|
85
93
|
expect(output).toContain('space_456')
|
|
86
94
|
expect(output).toContain('user@example.com')
|
|
87
95
|
})
|
|
88
96
|
|
|
89
97
|
test('send: with --markdown passes markdown option', async () => {
|
|
90
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
91
|
-
console.log = consoleSpy
|
|
92
|
-
|
|
93
98
|
await sendAction('space_456', '**bold**', { markdown: true, pretty: false })
|
|
94
99
|
|
|
95
|
-
expect(
|
|
100
|
+
expect(mockSendMessage).toHaveBeenCalledWith('space_456', '**bold**', { markdown: true })
|
|
96
101
|
})
|
|
97
102
|
|
|
98
103
|
test('send: not authenticated shows error', async () => {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
|
104
|
+
mockLogin.mockImplementation(async () => {
|
|
105
|
+
throw new WebexError('No Webex credentials found.', 'no_credentials')
|
|
106
|
+
})
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
process.exit = mock((_code?: number) => {
|
|
110
|
-
throw new Error('process.exit called')
|
|
111
|
-
}) as never
|
|
108
|
+
await expect(sendAction('space_456', 'Hello', { pretty: false })).rejects.toThrow('No Webex credentials found.')
|
|
112
109
|
|
|
113
|
-
|
|
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')
|
|
110
|
+
expect(mockHandleError).toHaveBeenCalledWith(expect.any(WebexError))
|
|
122
111
|
})
|
|
123
112
|
|
|
124
113
|
test('dm: calls sendDirectMessage with email and text', async () => {
|
|
125
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
126
|
-
console.log = consoleSpy
|
|
127
|
-
|
|
128
114
|
await dmAction('alice@example.com', 'Hello!', { pretty: false })
|
|
129
115
|
|
|
130
|
-
expect(
|
|
116
|
+
expect(mockSendDirectMessage).toHaveBeenCalledWith('alice@example.com', 'Hello!', {
|
|
131
117
|
markdown: undefined,
|
|
132
118
|
})
|
|
133
|
-
expect(
|
|
134
|
-
const output =
|
|
119
|
+
expect(consoleLogSpy).toHaveBeenCalled()
|
|
120
|
+
const output = consoleLogSpy.mock.calls[0][0]
|
|
135
121
|
expect(output).toContain('msg_123')
|
|
136
122
|
})
|
|
137
123
|
|
|
138
124
|
test('dm: with --markdown passes markdown option', async () => {
|
|
139
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
140
|
-
console.log = consoleSpy
|
|
141
|
-
|
|
142
125
|
await dmAction('alice@example.com', '**bold**', { markdown: true, pretty: false })
|
|
143
126
|
|
|
144
|
-
expect(
|
|
127
|
+
expect(mockSendDirectMessage).toHaveBeenCalledWith('alice@example.com', '**bold**', {
|
|
145
128
|
markdown: true,
|
|
146
129
|
})
|
|
147
130
|
})
|
|
148
131
|
|
|
149
132
|
test('list: calls listMessages with limit and outputs array', async () => {
|
|
150
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
151
|
-
console.log = consoleSpy
|
|
152
|
-
|
|
153
133
|
await listAction('space_456', { limit: 50, pretty: false })
|
|
154
134
|
|
|
155
|
-
expect(
|
|
156
|
-
expect(
|
|
157
|
-
const output =
|
|
135
|
+
expect(mockListMessages).toHaveBeenCalledWith('space_456', { max: 50 })
|
|
136
|
+
expect(consoleLogSpy).toHaveBeenCalled()
|
|
137
|
+
const output = consoleLogSpy.mock.calls[0][0]
|
|
158
138
|
expect(output).toContain('msg_123')
|
|
159
139
|
expect(output).toContain('msg_124')
|
|
160
140
|
})
|
|
161
141
|
|
|
162
142
|
test('get: calls getMessage with correct id and outputs result', async () => {
|
|
163
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
164
|
-
console.log = consoleSpy
|
|
165
|
-
|
|
166
143
|
await getAction('msg_123', { pretty: false })
|
|
167
144
|
|
|
168
|
-
expect(
|
|
169
|
-
expect(
|
|
170
|
-
const output =
|
|
145
|
+
expect(mockGetMessage).toHaveBeenCalledWith('msg_123')
|
|
146
|
+
expect(consoleLogSpy).toHaveBeenCalled()
|
|
147
|
+
const output = consoleLogSpy.mock.calls[0][0]
|
|
171
148
|
expect(output).toContain('msg_123')
|
|
172
149
|
expect(output).toContain('user@example.com')
|
|
173
150
|
})
|
|
174
151
|
|
|
175
152
|
test('delete: with --force calls deleteMessage and outputs deleted id', async () => {
|
|
176
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
177
|
-
console.log = consoleSpy
|
|
178
|
-
|
|
179
153
|
await deleteAction('msg_123', { force: true, pretty: false })
|
|
180
154
|
|
|
181
|
-
expect(
|
|
182
|
-
expect(
|
|
183
|
-
const output =
|
|
155
|
+
expect(mockDeleteMessage).toHaveBeenCalledWith('msg_123')
|
|
156
|
+
expect(consoleLogSpy).toHaveBeenCalled()
|
|
157
|
+
const output = consoleLogSpy.mock.calls[0][0]
|
|
184
158
|
expect(output).toContain('deleted')
|
|
185
159
|
expect(output).toContain('msg_123')
|
|
186
160
|
})
|
|
187
161
|
|
|
188
162
|
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
163
|
try {
|
|
198
164
|
await deleteAction('msg_123', { force: false, pretty: false })
|
|
199
|
-
} catch {
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
expect(clientDeleteMessageSpy).not.toHaveBeenCalled()
|
|
205
|
-
expect(consoleSpy).toHaveBeenCalled()
|
|
206
|
-
const output = consoleSpy.mock.calls[0][0]
|
|
165
|
+
} catch {}
|
|
166
|
+
|
|
167
|
+
expect(mockDeleteMessage).not.toHaveBeenCalled()
|
|
168
|
+
expect(consoleLogSpy).toHaveBeenCalled()
|
|
169
|
+
const output = consoleLogSpy.mock.calls[0][0]
|
|
207
170
|
expect(output).toContain('warning')
|
|
208
171
|
expect(output).toContain('--force')
|
|
209
172
|
})
|
|
210
173
|
|
|
211
174
|
test('edit: calls editMessage with roomId in args and outputs result', async () => {
|
|
212
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
213
|
-
console.log = consoleSpy
|
|
214
|
-
|
|
215
175
|
await editAction('msg_123', 'space_456', 'Updated message', { pretty: false })
|
|
216
176
|
|
|
217
|
-
expect(
|
|
177
|
+
expect(mockEditMessage).toHaveBeenCalledWith('msg_123', 'space_456', 'Updated message', {
|
|
218
178
|
markdown: undefined,
|
|
219
179
|
})
|
|
220
|
-
expect(
|
|
221
|
-
const output =
|
|
180
|
+
expect(consoleLogSpy).toHaveBeenCalled()
|
|
181
|
+
const output = consoleLogSpy.mock.calls[0][0]
|
|
222
182
|
expect(output).toContain('msg_123')
|
|
223
183
|
expect(output).toContain('Updated message')
|
|
224
184
|
})
|
|
225
185
|
|
|
226
186
|
test('edit: with --markdown passes markdown option', async () => {
|
|
227
|
-
const consoleSpy = mock((_msg: string) => {})
|
|
228
|
-
console.log = consoleSpy
|
|
229
|
-
|
|
230
187
|
await editAction('msg_123', 'space_456', '**updated**', { markdown: true, pretty: false })
|
|
231
188
|
|
|
232
|
-
expect(
|
|
189
|
+
expect(mockEditMessage).toHaveBeenCalledWith('msg_123', 'space_456', '**updated**', {
|
|
233
190
|
markdown: true,
|
|
234
191
|
})
|
|
235
192
|
})
|
|
@@ -1,44 +1,67 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
2
|
-
import { WebexClient } from '../client'
|
|
1
|
+
import { afterAll, afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
3
2
|
import { WebexError } from '../types'
|
|
4
|
-
import { snapshotAction } from './snapshot'
|
|
5
3
|
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
const mockHandleError = mock((err: Error) => {
|
|
5
|
+
throw err
|
|
6
|
+
})
|
|
7
|
+
|
|
8
|
+
mock.module('@/shared/utils/error-handler', () => ({
|
|
9
|
+
handleError: mockHandleError,
|
|
10
|
+
}))
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
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
15
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
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
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
+
const mockListSpaces = mock(() => Promise.resolve(mockSpaces as any))
|
|
25
|
+
const mockListMessages = mock(() => Promise.resolve(mockMessages as any))
|
|
26
|
+
const mockListMemberships = mock(() => Promise.resolve(mockMembers as any))
|
|
27
|
+
|
|
28
|
+
const mockClient = {
|
|
29
|
+
listSpaces: mockListSpaces,
|
|
30
|
+
listMessages: mockListMessages,
|
|
31
|
+
listMemberships: mockListMemberships,
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const mockLogin = mock(() => Promise.resolve(mockClient))
|
|
35
|
+
|
|
36
|
+
mock.module('../client', () => ({
|
|
37
|
+
WebexClient: class {
|
|
38
|
+
login = mockLogin
|
|
39
|
+
},
|
|
40
|
+
}))
|
|
41
|
+
|
|
42
|
+
import { snapshotAction } from './snapshot'
|
|
43
|
+
|
|
44
|
+
afterAll(() => {
|
|
45
|
+
mock.restore()
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
describe('snapshot command', () => {
|
|
49
|
+
let consoleSpy: ReturnType<typeof spyOn>
|
|
23
50
|
|
|
24
51
|
beforeEach(() => {
|
|
52
|
+
mockListSpaces.mockReset().mockImplementation(() => Promise.resolve(mockSpaces as any))
|
|
53
|
+
mockListMessages.mockReset().mockImplementation(() => Promise.resolve(mockMessages as any))
|
|
54
|
+
mockListMemberships.mockReset().mockImplementation(() => Promise.resolve(mockMembers as any))
|
|
55
|
+
mockLogin.mockReset().mockImplementation(() => Promise.resolve(mockClient))
|
|
56
|
+
mockHandleError.mockReset().mockImplementation((err: Error) => {
|
|
57
|
+
throw err
|
|
58
|
+
})
|
|
59
|
+
|
|
25
60
|
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
61
|
})
|
|
38
62
|
|
|
39
63
|
afterEach(() => {
|
|
40
|
-
|
|
41
|
-
mock.restore()
|
|
64
|
+
consoleSpy.mockRestore()
|
|
42
65
|
})
|
|
43
66
|
|
|
44
67
|
test('full snapshot includes spaces, recent_messages, members', async () => {
|
|
@@ -78,28 +101,18 @@ describe('snapshot command', () => {
|
|
|
78
101
|
})
|
|
79
102
|
|
|
80
103
|
test('not authenticated outputs error', async () => {
|
|
81
|
-
|
|
82
|
-
new WebexError('No Webex credentials found.', 'no_credentials')
|
|
83
|
-
)
|
|
104
|
+
mockLogin.mockImplementation(async () => {
|
|
105
|
+
throw new WebexError('No Webex credentials found.', 'no_credentials')
|
|
106
|
+
})
|
|
84
107
|
|
|
85
|
-
|
|
86
|
-
process.exit = mock((_code?: number) => { throw new Error('process.exit called') }) as never
|
|
108
|
+
await expect(snapshotAction({})).rejects.toThrow('No Webex credentials found.')
|
|
87
109
|
|
|
88
|
-
|
|
89
|
-
await snapshotAction({})
|
|
90
|
-
} catch {
|
|
91
|
-
} finally {
|
|
92
|
-
process.exit = originalExit
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
expect(stderrOutput).toContain('No Webex credentials found')
|
|
110
|
+
expect(mockHandleError).toHaveBeenCalledWith(expect.any(WebexError))
|
|
96
111
|
})
|
|
97
112
|
|
|
98
113
|
test('passes limit option to listMessages', async () => {
|
|
99
|
-
const listMessagesSpy = spyOn(WebexClient.prototype, 'listMessages').mockResolvedValue(mockMessages as any)
|
|
100
|
-
|
|
101
114
|
await snapshotAction({ limit: 5 })
|
|
102
115
|
|
|
103
|
-
expect(
|
|
116
|
+
expect(mockListMessages).toHaveBeenCalledWith('space-1', { max: 5 })
|
|
104
117
|
})
|
|
105
118
|
})
|
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
import { afterEach, beforeEach, describe, expect, spyOn, test } from 'bun:test'
|
|
1
|
+
import { afterAll, afterEach, beforeEach, describe, expect, mock, spyOn, test } from 'bun:test'
|
|
2
2
|
|
|
3
|
-
import { WebexClient } from '../client'
|
|
4
3
|
import { WebexError } from '../types'
|
|
5
|
-
|
|
4
|
+
|
|
5
|
+
const mockHandleError = mock((err: Error) => {
|
|
6
|
+
throw err
|
|
7
|
+
})
|
|
8
|
+
|
|
9
|
+
mock.module('@/shared/utils/error-handler', () => ({
|
|
10
|
+
handleError: mockHandleError,
|
|
11
|
+
}))
|
|
6
12
|
|
|
7
13
|
const mockSpaces = [
|
|
8
14
|
{
|
|
@@ -36,54 +42,46 @@ const mockSpace = {
|
|
|
36
42
|
creatorId: 'person-1',
|
|
37
43
|
}
|
|
38
44
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
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
|
|
45
|
+
const mockListSpaces = mock(() => Promise.resolve(mockSpaces))
|
|
46
|
+
const mockGetSpace = mock(() => Promise.resolve(mockSpace))
|
|
47
|
+
const mockLogin = mock(() => Promise.resolve({ listSpaces: mockListSpaces, getSpace: mockGetSpace }))
|
|
47
48
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
49
|
+
mock.module('../client', () => ({
|
|
50
|
+
WebexClient: class {
|
|
51
|
+
login = mockLogin
|
|
52
|
+
},
|
|
53
|
+
}))
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
import { infoAction, listAction } from './space'
|
|
54
56
|
|
|
55
|
-
|
|
57
|
+
afterAll(() => {
|
|
58
|
+
mock.restore()
|
|
59
|
+
})
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
consoleErrorSpy = spyOn(console, 'error').mockImplementation(() => {})
|
|
61
|
+
let consoleLogSpy: ReturnType<typeof spyOn>
|
|
59
62
|
|
|
60
|
-
|
|
61
|
-
|
|
63
|
+
beforeEach(() => {
|
|
64
|
+
mockListSpaces.mockReset().mockImplementation(() => Promise.resolve(mockSpaces))
|
|
65
|
+
mockGetSpace.mockReset().mockImplementation(() => Promise.resolve(mockSpace))
|
|
66
|
+
mockLogin.mockReset().mockImplementation(() =>
|
|
67
|
+
Promise.resolve({ listSpaces: mockListSpaces, getSpace: mockGetSpace }),
|
|
68
|
+
)
|
|
69
|
+
mockHandleError.mockReset().mockImplementation((err: Error) => {
|
|
70
|
+
throw err
|
|
62
71
|
})
|
|
63
72
|
|
|
64
|
-
|
|
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
|
|
73
|
+
consoleLogSpy = spyOn(console, 'log').mockImplementation(() => {})
|
|
70
74
|
})
|
|
71
75
|
|
|
72
76
|
afterEach(() => {
|
|
73
|
-
|
|
74
|
-
clientLoginSpy?.mockRestore()
|
|
75
|
-
clientListSpacesSpy?.mockRestore()
|
|
76
|
-
clientGetSpaceSpy?.mockRestore()
|
|
77
|
-
consoleLogSpy?.mockRestore()
|
|
78
|
-
consoleErrorSpy?.mockRestore()
|
|
79
|
-
processExitSpy?.mockRestore()
|
|
77
|
+
consoleLogSpy.mockRestore()
|
|
80
78
|
})
|
|
81
79
|
|
|
82
80
|
describe('listAction', () => {
|
|
83
81
|
test('calls listSpaces and outputs mapped array', async () => {
|
|
84
82
|
await listAction({})
|
|
85
83
|
|
|
86
|
-
expect(
|
|
84
|
+
expect(mockListSpaces).toHaveBeenCalled()
|
|
87
85
|
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
88
86
|
JSON.stringify([
|
|
89
87
|
{
|
|
@@ -107,13 +105,13 @@ describe('listAction', () => {
|
|
|
107
105
|
test('passes type and limit options to listSpaces', async () => {
|
|
108
106
|
await listAction({ type: 'group', limit: 10 })
|
|
109
107
|
|
|
110
|
-
expect(
|
|
108
|
+
expect(mockListSpaces).toHaveBeenCalledWith({ type: 'group', max: 10 })
|
|
111
109
|
})
|
|
112
110
|
|
|
113
111
|
test('passes undefined type and limit when not provided', async () => {
|
|
114
112
|
await listAction({})
|
|
115
113
|
|
|
116
|
-
expect(
|
|
114
|
+
expect(mockListSpaces).toHaveBeenCalledWith({ type: undefined, max: undefined })
|
|
117
115
|
})
|
|
118
116
|
|
|
119
117
|
test('outputs pretty-printed JSON when pretty is true', async () => {
|
|
@@ -144,12 +142,14 @@ describe('listAction', () => {
|
|
|
144
142
|
})
|
|
145
143
|
|
|
146
144
|
test('not authenticated: outputs error and exits', async () => {
|
|
147
|
-
|
|
145
|
+
mockLogin.mockImplementation(async () => {
|
|
146
|
+
throw new WebexError('No Webex credentials found.', 'no_credentials')
|
|
147
|
+
})
|
|
148
148
|
|
|
149
|
-
await expect(listAction({})).rejects.toThrow('
|
|
149
|
+
await expect(listAction({})).rejects.toThrow('No Webex credentials found.')
|
|
150
150
|
|
|
151
|
-
expect(
|
|
152
|
-
expect(
|
|
151
|
+
expect(mockListSpaces).not.toHaveBeenCalled()
|
|
152
|
+
expect(mockHandleError).toHaveBeenCalledWith(expect.any(WebexError))
|
|
153
153
|
})
|
|
154
154
|
})
|
|
155
155
|
|
|
@@ -157,7 +157,7 @@ describe('infoAction', () => {
|
|
|
157
157
|
test('calls getSpace with spaceId and outputs space details', async () => {
|
|
158
158
|
await infoAction('space-1', {})
|
|
159
159
|
|
|
160
|
-
expect(
|
|
160
|
+
expect(mockGetSpace).toHaveBeenCalledWith('space-1')
|
|
161
161
|
expect(consoleLogSpy).toHaveBeenCalledWith(
|
|
162
162
|
JSON.stringify({
|
|
163
163
|
id: 'space-1',
|
|
@@ -173,8 +173,7 @@ describe('infoAction', () => {
|
|
|
173
173
|
})
|
|
174
174
|
|
|
175
175
|
test('outputs null for teamId when not present', async () => {
|
|
176
|
-
|
|
177
|
-
clientGetSpaceSpy.mockResolvedValue(spaceWithoutTeam)
|
|
176
|
+
mockGetSpace.mockImplementation(() => Promise.resolve({ ...mockSpace, teamId: undefined }))
|
|
178
177
|
|
|
179
178
|
await infoAction('space-1', {})
|
|
180
179
|
|
|
@@ -206,11 +205,13 @@ describe('infoAction', () => {
|
|
|
206
205
|
})
|
|
207
206
|
|
|
208
207
|
test('not authenticated: outputs error and exits', async () => {
|
|
209
|
-
|
|
208
|
+
mockLogin.mockImplementation(async () => {
|
|
209
|
+
throw new WebexError('No Webex credentials found.', 'no_credentials')
|
|
210
|
+
})
|
|
210
211
|
|
|
211
|
-
await expect(infoAction('space-1', {})).rejects.toThrow('
|
|
212
|
+
await expect(infoAction('space-1', {})).rejects.toThrow('No Webex credentials found.')
|
|
212
213
|
|
|
213
|
-
expect(
|
|
214
|
-
expect(
|
|
214
|
+
expect(mockGetSpace).not.toHaveBeenCalled()
|
|
215
|
+
expect(mockHandleError).toHaveBeenCalledWith(expect.any(WebexError))
|
|
215
216
|
})
|
|
216
217
|
})
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
|
|
3
|
+
import { Command } from 'commander'
|
|
4
|
+
|
|
5
|
+
import pkg from '../../../package.json' with { type: 'json' }
|
|
6
|
+
import { authCommand, messageCommand, templateCommand, userCommand } from './commands/index'
|
|
7
|
+
|
|
8
|
+
const program = new Command()
|
|
9
|
+
|
|
10
|
+
program
|
|
11
|
+
.name('agent-wechatbot')
|
|
12
|
+
.description('CLI tool for WeChat Official Account bot integration')
|
|
13
|
+
.version(pkg.version)
|
|
14
|
+
.option('--pretty', 'Pretty-print JSON output')
|
|
15
|
+
.option('--account <id>', 'Account ID to use')
|
|
16
|
+
|
|
17
|
+
program.addCommand(authCommand)
|
|
18
|
+
program.addCommand(messageCommand)
|
|
19
|
+
program.addCommand(templateCommand)
|
|
20
|
+
program.addCommand(userCommand)
|
|
21
|
+
|
|
22
|
+
program.parseAsync(process.argv)
|
|
23
|
+
|
|
24
|
+
export default program
|