agent-messenger 1.3.6 → 1.5.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 +38 -14
- package/.claude-plugin/plugin.json +1 -1
- package/.github/workflows/ci.yml +3 -0
- package/CONTRIBUTING.md +24 -1
- package/README.md +12 -8
- package/dist/package.json +1 -1
- package/dist/src/cli.d.ts.map +1 -1
- package/dist/src/cli.js +3 -0
- package/dist/src/cli.js.map +1 -1
- package/dist/src/platforms/discord/cli.d.ts +2 -2
- package/dist/src/platforms/discord/cli.d.ts.map +1 -1
- package/dist/src/platforms/discord/cli.js +23 -1
- package/dist/src/platforms/discord/cli.js.map +1 -1
- package/dist/src/platforms/discord/commands/file.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/file.js +13 -7
- package/dist/src/platforms/discord/commands/file.js.map +1 -1
- package/dist/src/platforms/discord/commands/friend.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/friend.js +30 -30
- package/dist/src/platforms/discord/commands/friend.js.map +1 -1
- package/dist/src/platforms/discord/commands/index.d.ts +7 -0
- package/dist/src/platforms/discord/commands/index.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/index.js +7 -0
- package/dist/src/platforms/discord/commands/index.js.map +1 -1
- package/dist/src/platforms/discord/commands/snapshot.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/snapshot.js +1 -2
- package/dist/src/platforms/discord/commands/snapshot.js.map +1 -1
- package/dist/src/platforms/discord/ensure-auth.d.ts +2 -0
- package/dist/src/platforms/discord/ensure-auth.d.ts.map +1 -0
- package/dist/src/platforms/discord/ensure-auth.js +31 -0
- package/dist/src/platforms/discord/ensure-auth.js.map +1 -0
- package/dist/src/platforms/slack/cli.d.ts +2 -2
- package/dist/src/platforms/slack/cli.d.ts.map +1 -1
- package/dist/src/platforms/slack/cli.js +15 -0
- package/dist/src/platforms/slack/cli.js.map +1 -1
- package/dist/src/platforms/slack/client.d.ts +1 -0
- package/dist/src/platforms/slack/client.d.ts.map +1 -1
- package/dist/src/platforms/slack/client.js +13 -0
- package/dist/src/platforms/slack/client.js.map +1 -1
- package/dist/src/platforms/slack/commands/channel.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/channel.js +2 -0
- package/dist/src/platforms/slack/commands/channel.js.map +1 -1
- package/dist/src/platforms/slack/commands/file.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/file.js +13 -5
- package/dist/src/platforms/slack/commands/file.js.map +1 -1
- package/dist/src/platforms/slack/commands/message.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/message.js +12 -6
- package/dist/src/platforms/slack/commands/message.js.map +1 -1
- package/dist/src/platforms/slack/commands/reaction.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/reaction.js +3 -0
- package/dist/src/platforms/slack/commands/reaction.js.map +1 -1
- package/dist/src/platforms/slack/commands/sections.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/sections.js +5 -6
- package/dist/src/platforms/slack/commands/sections.js.map +1 -1
- package/dist/src/platforms/slack/commands/snapshot.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/snapshot.js +1 -2
- package/dist/src/platforms/slack/commands/snapshot.js.map +1 -1
- package/dist/src/platforms/slack/commands/unread.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/unread.js +2 -0
- package/dist/src/platforms/slack/commands/unread.js.map +1 -1
- package/dist/src/platforms/slack/commands/user.js +8 -8
- package/dist/src/platforms/slack/ensure-auth.d.ts +2 -0
- package/dist/src/platforms/slack/ensure-auth.d.ts.map +1 -0
- package/dist/src/platforms/slack/ensure-auth.js +30 -0
- package/dist/src/platforms/slack/ensure-auth.js.map +1 -0
- package/dist/src/platforms/slackbot/client.d.ts +1 -0
- package/dist/src/platforms/slackbot/client.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/client.js +13 -0
- package/dist/src/platforms/slackbot/client.js.map +1 -1
- package/dist/src/platforms/slackbot/commands/channel.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/commands/channel.js +3 -2
- package/dist/src/platforms/slackbot/commands/channel.js.map +1 -1
- package/dist/src/platforms/slackbot/commands/message.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/commands/message.js +18 -12
- package/dist/src/platforms/slackbot/commands/message.js.map +1 -1
- package/dist/src/platforms/slackbot/commands/reaction.d.ts.map +1 -1
- package/dist/src/platforms/slackbot/commands/reaction.js +8 -6
- package/dist/src/platforms/slackbot/commands/reaction.js.map +1 -1
- package/dist/src/platforms/teams/cli.d.ts +2 -2
- package/dist/src/platforms/teams/cli.d.ts.map +1 -1
- package/dist/src/platforms/teams/cli.js +15 -0
- package/dist/src/platforms/teams/cli.js.map +1 -1
- package/dist/src/platforms/teams/commands/file.js +12 -12
- package/dist/src/platforms/teams/commands/file.js.map +1 -1
- package/dist/src/platforms/teams/commands/snapshot.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/snapshot.js +1 -2
- package/dist/src/platforms/teams/commands/snapshot.js.map +1 -1
- package/dist/src/platforms/teams/ensure-auth.d.ts +2 -0
- package/dist/src/platforms/teams/ensure-auth.d.ts.map +1 -0
- package/dist/src/platforms/teams/ensure-auth.js +32 -0
- package/dist/src/platforms/teams/ensure-auth.js.map +1 -0
- package/e2e/README.md +1 -1
- package/package.json +1 -1
- package/skills/agent-discord/SKILL.md +22 -22
- package/skills/agent-slack/SKILL.md +28 -40
- package/skills/agent-teams/SKILL.md +41 -65
- package/skills/agent-teams/references/common-patterns.md +63 -49
- package/src/cli.ts +4 -0
- package/src/platforms/discord/cli.ts +30 -0
- package/src/platforms/discord/commands/file.ts +13 -7
- package/src/platforms/discord/commands/friend.ts +34 -34
- package/src/platforms/discord/commands/index.ts +7 -0
- package/src/platforms/discord/commands/snapshot.ts +1 -2
- package/src/platforms/discord/ensure-auth.test.ts +123 -0
- package/src/platforms/discord/ensure-auth.ts +31 -0
- package/src/platforms/slack/cli.ts +16 -0
- package/src/platforms/slack/client.test.ts +101 -0
- package/src/platforms/slack/client.ts +22 -0
- package/src/platforms/slack/commands/channel.ts +2 -0
- package/src/platforms/slack/commands/file.ts +15 -5
- package/src/platforms/slack/commands/message.ts +17 -6
- package/src/platforms/slack/commands/reaction.ts +3 -0
- package/src/platforms/slack/commands/sections.ts +8 -9
- package/src/platforms/slack/commands/snapshot.ts +1 -2
- package/src/platforms/slack/commands/unread.ts +2 -0
- package/src/platforms/slack/commands/user.ts +8 -8
- package/src/platforms/slack/ensure-auth.test.ts +186 -0
- package/src/platforms/slack/ensure-auth.ts +30 -0
- package/src/platforms/slackbot/client.test.ts +87 -0
- package/src/platforms/slackbot/client.ts +21 -0
- package/src/platforms/slackbot/commands/channel.ts +3 -2
- package/src/platforms/slackbot/commands/message.ts +18 -12
- package/src/platforms/slackbot/commands/reaction.ts +8 -6
- package/src/platforms/teams/cli.ts +16 -0
- package/src/platforms/teams/commands/file.ts +12 -12
- package/src/platforms/teams/commands/snapshot.ts +1 -2
- package/src/platforms/teams/ensure-auth.test.ts +167 -0
- package/src/platforms/teams/ensure-auth.ts +34 -0
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, spyOn, test } from 'bun:test'
|
|
2
|
+
import { DiscordClient } from './client'
|
|
3
|
+
import { DiscordCredentialManager } from './credential-manager'
|
|
4
|
+
import { ensureDiscordAuth } from './ensure-auth'
|
|
5
|
+
import { DiscordTokenExtractor } from './token-extractor'
|
|
6
|
+
|
|
7
|
+
let getTokenSpy: ReturnType<typeof spyOn>
|
|
8
|
+
let extractSpy: ReturnType<typeof spyOn>
|
|
9
|
+
let testAuthSpy: ReturnType<typeof spyOn>
|
|
10
|
+
let listServersSpy: ReturnType<typeof spyOn>
|
|
11
|
+
let saveSpy: ReturnType<typeof spyOn>
|
|
12
|
+
|
|
13
|
+
beforeEach(() => {
|
|
14
|
+
getTokenSpy = spyOn(DiscordCredentialManager.prototype, 'getToken').mockResolvedValue(null)
|
|
15
|
+
|
|
16
|
+
extractSpy = spyOn(DiscordTokenExtractor.prototype, 'extract').mockResolvedValue({
|
|
17
|
+
token: 'test-token-123',
|
|
18
|
+
})
|
|
19
|
+
|
|
20
|
+
testAuthSpy = spyOn(DiscordClient.prototype, 'testAuth').mockResolvedValue({
|
|
21
|
+
id: 'user-123',
|
|
22
|
+
username: 'testuser',
|
|
23
|
+
})
|
|
24
|
+
|
|
25
|
+
listServersSpy = spyOn(DiscordClient.prototype, 'listServers').mockResolvedValue([
|
|
26
|
+
{ id: 'server-1', name: 'Server One' },
|
|
27
|
+
{ id: 'server-2', name: 'Server Two' },
|
|
28
|
+
])
|
|
29
|
+
|
|
30
|
+
saveSpy = spyOn(DiscordCredentialManager.prototype, 'save').mockResolvedValue(undefined)
|
|
31
|
+
})
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
getTokenSpy?.mockRestore()
|
|
35
|
+
extractSpy?.mockRestore()
|
|
36
|
+
testAuthSpy?.mockRestore()
|
|
37
|
+
listServersSpy?.mockRestore()
|
|
38
|
+
saveSpy?.mockRestore()
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
describe('ensureDiscordAuth', () => {
|
|
42
|
+
test('skips extraction when token already exists', async () => {
|
|
43
|
+
// given
|
|
44
|
+
getTokenSpy.mockResolvedValue('existing-token')
|
|
45
|
+
|
|
46
|
+
// when
|
|
47
|
+
await ensureDiscordAuth()
|
|
48
|
+
|
|
49
|
+
// then
|
|
50
|
+
expect(extractSpy).not.toHaveBeenCalled()
|
|
51
|
+
})
|
|
52
|
+
|
|
53
|
+
test('extracts and saves credentials when no token', async () => {
|
|
54
|
+
// when
|
|
55
|
+
await ensureDiscordAuth()
|
|
56
|
+
|
|
57
|
+
// then
|
|
58
|
+
expect(extractSpy).toHaveBeenCalled()
|
|
59
|
+
expect(testAuthSpy).toHaveBeenCalled()
|
|
60
|
+
expect(listServersSpy).toHaveBeenCalled()
|
|
61
|
+
expect(saveSpy).toHaveBeenCalledWith({
|
|
62
|
+
token: 'test-token-123',
|
|
63
|
+
current_server: 'server-1',
|
|
64
|
+
servers: {
|
|
65
|
+
'server-1': { server_id: 'server-1', server_name: 'Server One' },
|
|
66
|
+
'server-2': { server_id: 'server-2', server_name: 'Server Two' },
|
|
67
|
+
},
|
|
68
|
+
})
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
test('sets first server as current', async () => {
|
|
72
|
+
// when
|
|
73
|
+
await ensureDiscordAuth()
|
|
74
|
+
|
|
75
|
+
// then
|
|
76
|
+
expect(saveSpy).toHaveBeenCalledWith(expect.objectContaining({ current_server: 'server-1' }))
|
|
77
|
+
})
|
|
78
|
+
|
|
79
|
+
test('handles no servers with null current_server', async () => {
|
|
80
|
+
// given
|
|
81
|
+
listServersSpy.mockResolvedValue([])
|
|
82
|
+
|
|
83
|
+
// when
|
|
84
|
+
await ensureDiscordAuth()
|
|
85
|
+
|
|
86
|
+
// then
|
|
87
|
+
expect(saveSpy).toHaveBeenCalledWith(expect.objectContaining({ current_server: null, servers: {} }))
|
|
88
|
+
})
|
|
89
|
+
|
|
90
|
+
test('does not save when extraction returns null', async () => {
|
|
91
|
+
// given
|
|
92
|
+
extractSpy.mockResolvedValue(null)
|
|
93
|
+
|
|
94
|
+
// when
|
|
95
|
+
await ensureDiscordAuth()
|
|
96
|
+
|
|
97
|
+
// then
|
|
98
|
+
expect(testAuthSpy).not.toHaveBeenCalled()
|
|
99
|
+
expect(saveSpy).not.toHaveBeenCalled()
|
|
100
|
+
})
|
|
101
|
+
|
|
102
|
+
test('silently handles extraction failure', async () => {
|
|
103
|
+
// given
|
|
104
|
+
extractSpy.mockRejectedValue(new Error('Discord not found'))
|
|
105
|
+
|
|
106
|
+
// when
|
|
107
|
+
await ensureDiscordAuth()
|
|
108
|
+
|
|
109
|
+
// then
|
|
110
|
+
expect(saveSpy).not.toHaveBeenCalled()
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
test('silently handles auth validation failure', async () => {
|
|
114
|
+
// given
|
|
115
|
+
testAuthSpy.mockRejectedValue(new Error('401 Unauthorized'))
|
|
116
|
+
|
|
117
|
+
// when
|
|
118
|
+
await ensureDiscordAuth()
|
|
119
|
+
|
|
120
|
+
// then
|
|
121
|
+
expect(saveSpy).not.toHaveBeenCalled()
|
|
122
|
+
})
|
|
123
|
+
})
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { DiscordClient } from './client'
|
|
2
|
+
import { DiscordCredentialManager } from './credential-manager'
|
|
3
|
+
import { DiscordTokenExtractor } from './token-extractor'
|
|
4
|
+
|
|
5
|
+
export async function ensureDiscordAuth(): Promise<void> {
|
|
6
|
+
try {
|
|
7
|
+
const credManager = new DiscordCredentialManager()
|
|
8
|
+
const token = await credManager.getToken()
|
|
9
|
+
if (token) return
|
|
10
|
+
|
|
11
|
+
const extractor = new DiscordTokenExtractor()
|
|
12
|
+
const extracted = await extractor.extract()
|
|
13
|
+
if (!extracted) return
|
|
14
|
+
|
|
15
|
+
const client = new DiscordClient(extracted.token)
|
|
16
|
+
const authInfo = await client.testAuth()
|
|
17
|
+
if (!authInfo) return
|
|
18
|
+
|
|
19
|
+
const servers = await client.listServers()
|
|
20
|
+
const serverMap: Record<string, { server_id: string; server_name: string }> = {}
|
|
21
|
+
for (const server of servers) {
|
|
22
|
+
serverMap[server.id] = { server_id: server.id, server_name: server.name }
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
await credManager.save({
|
|
26
|
+
token: extracted.token,
|
|
27
|
+
current_server: servers[0]?.id ?? null,
|
|
28
|
+
servers: serverMap,
|
|
29
|
+
})
|
|
30
|
+
} catch {}
|
|
31
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bun
|
|
2
2
|
|
|
3
|
+
import type { Command as CommandType } from 'commander'
|
|
3
4
|
import { Command } from 'commander'
|
|
4
5
|
import pkg from '../../../package.json' with { type: 'json' }
|
|
5
6
|
import {
|
|
@@ -17,6 +18,16 @@ import {
|
|
|
17
18
|
userCommand,
|
|
18
19
|
workspaceCommand,
|
|
19
20
|
} from './commands/index'
|
|
21
|
+
import { ensureSlackAuth } from './ensure-auth'
|
|
22
|
+
|
|
23
|
+
function isAuthCommand(command: CommandType): boolean {
|
|
24
|
+
let cmd: CommandType | null = command
|
|
25
|
+
while (cmd) {
|
|
26
|
+
if (cmd.name() === 'auth') return true
|
|
27
|
+
cmd = cmd.parent
|
|
28
|
+
}
|
|
29
|
+
return false
|
|
30
|
+
}
|
|
20
31
|
|
|
21
32
|
const program = new Command()
|
|
22
33
|
|
|
@@ -27,6 +38,11 @@ program
|
|
|
27
38
|
.option('--pretty', 'Pretty-print JSON output')
|
|
28
39
|
.option('--workspace <id>', 'Use specific workspace')
|
|
29
40
|
|
|
41
|
+
program.hook('preAction', async (_thisCommand, actionCommand) => {
|
|
42
|
+
if (isAuthCommand(actionCommand)) return
|
|
43
|
+
await ensureSlackAuth()
|
|
44
|
+
})
|
|
45
|
+
|
|
30
46
|
program.addCommand(authCommand)
|
|
31
47
|
program.addCommand(workspaceCommand)
|
|
32
48
|
program.addCommand(messageCommand)
|
|
@@ -226,6 +226,107 @@ describe('SlackClient', () => {
|
|
|
226
226
|
})
|
|
227
227
|
})
|
|
228
228
|
|
|
229
|
+
describe('resolveChannel', () => {
|
|
230
|
+
beforeEach(() => resetMocks())
|
|
231
|
+
|
|
232
|
+
test('returns channel ID unchanged when input starts with C', async () => {
|
|
233
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie')
|
|
234
|
+
const channel = await client.resolveChannel('C123ABC')
|
|
235
|
+
expect(channel).toBe('C123ABC')
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
test('returns channel ID unchanged when input starts with D', async () => {
|
|
239
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie')
|
|
240
|
+
const channel = await client.resolveChannel('D123ABC')
|
|
241
|
+
expect(channel).toBe('D123ABC')
|
|
242
|
+
})
|
|
243
|
+
|
|
244
|
+
test('returns channel ID unchanged when input starts with G', async () => {
|
|
245
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie')
|
|
246
|
+
const channel = await client.resolveChannel('G123ABC')
|
|
247
|
+
expect(channel).toBe('G123ABC')
|
|
248
|
+
})
|
|
249
|
+
|
|
250
|
+
test('resolves channel name to ID by calling listChannels', async () => {
|
|
251
|
+
mockWebClient.conversations.list.mockResolvedValue({
|
|
252
|
+
ok: true,
|
|
253
|
+
channels: [
|
|
254
|
+
{
|
|
255
|
+
id: 'C123',
|
|
256
|
+
name: 'general',
|
|
257
|
+
is_private: false,
|
|
258
|
+
is_archived: false,
|
|
259
|
+
created: 1234567890,
|
|
260
|
+
creator: 'U123',
|
|
261
|
+
},
|
|
262
|
+
],
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie')
|
|
266
|
+
// @ts-expect-error - accessing private property for testing
|
|
267
|
+
client.client = mockWebClient as unknown as WebClient
|
|
268
|
+
|
|
269
|
+
const channel = await client.resolveChannel('general')
|
|
270
|
+
expect(channel).toBe('C123')
|
|
271
|
+
expect(mockWebClient.conversations.list).toHaveBeenCalledTimes(1)
|
|
272
|
+
})
|
|
273
|
+
|
|
274
|
+
test('strips leading # from channel name', async () => {
|
|
275
|
+
mockWebClient.conversations.list.mockResolvedValue({
|
|
276
|
+
ok: true,
|
|
277
|
+
channels: [
|
|
278
|
+
{
|
|
279
|
+
id: 'C123',
|
|
280
|
+
name: 'general',
|
|
281
|
+
is_private: false,
|
|
282
|
+
is_archived: false,
|
|
283
|
+
created: 1234567890,
|
|
284
|
+
creator: 'U123',
|
|
285
|
+
},
|
|
286
|
+
],
|
|
287
|
+
})
|
|
288
|
+
|
|
289
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie')
|
|
290
|
+
// @ts-expect-error - accessing private property for testing
|
|
291
|
+
client.client = mockWebClient as unknown as WebClient
|
|
292
|
+
|
|
293
|
+
const channel = await client.resolveChannel('#general')
|
|
294
|
+
expect(channel).toBe('C123')
|
|
295
|
+
expect(mockWebClient.conversations.list).toHaveBeenCalledTimes(1)
|
|
296
|
+
})
|
|
297
|
+
|
|
298
|
+
test('returns channel ID unchanged when input is #C prefixed ID', async () => {
|
|
299
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie')
|
|
300
|
+
const channel = await client.resolveChannel('#C123ABC')
|
|
301
|
+
expect(channel).toBe('C123ABC')
|
|
302
|
+
})
|
|
303
|
+
|
|
304
|
+
test("throws SlackError with code 'channel_not_found' when name is not found", async () => {
|
|
305
|
+
mockWebClient.conversations.list.mockResolvedValue({
|
|
306
|
+
ok: true,
|
|
307
|
+
channels: [
|
|
308
|
+
{
|
|
309
|
+
id: 'C123',
|
|
310
|
+
name: 'general',
|
|
311
|
+
is_private: false,
|
|
312
|
+
is_archived: false,
|
|
313
|
+
created: 1234567890,
|
|
314
|
+
creator: 'U123',
|
|
315
|
+
},
|
|
316
|
+
],
|
|
317
|
+
})
|
|
318
|
+
|
|
319
|
+
const client = new SlackClient('xoxc-token', 'xoxd-cookie')
|
|
320
|
+
// @ts-expect-error - accessing private property for testing
|
|
321
|
+
client.client = mockWebClient as unknown as WebClient
|
|
322
|
+
|
|
323
|
+
await expect(client.resolveChannel('missing-channel')).rejects.toMatchObject({
|
|
324
|
+
name: 'SlackError',
|
|
325
|
+
code: 'channel_not_found',
|
|
326
|
+
})
|
|
327
|
+
})
|
|
328
|
+
})
|
|
329
|
+
|
|
229
330
|
describe('getMessages', () => {
|
|
230
331
|
beforeEach(() => resetMocks())
|
|
231
332
|
|
|
@@ -161,6 +161,28 @@ export class SlackClient {
|
|
|
161
161
|
})
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
async resolveChannel(channel: string): Promise<string> {
|
|
165
|
+
const normalized = channel.replace(/^#/, '')
|
|
166
|
+
|
|
167
|
+
if (/^[CDG][A-Z0-9]+$/.test(normalized)) {
|
|
168
|
+
return normalized
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const name = normalized
|
|
172
|
+
|
|
173
|
+
const channels = await this.listChannels()
|
|
174
|
+
const found = channels.find((ch) => ch.name === name)
|
|
175
|
+
|
|
176
|
+
if (!found) {
|
|
177
|
+
throw new SlackError(
|
|
178
|
+
`Channel not found: "${channel}". Use channel ID or exact channel name.`,
|
|
179
|
+
'channel_not_found',
|
|
180
|
+
)
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return found.id
|
|
184
|
+
}
|
|
185
|
+
|
|
164
186
|
async sendMessage(channel: string, text: string, threadTs?: string): Promise<SlackMessage> {
|
|
165
187
|
return this.withRetry(async () => {
|
|
166
188
|
const response = await this.client.chat.postMessage({
|
|
@@ -56,6 +56,7 @@ async function infoAction(channel: string, options: { pretty?: boolean }): Promi
|
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
59
|
+
channel = await client.resolveChannel(channel)
|
|
59
60
|
const ch = await client.getChannel(channel)
|
|
60
61
|
|
|
61
62
|
const output = {
|
|
@@ -86,6 +87,7 @@ async function historyAction(channel: string, options: { limit?: number; pretty?
|
|
|
86
87
|
}
|
|
87
88
|
|
|
88
89
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
90
|
+
channel = await client.resolveChannel(channel)
|
|
89
91
|
const messages = await client.getMessages(channel, options.limit || 20)
|
|
90
92
|
|
|
91
93
|
const output = messages.map((msg) => ({
|
|
@@ -21,6 +21,7 @@ async function uploadAction(
|
|
|
21
21
|
}
|
|
22
22
|
|
|
23
23
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
24
|
+
channel = await client.resolveChannel(channel)
|
|
24
25
|
|
|
25
26
|
const filePath = resolve(path)
|
|
26
27
|
const fileBuffer = readFileSync(filePath)
|
|
@@ -57,7 +58,8 @@ async function listAction(options: { channel?: string; pretty?: boolean }): Prom
|
|
|
57
58
|
}
|
|
58
59
|
|
|
59
60
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
60
|
-
const
|
|
61
|
+
const channel = options.channel ? await client.resolveChannel(options.channel) : undefined
|
|
62
|
+
const files = await client.listFiles(channel)
|
|
61
63
|
|
|
62
64
|
const output = files.map((file) => ({
|
|
63
65
|
id: file.id,
|
|
@@ -115,19 +117,27 @@ async function infoAction(fileId: string, options: { pretty?: boolean }): Promis
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
export const fileCommand = new Command('file')
|
|
118
|
-
.description('
|
|
120
|
+
.description('File commands')
|
|
119
121
|
.addCommand(
|
|
120
122
|
new Command('upload')
|
|
121
|
-
.description('
|
|
123
|
+
.description('Upload file to channel')
|
|
122
124
|
.argument('<channel>', 'channel ID or name')
|
|
123
125
|
.argument('<path>', 'file path')
|
|
124
126
|
.option('--filename <name>', 'override filename')
|
|
127
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
125
128
|
.action(uploadAction),
|
|
126
129
|
)
|
|
127
130
|
.addCommand(
|
|
128
131
|
new Command('list')
|
|
129
|
-
.description('
|
|
132
|
+
.description('List files in workspace')
|
|
130
133
|
.option('--channel <id>', 'filter by channel')
|
|
134
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
131
135
|
.action(listAction),
|
|
132
136
|
)
|
|
133
|
-
.addCommand(
|
|
137
|
+
.addCommand(
|
|
138
|
+
new Command('info')
|
|
139
|
+
.description('Show file details')
|
|
140
|
+
.argument('<file>', 'file ID')
|
|
141
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
142
|
+
.action(infoAction),
|
|
143
|
+
)
|
|
@@ -6,7 +6,7 @@ import { CredentialManager } from '../credential-manager'
|
|
|
6
6
|
import type { SlackMessage } from '../types'
|
|
7
7
|
|
|
8
8
|
async function sendAction(
|
|
9
|
-
|
|
9
|
+
channelInput: string,
|
|
10
10
|
text: string,
|
|
11
11
|
options: { thread?: string; pretty?: boolean },
|
|
12
12
|
): Promise<void> {
|
|
@@ -20,6 +20,7 @@ async function sendAction(
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
23
|
+
const channel = await client.resolveChannel(channelInput)
|
|
23
24
|
const message = await client.sendMessage(channel, text, options.thread)
|
|
24
25
|
|
|
25
26
|
const output = {
|
|
@@ -37,7 +38,7 @@ async function sendAction(
|
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
async function listAction(
|
|
40
|
-
|
|
41
|
+
channelInput: string,
|
|
41
42
|
options: { limit?: number; thread?: string; pretty?: boolean },
|
|
42
43
|
): Promise<void> {
|
|
43
44
|
try {
|
|
@@ -50,6 +51,7 @@ async function listAction(
|
|
|
50
51
|
}
|
|
51
52
|
|
|
52
53
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
54
|
+
const channel = await client.resolveChannel(channelInput)
|
|
53
55
|
const limit = options.limit || 20
|
|
54
56
|
const messages = await client.getMessages(channel, limit)
|
|
55
57
|
|
|
@@ -70,7 +72,7 @@ async function listAction(
|
|
|
70
72
|
}
|
|
71
73
|
}
|
|
72
74
|
|
|
73
|
-
async function getAction(
|
|
75
|
+
async function getAction(channelInput: string, ts: string, options: { pretty?: boolean }): Promise<void> {
|
|
74
76
|
try {
|
|
75
77
|
const credManager = new CredentialManager()
|
|
76
78
|
const workspace = await credManager.getWorkspace()
|
|
@@ -81,6 +83,7 @@ async function getAction(channel: string, ts: string, options: { pretty?: boolea
|
|
|
81
83
|
}
|
|
82
84
|
|
|
83
85
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
86
|
+
const channel = await client.resolveChannel(channelInput)
|
|
84
87
|
const message = await client.getMessage(channel, ts)
|
|
85
88
|
|
|
86
89
|
if (!message) {
|
|
@@ -105,7 +108,12 @@ async function getAction(channel: string, ts: string, options: { pretty?: boolea
|
|
|
105
108
|
}
|
|
106
109
|
}
|
|
107
110
|
|
|
108
|
-
async function updateAction(
|
|
111
|
+
async function updateAction(
|
|
112
|
+
channelInput: string,
|
|
113
|
+
ts: string,
|
|
114
|
+
text: string,
|
|
115
|
+
options: { pretty?: boolean },
|
|
116
|
+
): Promise<void> {
|
|
109
117
|
try {
|
|
110
118
|
const credManager = new CredentialManager()
|
|
111
119
|
const workspace = await credManager.getWorkspace()
|
|
@@ -116,6 +124,7 @@ async function updateAction(channel: string, ts: string, text: string, options:
|
|
|
116
124
|
}
|
|
117
125
|
|
|
118
126
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
127
|
+
const channel = await client.resolveChannel(channelInput)
|
|
119
128
|
const message = await client.updateMessage(channel, ts, text)
|
|
120
129
|
|
|
121
130
|
const output = {
|
|
@@ -132,7 +141,7 @@ async function updateAction(channel: string, ts: string, text: string, options:
|
|
|
132
141
|
}
|
|
133
142
|
|
|
134
143
|
async function deleteAction(
|
|
135
|
-
|
|
144
|
+
channelInput: string,
|
|
136
145
|
ts: string,
|
|
137
146
|
options: { force?: boolean; pretty?: boolean },
|
|
138
147
|
): Promise<void> {
|
|
@@ -151,6 +160,7 @@ async function deleteAction(
|
|
|
151
160
|
}
|
|
152
161
|
|
|
153
162
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
163
|
+
const channel = await client.resolveChannel(channelInput)
|
|
154
164
|
await client.deleteMessage(channel, ts)
|
|
155
165
|
|
|
156
166
|
console.log(formatOutput({ deleted: ts }, options.pretty))
|
|
@@ -196,7 +206,7 @@ async function searchAction(
|
|
|
196
206
|
}
|
|
197
207
|
|
|
198
208
|
async function repliesAction(
|
|
199
|
-
|
|
209
|
+
channelInput: string,
|
|
200
210
|
threadTs: string,
|
|
201
211
|
options: { limit?: number; oldest?: string; latest?: string; cursor?: string; pretty?: boolean },
|
|
202
212
|
): Promise<void> {
|
|
@@ -210,6 +220,7 @@ async function repliesAction(
|
|
|
210
220
|
}
|
|
211
221
|
|
|
212
222
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
223
|
+
const channel = await client.resolveChannel(channelInput)
|
|
213
224
|
const result = await client.getThreadReplies(channel, threadTs, {
|
|
214
225
|
limit: options.limit,
|
|
215
226
|
oldest: options.oldest,
|
|
@@ -15,6 +15,7 @@ async function addAction(channel: string, ts: string, emoji: string, options: {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
const client = new SlackClient(ws.token, ws.cookie)
|
|
18
|
+
channel = await client.resolveChannel(channel)
|
|
18
19
|
await client.addReaction(channel, ts, emoji)
|
|
19
20
|
|
|
20
21
|
console.log(
|
|
@@ -44,6 +45,7 @@ async function removeAction(channel: string, ts: string, emoji: string, options:
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
const client = new SlackClient(ws.token, ws.cookie)
|
|
48
|
+
channel = await client.resolveChannel(channel)
|
|
47
49
|
await client.removeReaction(channel, ts, emoji)
|
|
48
50
|
|
|
49
51
|
console.log(
|
|
@@ -73,6 +75,7 @@ async function listAction(channel: string, ts: string, options: { pretty?: boole
|
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
const client = new SlackClient(ws.token, ws.cookie)
|
|
78
|
+
channel = await client.resolveChannel(channel)
|
|
76
79
|
const message = await client.getMessage(channel, ts)
|
|
77
80
|
|
|
78
81
|
if (!message) {
|
|
@@ -32,12 +32,11 @@ async function listAction(options: { pretty?: boolean }): Promise<void> {
|
|
|
32
32
|
}
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
const
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
export const sectionsCommand = sections
|
|
35
|
+
export const sectionsCommand = new Command('sections')
|
|
36
|
+
.description('Sidebar section commands')
|
|
37
|
+
.addCommand(
|
|
38
|
+
new Command('list')
|
|
39
|
+
.description('List all channel sections')
|
|
40
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
41
|
+
.action(listAction),
|
|
42
|
+
)
|
|
@@ -93,8 +93,7 @@ async function snapshotAction(options: {
|
|
|
93
93
|
}
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
export const snapshotCommand = new Command()
|
|
97
|
-
.name('snapshot')
|
|
96
|
+
export const snapshotCommand = new Command('snapshot')
|
|
98
97
|
.description('Get comprehensive workspace state for AI agents')
|
|
99
98
|
.option('--channels-only', 'Include only channels (exclude messages and users)')
|
|
100
99
|
.option('--users-only', 'Include only users (exclude channels and messages)')
|
|
@@ -45,6 +45,7 @@ export async function threadsAction(channel: string, threadTs: string, options:
|
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
48
|
+
channel = await client.resolveChannel(channel)
|
|
48
49
|
const threadView = await client.getThreadView(channel, threadTs)
|
|
49
50
|
|
|
50
51
|
const output = {
|
|
@@ -72,6 +73,7 @@ export async function markAction(channel: string, ts: string, options: { pretty?
|
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
76
|
+
channel = await client.resolveChannel(channel)
|
|
75
77
|
await client.markRead(channel, ts)
|
|
76
78
|
|
|
77
79
|
console.log(formatOutput({ marked_read: true, channel, ts }, options.pretty))
|
|
@@ -17,12 +17,12 @@ async function getClient(pretty?: boolean): Promise<SlackClient | null> {
|
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
export const userCommand = new Command('user')
|
|
20
|
-
.description('
|
|
20
|
+
.description('User commands')
|
|
21
21
|
.addCommand(
|
|
22
22
|
new Command('list')
|
|
23
|
-
.description('
|
|
24
|
-
.option('--include-bots', '
|
|
25
|
-
.option('--pretty', '
|
|
23
|
+
.description('List workspace users')
|
|
24
|
+
.option('--include-bots', 'Include bot users')
|
|
25
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
26
26
|
.action(async (options) => {
|
|
27
27
|
try {
|
|
28
28
|
const client = await getClient(options.pretty)
|
|
@@ -51,9 +51,9 @@ export const userCommand = new Command('user')
|
|
|
51
51
|
)
|
|
52
52
|
.addCommand(
|
|
53
53
|
new Command('info')
|
|
54
|
-
.description('
|
|
54
|
+
.description('Show user details')
|
|
55
55
|
.argument('<user>', 'user ID or username')
|
|
56
|
-
.option('--pretty', '
|
|
56
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
57
57
|
.action(async (userArg, options) => {
|
|
58
58
|
try {
|
|
59
59
|
const client = await getClient(options.pretty)
|
|
@@ -80,8 +80,8 @@ export const userCommand = new Command('user')
|
|
|
80
80
|
)
|
|
81
81
|
.addCommand(
|
|
82
82
|
new Command('me')
|
|
83
|
-
.description('
|
|
84
|
-
.option('--pretty', '
|
|
83
|
+
.description('Show current authenticated user')
|
|
84
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
85
85
|
.action(async (options) => {
|
|
86
86
|
try {
|
|
87
87
|
const client = await getClient(options.pretty)
|