agent-messenger 1.9.2 → 1.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/plugin.json +1 -1
- package/dist/package.json +1 -1
- package/dist/src/platforms/discord/commands/server.d.ts.map +1 -1
- package/dist/src/platforms/discord/commands/server.js +5 -2
- package/dist/src/platforms/discord/commands/server.js.map +1 -1
- package/dist/src/platforms/discord/token-extractor.d.ts +1 -0
- package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/discord/token-extractor.js +7 -0
- package/dist/src/platforms/discord/token-extractor.js.map +1 -1
- package/dist/src/platforms/discordbot/client.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/client.js +1 -1
- package/dist/src/platforms/discordbot/client.js.map +1 -1
- package/dist/src/platforms/slack/commands/auth.d.ts +1 -0
- package/dist/src/platforms/slack/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/auth.js +28 -6
- package/dist/src/platforms/slack/commands/auth.js.map +1 -1
- package/dist/src/platforms/slack/commands/file.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/file.js +1 -1
- package/dist/src/platforms/slack/commands/file.js.map +1 -1
- package/dist/src/platforms/slack/commands/reaction.js +4 -4
- package/dist/src/platforms/slack/commands/reaction.js.map +1 -1
- package/dist/src/platforms/slack/commands/workspace.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/workspace.js +2 -2
- package/dist/src/platforms/slack/commands/workspace.js.map +1 -1
- package/dist/src/platforms/slack/ensure-auth.d.ts +7 -0
- package/dist/src/platforms/slack/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/slack/ensure-auth.js +29 -3
- package/dist/src/platforms/slack/ensure-auth.js.map +1 -1
- package/dist/src/platforms/slack/token-extractor.d.ts +10 -2
- package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
- package/dist/src/platforms/slack/token-extractor.js +147 -23
- package/dist/src/platforms/slack/token-extractor.js.map +1 -1
- package/dist/src/platforms/teams/client.js +2 -2
- package/dist/src/platforms/teams/client.js.map +1 -1
- package/dist/src/platforms/teams/commands/team.d.ts.map +1 -1
- package/dist/src/platforms/teams/commands/team.js +4 -4
- package/dist/src/platforms/teams/commands/team.js.map +1 -1
- package/package.json +1 -1
- package/skills/agent-discord/SKILL.md +1 -1
- package/skills/agent-discordbot/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +1 -1
- package/skills/agent-slackbot/SKILL.md +1 -1
- package/skills/agent-teams/SKILL.md +1 -1
- package/src/platforms/discord/commands/server.ts +15 -2
- package/src/platforms/discord/token-extractor.test.ts +25 -0
- package/src/platforms/discord/token-extractor.ts +7 -0
- package/src/platforms/discordbot/client.ts +4 -1
- package/src/platforms/slack/commands/auth.test.ts +23 -0
- package/src/platforms/slack/commands/auth.ts +34 -6
- package/src/platforms/slack/commands/file.ts +6 -1
- package/src/platforms/slack/commands/reaction.ts +4 -4
- package/src/platforms/slack/commands/workspace.ts +12 -2
- package/src/platforms/slack/ensure-auth.test.ts +54 -1
- package/src/platforms/slack/ensure-auth.ts +32 -2
- package/src/platforms/slack/token-extractor.test.ts +207 -0
- package/src/platforms/slack/token-extractor.ts +161 -28
- package/src/platforms/teams/client.ts +2 -2
- package/src/platforms/teams/commands/team.ts +14 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../../../../src/platforms/teams/commands/team.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AACnC,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,WAAW,CAAA;AACvC,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAA;AAE9D,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,OAA6B;IAC5D,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,sBAAsB,EAAE,CAAA;QAChD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,iBAAiB,EAAE,CAAA;QACrD,MAAM,KAAK,GAAG,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAA;QAEhE,MAAM,MAAM,GAAG,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;YAClC,EAAE,EAAE,IAAI,CAAC,OAAO;YAChB,IAAI,EAAE,IAAI,CAAC,SAAS;YACpB,OAAO,EAAE,IAAI,CAAC,OAAO,KAAK,OAAO,EAAE,YAAY;SAChD,CAAC,CAAC,CAAA;QAEH,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,WAAW,CAAC,KAAc,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,MAAc,EAAE,OAA6B;IAC5E,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,sBAAsB,EAAE,CAAA;QAChD,MAAM,IAAI,GAAG,MAAM,WAAW,CAAC,kBAAkB,EAAE,CAAA;QAEnD,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,KAAK,EAAE,8CAA8C,EAAE,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;YACpG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACjB,CAAC;QAED,MAAM,MAAM,GAAG,IAAI,WAAW,CAAC,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,cAAc,CAAC,CAAA;QAC/D,MAAM,IAAI,GAAG,MAAM,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAA;QAEzC,MAAM,MAAM,GAAG;YACb,EAAE,EAAE,IAAI,CAAC,EAAE;YACX,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,WAAW,EAAE,IAAI,CAAC,WAAW;SAC9B,CAAA;QAED,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,CAAA;IACnD,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,WAAW,CAAC,KAAc,CAAC,CAAA;IAC7B,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,MAAc,EAAE,OAA6B;IAC9E,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,sBAAsB,EAAE,CAAA;QAChD,MAAM,OAAO,GAAG,MAAM,WAAW,CAAC,iBAAiB,EAAE,CAAA;QAErD,IAAI,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC,EAAE,CAAC;YAC9B,OAAO,CAAC,GAAG,
|
|
1
|
+
{"version":3,"file":"team.js","sourceRoot":"","sources":["../../../../../src/platforms/teams/commands/team.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -54,7 +54,12 @@ export async function switchAction(serverId: string, options: { pretty?: boolean
|
|
|
54
54
|
const config = await credManager.load()
|
|
55
55
|
|
|
56
56
|
if (!config.servers[serverId]) {
|
|
57
|
-
console.log(
|
|
57
|
+
console.log(
|
|
58
|
+
formatOutput(
|
|
59
|
+
{ error: `Server not found: ${serverId}`, hint: 'Run "server list" to see available servers.' },
|
|
60
|
+
options.pretty,
|
|
61
|
+
),
|
|
62
|
+
)
|
|
58
63
|
process.exit(1)
|
|
59
64
|
}
|
|
60
65
|
|
|
@@ -78,7 +83,15 @@ export async function currentAction(options: { pretty?: boolean }): Promise<void
|
|
|
78
83
|
const server = config.servers[config.current_server]
|
|
79
84
|
|
|
80
85
|
if (!server) {
|
|
81
|
-
console.log(
|
|
86
|
+
console.log(
|
|
87
|
+
formatOutput(
|
|
88
|
+
{
|
|
89
|
+
error: 'Current server not found in configuration.',
|
|
90
|
+
hint: 'Run "auth extract" to refresh, or "server switch <server-id>".',
|
|
91
|
+
},
|
|
92
|
+
options.pretty,
|
|
93
|
+
),
|
|
94
|
+
)
|
|
82
95
|
process.exit(1)
|
|
83
96
|
}
|
|
84
97
|
|
|
@@ -79,6 +79,31 @@ describe('DiscordTokenExtractor', () => {
|
|
|
79
79
|
})
|
|
80
80
|
})
|
|
81
81
|
|
|
82
|
+
describe('Linux token decryption', () => {
|
|
83
|
+
test('decrypts encrypted token using peanuts password on Linux', () => {
|
|
84
|
+
// given — AES-128-CBC encrypted token with Linux Chromium key
|
|
85
|
+
const { createCipheriv, pbkdf2Sync } = require('node:crypto')
|
|
86
|
+
const plainToken = 'XXXXXXXXXXXXXXXXXXXXXXXX.YYYYYY.ZZZZZZZZZZZZZZZZZZZZZZZZZ'
|
|
87
|
+
const key = pbkdf2Sync('peanuts', 'saltysalt', 1, 16, 'sha1')
|
|
88
|
+
const iv = Buffer.alloc(16, 0x20)
|
|
89
|
+
const cipher = createCipheriv('aes-128-cbc', key, iv)
|
|
90
|
+
const ciphertext = Buffer.concat([cipher.update(plainToken, 'utf8'), cipher.final()])
|
|
91
|
+
// v10 prefix (3 bytes) + ciphertext
|
|
92
|
+
const encrypted = Buffer.concat([Buffer.from('v10'), ciphertext])
|
|
93
|
+
const encryptedToken = `dQw4w9WgXcQ:${encrypted.toString('base64')}`
|
|
94
|
+
|
|
95
|
+
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
96
|
+
const decryptTokenSpy = spyOn(linuxExtractor as any, 'decryptToken')
|
|
97
|
+
decryptTokenSpy.mockRestore()
|
|
98
|
+
|
|
99
|
+
// when
|
|
100
|
+
const result = (linuxExtractor as any).decryptToken(encryptedToken, '/home/user/.config/discord')
|
|
101
|
+
|
|
102
|
+
// then
|
|
103
|
+
expect(result).toBe(plainToken)
|
|
104
|
+
})
|
|
105
|
+
})
|
|
106
|
+
|
|
82
107
|
describe('extract', () => {
|
|
83
108
|
test('returns null when no Discord directories exist on linux', async () => {
|
|
84
109
|
const linuxExtractor = new DiscordTokenExtractor('linux')
|
|
@@ -260,6 +260,8 @@ export class DiscordTokenExtractor {
|
|
|
260
260
|
return this.decryptWindowsToken(encryptedData, discordDir)
|
|
261
261
|
} else if (this.platform === 'darwin') {
|
|
262
262
|
return this.decryptMacToken(encryptedData, discordDir)
|
|
263
|
+
} else if (this.platform === 'linux') {
|
|
264
|
+
return this.decryptLinuxToken(encryptedData)
|
|
263
265
|
}
|
|
264
266
|
|
|
265
267
|
return null
|
|
@@ -335,6 +337,11 @@ export class DiscordTokenExtractor {
|
|
|
335
337
|
return null
|
|
336
338
|
}
|
|
337
339
|
|
|
340
|
+
private decryptLinuxToken(encryptedData: Buffer): string | null {
|
|
341
|
+
const key = pbkdf2Sync('peanuts', 'saltysalt', 1, 16, 'sha1')
|
|
342
|
+
return this.decryptAESCBC(encryptedData, key)
|
|
343
|
+
}
|
|
344
|
+
|
|
338
345
|
private decryptAESCBC(encryptedData: Buffer, key: Buffer): string | null {
|
|
339
346
|
try {
|
|
340
347
|
const ciphertext = encryptedData.subarray(3)
|
|
@@ -334,7 +334,10 @@ export class DiscordBotClient {
|
|
|
334
334
|
const channels = await this.listChannels(guildId)
|
|
335
335
|
const found = channels.find((c) => c.name === channel || c.name === channel.replace(/^#/, ''))
|
|
336
336
|
if (!found) {
|
|
337
|
-
throw new DiscordBotError(
|
|
337
|
+
throw new DiscordBotError(
|
|
338
|
+
`Channel not found: "${channel}". Use channel ID or exact channel name.`,
|
|
339
|
+
'channel_not_found',
|
|
340
|
+
)
|
|
338
341
|
}
|
|
339
342
|
return found.id
|
|
340
343
|
}
|
|
@@ -2,6 +2,7 @@ import { afterAll, beforeEach, describe, expect, mock, test } from 'bun:test'
|
|
|
2
2
|
import { mkdirSync, rmSync } from 'node:fs'
|
|
3
3
|
import { homedir } from 'node:os'
|
|
4
4
|
import { join } from 'node:path'
|
|
5
|
+
import { getExtractionErrorMessage } from '@/platforms/slack/commands/auth'
|
|
5
6
|
import { CredentialManager } from '@/platforms/slack/credential-manager'
|
|
6
7
|
import { type ExtractedWorkspace, TokenExtractor } from '@/platforms/slack/token-extractor'
|
|
7
8
|
|
|
@@ -351,6 +352,28 @@ describe('Output Formatting', () => {
|
|
|
351
352
|
})
|
|
352
353
|
})
|
|
353
354
|
|
|
355
|
+
describe('getExtractionErrorMessage', () => {
|
|
356
|
+
test('returns cookie failure message for missing_cookie', () => {
|
|
357
|
+
expect(getExtractionErrorMessage(['missing_cookie'])).toContain('Cookie extraction failed')
|
|
358
|
+
})
|
|
359
|
+
|
|
360
|
+
test('returns session expired message for invalid_auth', () => {
|
|
361
|
+
expect(getExtractionErrorMessage(['invalid_auth'])).toContain('session has expired')
|
|
362
|
+
})
|
|
363
|
+
|
|
364
|
+
test('prioritizes missing_cookie over invalid_auth', () => {
|
|
365
|
+
expect(getExtractionErrorMessage(['invalid_auth', 'missing_cookie'])).toContain('Cookie extraction failed')
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
test('returns generic message for unknown error codes', () => {
|
|
369
|
+
expect(getExtractionErrorMessage(['unknown_error'])).toContain('Extracted tokens are invalid')
|
|
370
|
+
})
|
|
371
|
+
|
|
372
|
+
test('returns generic message for empty failure list', () => {
|
|
373
|
+
expect(getExtractionErrorMessage([])).toContain('Extracted tokens are invalid')
|
|
374
|
+
})
|
|
375
|
+
})
|
|
376
|
+
|
|
354
377
|
describe('Error Handling', () => {
|
|
355
378
|
beforeEach(() => {
|
|
356
379
|
rmSync(testSlackDir, { recursive: true, force: true })
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
import { Command } from 'commander'
|
|
2
2
|
import { handleError } from '@/shared/utils/error-handler'
|
|
3
3
|
import { formatOutput } from '@/shared/utils/output'
|
|
4
|
-
import { SlackClient } from '../client'
|
|
4
|
+
import { SlackClient, SlackError } from '../client'
|
|
5
5
|
import { CredentialManager } from '../credential-manager'
|
|
6
|
+
import { refreshCookie } from '../ensure-auth'
|
|
6
7
|
import { TokenExtractor } from '../token-extractor'
|
|
7
8
|
|
|
8
9
|
async function extractAction(options: { pretty?: boolean; debug?: boolean }): Promise<void> {
|
|
9
10
|
try {
|
|
10
|
-
const
|
|
11
|
+
const debugLog = options.debug ? (msg: string) => console.error(`[debug] ${msg}`) : undefined
|
|
12
|
+
const extractor = new TokenExtractor(undefined, undefined, undefined, debugLog)
|
|
11
13
|
|
|
12
14
|
if (process.platform === 'darwin') {
|
|
13
15
|
console.log('')
|
|
@@ -58,6 +60,7 @@ async function extractAction(options: { pretty?: boolean; debug?: boolean }): Pr
|
|
|
58
60
|
const config = await credManager.load()
|
|
59
61
|
|
|
60
62
|
const validWorkspaces = []
|
|
63
|
+
const failureReasons: string[] = []
|
|
61
64
|
for (const ws of workspaces) {
|
|
62
65
|
if (options.debug) {
|
|
63
66
|
console.error(`[debug] Testing credentials for ${ws.workspace_id}...`)
|
|
@@ -74,6 +77,10 @@ async function extractAction(options: { pretty?: boolean; debug?: boolean }): Pr
|
|
|
74
77
|
console.error(`[debug] ✓ Valid: ${authInfo.team} (${authInfo.user})`)
|
|
75
78
|
}
|
|
76
79
|
} catch (error) {
|
|
80
|
+
const code = error instanceof SlackError ? error.code : undefined
|
|
81
|
+
if (code && !failureReasons.includes(code)) {
|
|
82
|
+
failureReasons.push(code)
|
|
83
|
+
}
|
|
77
84
|
if (options.debug) {
|
|
78
85
|
console.error(`[debug] ✗ Invalid: ${(error as Error).message}`)
|
|
79
86
|
}
|
|
@@ -81,11 +88,13 @@ async function extractAction(options: { pretty?: boolean; debug?: boolean }): Pr
|
|
|
81
88
|
}
|
|
82
89
|
|
|
83
90
|
if (validWorkspaces.length === 0) {
|
|
91
|
+
const errorMessage = getExtractionErrorMessage(failureReasons)
|
|
84
92
|
console.log(
|
|
85
93
|
formatOutput(
|
|
86
94
|
{
|
|
87
|
-
error:
|
|
95
|
+
error: errorMessage,
|
|
88
96
|
extracted_count: workspaces.length,
|
|
97
|
+
hint: options.debug ? undefined : 'Run with --debug for more details.',
|
|
89
98
|
},
|
|
90
99
|
options.pretty,
|
|
91
100
|
),
|
|
@@ -124,7 +133,15 @@ async function logoutAction(workspace: string | undefined, options: { pretty?: b
|
|
|
124
133
|
}
|
|
125
134
|
|
|
126
135
|
if (!config.workspaces[targetWorkspace]) {
|
|
127
|
-
console.log(
|
|
136
|
+
console.log(
|
|
137
|
+
formatOutput(
|
|
138
|
+
{
|
|
139
|
+
error: `Workspace not found: ${targetWorkspace}`,
|
|
140
|
+
hint: 'Run "workspace list" to see available workspaces.',
|
|
141
|
+
},
|
|
142
|
+
options.pretty,
|
|
143
|
+
),
|
|
144
|
+
)
|
|
128
145
|
process.exit(1)
|
|
129
146
|
}
|
|
130
147
|
|
|
@@ -142,7 +159,7 @@ async function statusAction(options: { pretty?: boolean }): Promise<void> {
|
|
|
142
159
|
const ws = await credManager.getWorkspace()
|
|
143
160
|
|
|
144
161
|
if (!ws) {
|
|
145
|
-
console.log(formatOutput({ error: 'No workspace
|
|
162
|
+
console.log(formatOutput({ error: 'No current workspace set. Run "auth extract" first.' }, options.pretty))
|
|
146
163
|
process.exit(1)
|
|
147
164
|
}
|
|
148
165
|
|
|
@@ -154,7 +171,8 @@ async function statusAction(options: { pretty?: boolean }): Promise<void> {
|
|
|
154
171
|
authInfo = await client.testAuth()
|
|
155
172
|
valid = true
|
|
156
173
|
} catch {
|
|
157
|
-
|
|
174
|
+
authInfo = await refreshCookie(ws.token, credManager)
|
|
175
|
+
valid = authInfo !== null
|
|
158
176
|
}
|
|
159
177
|
|
|
160
178
|
const output = {
|
|
@@ -171,6 +189,16 @@ async function statusAction(options: { pretty?: boolean }): Promise<void> {
|
|
|
171
189
|
}
|
|
172
190
|
}
|
|
173
191
|
|
|
192
|
+
export function getExtractionErrorMessage(failureReasons: string[]): string {
|
|
193
|
+
if (failureReasons.includes('missing_cookie')) {
|
|
194
|
+
return 'Cookie extraction failed. Make sure the Slack desktop app is installed and grant Keychain access when prompted.'
|
|
195
|
+
}
|
|
196
|
+
if (failureReasons.includes('invalid_auth')) {
|
|
197
|
+
return 'Slack session has expired. Sign into the Slack desktop app, wait a few seconds, then re-run this command.'
|
|
198
|
+
}
|
|
199
|
+
return 'Extracted tokens are invalid. Make sure you are logged into the Slack desktop app.'
|
|
200
|
+
}
|
|
201
|
+
|
|
174
202
|
export const authCommand = new Command('auth')
|
|
175
203
|
.description('Authentication commands')
|
|
176
204
|
.addCommand(
|
|
@@ -94,7 +94,12 @@ async function infoAction(fileId: string, options: { pretty?: boolean }): Promis
|
|
|
94
94
|
const fileData = files.find((f) => f.id === fileId)
|
|
95
95
|
|
|
96
96
|
if (!fileData) {
|
|
97
|
-
console.log(
|
|
97
|
+
console.log(
|
|
98
|
+
formatOutput(
|
|
99
|
+
{ error: `File not found: ${fileId}`, hint: 'Run "file list" to see available files.' },
|
|
100
|
+
options.pretty,
|
|
101
|
+
),
|
|
102
|
+
)
|
|
98
103
|
process.exit(1)
|
|
99
104
|
}
|
|
100
105
|
|
|
@@ -10,7 +10,7 @@ async function addAction(channel: string, ts: string, emoji: string, options: {
|
|
|
10
10
|
const ws = await credManager.getWorkspace()
|
|
11
11
|
|
|
12
12
|
if (!ws) {
|
|
13
|
-
console.log(formatOutput({ error: 'No workspace
|
|
13
|
+
console.log(formatOutput({ error: 'No current workspace set. Run "auth extract" first.' }, options.pretty))
|
|
14
14
|
process.exit(1)
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -40,7 +40,7 @@ async function removeAction(channel: string, ts: string, emoji: string, options:
|
|
|
40
40
|
const ws = await credManager.getWorkspace()
|
|
41
41
|
|
|
42
42
|
if (!ws) {
|
|
43
|
-
console.log(formatOutput({ error: 'No workspace
|
|
43
|
+
console.log(formatOutput({ error: 'No current workspace set. Run "auth extract" first.' }, options.pretty))
|
|
44
44
|
process.exit(1)
|
|
45
45
|
}
|
|
46
46
|
|
|
@@ -70,7 +70,7 @@ async function listAction(channel: string, ts: string, options: { pretty?: boole
|
|
|
70
70
|
const ws = await credManager.getWorkspace()
|
|
71
71
|
|
|
72
72
|
if (!ws) {
|
|
73
|
-
console.log(formatOutput({ error: 'No workspace
|
|
73
|
+
console.log(formatOutput({ error: 'No current workspace set. Run "auth extract" first.' }, options.pretty))
|
|
74
74
|
process.exit(1)
|
|
75
75
|
}
|
|
76
76
|
|
|
@@ -82,7 +82,7 @@ async function listAction(channel: string, ts: string, options: { pretty?: boole
|
|
|
82
82
|
console.log(
|
|
83
83
|
formatOutput(
|
|
84
84
|
{
|
|
85
|
-
error:
|
|
85
|
+
error: `Message not found: ${ts}`,
|
|
86
86
|
channel,
|
|
87
87
|
ts,
|
|
88
88
|
},
|
|
@@ -27,7 +27,12 @@ async function switchAction(id: string, options: { pretty?: boolean }): Promise<
|
|
|
27
27
|
const config = await credManager.load()
|
|
28
28
|
|
|
29
29
|
if (!config.workspaces[id]) {
|
|
30
|
-
console.log(
|
|
30
|
+
console.log(
|
|
31
|
+
formatOutput(
|
|
32
|
+
{ error: `Workspace not found: ${id}`, hint: 'Run "workspace list" to see available workspaces.' },
|
|
33
|
+
options.pretty,
|
|
34
|
+
),
|
|
35
|
+
)
|
|
31
36
|
process.exit(1)
|
|
32
37
|
}
|
|
33
38
|
|
|
@@ -65,7 +70,12 @@ async function removeAction(id: string, options: { pretty?: boolean }): Promise<
|
|
|
65
70
|
const config = await credManager.load()
|
|
66
71
|
|
|
67
72
|
if (!config.workspaces[id]) {
|
|
68
|
-
console.log(
|
|
73
|
+
console.log(
|
|
74
|
+
formatOutput(
|
|
75
|
+
{ error: `Workspace not found: ${id}`, hint: 'Run "workspace list" to see available workspaces.' },
|
|
76
|
+
options.pretty,
|
|
77
|
+
),
|
|
78
|
+
)
|
|
69
79
|
process.exit(1)
|
|
70
80
|
}
|
|
71
81
|
|
|
@@ -6,6 +6,7 @@ import { TokenExtractor } from './token-extractor'
|
|
|
6
6
|
|
|
7
7
|
let getWorkspaceSpy: ReturnType<typeof spyOn>
|
|
8
8
|
let extractSpy: ReturnType<typeof spyOn>
|
|
9
|
+
let extractCookieSpy: ReturnType<typeof spyOn>
|
|
9
10
|
let testAuthSpy: ReturnType<typeof spyOn>
|
|
10
11
|
let setWorkspaceSpy: ReturnType<typeof spyOn>
|
|
11
12
|
let loadSpy: ReturnType<typeof spyOn>
|
|
@@ -23,6 +24,8 @@ beforeEach(() => {
|
|
|
23
24
|
},
|
|
24
25
|
])
|
|
25
26
|
|
|
27
|
+
extractCookieSpy = spyOn(TokenExtractor.prototype, 'extractCookie').mockResolvedValue('xoxd-fresh-cookie')
|
|
28
|
+
|
|
26
29
|
testAuthSpy = spyOn(SlackClient.prototype, 'testAuth').mockResolvedValue({
|
|
27
30
|
user_id: 'U123',
|
|
28
31
|
team_id: 'T123',
|
|
@@ -43,6 +46,7 @@ beforeEach(() => {
|
|
|
43
46
|
afterEach(() => {
|
|
44
47
|
getWorkspaceSpy?.mockRestore()
|
|
45
48
|
extractSpy?.mockRestore()
|
|
49
|
+
extractCookieSpy?.mockRestore()
|
|
46
50
|
testAuthSpy?.mockRestore()
|
|
47
51
|
setWorkspaceSpy?.mockRestore()
|
|
48
52
|
loadSpy?.mockRestore()
|
|
@@ -50,7 +54,7 @@ afterEach(() => {
|
|
|
50
54
|
})
|
|
51
55
|
|
|
52
56
|
describe('ensureSlackAuth', () => {
|
|
53
|
-
test('skips extraction when credentials
|
|
57
|
+
test('skips extraction when stored credentials are valid', async () => {
|
|
54
58
|
// given
|
|
55
59
|
getWorkspaceSpy.mockResolvedValue({
|
|
56
60
|
workspace_id: 'T123',
|
|
@@ -63,9 +67,58 @@ describe('ensureSlackAuth', () => {
|
|
|
63
67
|
await ensureSlackAuth()
|
|
64
68
|
|
|
65
69
|
// then
|
|
70
|
+
expect(testAuthSpy).toHaveBeenCalled()
|
|
66
71
|
expect(extractSpy).not.toHaveBeenCalled()
|
|
67
72
|
})
|
|
68
73
|
|
|
74
|
+
test('refreshes cookie when stored credentials are stale', async () => {
|
|
75
|
+
// given
|
|
76
|
+
getWorkspaceSpy.mockResolvedValue({
|
|
77
|
+
workspace_id: 'T123',
|
|
78
|
+
workspace_name: 'existing',
|
|
79
|
+
token: 'xoxc-existing',
|
|
80
|
+
cookie: 'xoxd-old-cookie',
|
|
81
|
+
})
|
|
82
|
+
let callCount = 0
|
|
83
|
+
testAuthSpy.mockImplementation(() => {
|
|
84
|
+
callCount++
|
|
85
|
+
if (callCount === 1) throw new Error('invalid_auth')
|
|
86
|
+
return Promise.resolve({ user_id: 'U1', team_id: 'T123', user: 'user', team: 'Team' })
|
|
87
|
+
})
|
|
88
|
+
loadSpy.mockResolvedValue({
|
|
89
|
+
current_workspace: 'T123',
|
|
90
|
+
workspaces: {
|
|
91
|
+
T123: { workspace_id: 'T123', workspace_name: 'existing', token: 'xoxc-existing', cookie: 'xoxd-old-cookie' },
|
|
92
|
+
},
|
|
93
|
+
})
|
|
94
|
+
|
|
95
|
+
// when
|
|
96
|
+
await ensureSlackAuth()
|
|
97
|
+
|
|
98
|
+
// then — cookie refreshed, no full extraction
|
|
99
|
+
expect(extractCookieSpy).toHaveBeenCalled()
|
|
100
|
+
expect(extractSpy).not.toHaveBeenCalled()
|
|
101
|
+
expect(setWorkspaceSpy).toHaveBeenCalledWith(expect.objectContaining({ cookie: 'xoxd-fresh-cookie' }))
|
|
102
|
+
})
|
|
103
|
+
|
|
104
|
+
test('falls through to full extraction when cookie refresh fails', async () => {
|
|
105
|
+
// given
|
|
106
|
+
getWorkspaceSpy.mockResolvedValue({
|
|
107
|
+
workspace_id: 'T123',
|
|
108
|
+
workspace_name: 'existing',
|
|
109
|
+
token: 'xoxc-existing',
|
|
110
|
+
cookie: 'xoxd-old-cookie',
|
|
111
|
+
})
|
|
112
|
+
testAuthSpy.mockRejectedValue(new Error('invalid_auth'))
|
|
113
|
+
extractCookieSpy.mockResolvedValue('')
|
|
114
|
+
|
|
115
|
+
// when
|
|
116
|
+
await ensureSlackAuth()
|
|
117
|
+
|
|
118
|
+
// then — falls through to full extraction
|
|
119
|
+
expect(extractSpy).toHaveBeenCalled()
|
|
120
|
+
})
|
|
121
|
+
|
|
69
122
|
test('extracts and saves credentials when none exist', async () => {
|
|
70
123
|
// when
|
|
71
124
|
await ensureSlackAuth()
|
|
@@ -5,7 +5,16 @@ import { TokenExtractor } from './token-extractor'
|
|
|
5
5
|
export async function ensureSlackAuth(): Promise<void> {
|
|
6
6
|
const credManager = new CredentialManager()
|
|
7
7
|
const workspace = await credManager.getWorkspace()
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
if (workspace) {
|
|
10
|
+
try {
|
|
11
|
+
const client = new SlackClient(workspace.token, workspace.cookie)
|
|
12
|
+
await client.testAuth()
|
|
13
|
+
return
|
|
14
|
+
} catch {
|
|
15
|
+
if (await refreshCookie(workspace.token, credManager)) return
|
|
16
|
+
}
|
|
17
|
+
}
|
|
9
18
|
|
|
10
19
|
try {
|
|
11
20
|
const extractor = new TokenExtractor()
|
|
@@ -32,6 +41,27 @@ export async function ensureSlackAuth(): Promise<void> {
|
|
|
32
41
|
if (code === 'EBUSY' || message.includes('locking the cookie')) {
|
|
33
42
|
throw error
|
|
34
43
|
}
|
|
35
|
-
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function refreshCookie(
|
|
48
|
+
token: string,
|
|
49
|
+
credManager: CredentialManager,
|
|
50
|
+
): Promise<{ user_id: string; team_id: string; user?: string; team?: string } | null> {
|
|
51
|
+
try {
|
|
52
|
+
const extractor = new TokenExtractor()
|
|
53
|
+
const freshCookie = await extractor.extractCookie()
|
|
54
|
+
if (!freshCookie) return null
|
|
55
|
+
|
|
56
|
+
const client = new SlackClient(token, freshCookie)
|
|
57
|
+
const authInfo = await client.testAuth()
|
|
58
|
+
|
|
59
|
+
const config = await credManager.load()
|
|
60
|
+
for (const ws of Object.values(config.workspaces)) {
|
|
61
|
+
await credManager.setWorkspace({ ...ws, cookie: freshCookie })
|
|
62
|
+
}
|
|
63
|
+
return authInfo
|
|
64
|
+
} catch {
|
|
65
|
+
return null
|
|
36
66
|
}
|
|
37
67
|
}
|