agent-messenger 2.23.6 → 2.24.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/README.md +12 -1
- package/bun.lock +10 -1
- package/dist/package.json +4 -1
- package/dist/src/platforms/slack/commands/auth.d.ts.map +1 -1
- package/dist/src/platforms/slack/commands/auth.js +56 -0
- package/dist/src/platforms/slack/commands/auth.js.map +1 -1
- package/dist/src/platforms/slack/ensure-auth.d.ts +1 -1
- package/dist/src/platforms/slack/ensure-auth.d.ts.map +1 -1
- package/dist/src/platforms/slack/ensure-auth.js +2 -2
- package/dist/src/platforms/slack/ensure-auth.js.map +1 -1
- package/dist/src/platforms/slack/index.d.ts +4 -0
- package/dist/src/platforms/slack/index.d.ts.map +1 -1
- package/dist/src/platforms/slack/index.js +2 -0
- package/dist/src/platforms/slack/index.js.map +1 -1
- package/dist/src/platforms/slack/qr-http-login.d.ts +14 -0
- package/dist/src/platforms/slack/qr-http-login.d.ts.map +1 -0
- package/dist/src/platforms/slack/qr-http-login.js +90 -0
- package/dist/src/platforms/slack/qr-http-login.js.map +1 -0
- package/dist/src/platforms/slack/qr-login.d.ts +10 -0
- package/dist/src/platforms/slack/qr-login.d.ts.map +1 -0
- package/dist/src/platforms/slack/qr-login.js +72 -0
- package/dist/src/platforms/slack/qr-login.js.map +1 -0
- package/dist/src/vendor/linejs/base/request/mod.js +1 -1
- package/dist/src/vendor/linejs/base/request/mod.test.ts +54 -0
- package/docs/content/docs/cli/slack.mdx +22 -0
- package/docs/content/docs/sdk/slack.mdx +15 -0
- package/package.json +4 -1
- 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 +1 -1
- package/skills/agent-line/SKILL.md +1 -1
- package/skills/agent-slack/SKILL.md +45 -1
- package/skills/agent-slack/references/authentication.md +29 -0
- 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-telegrambot/SKILL.md +1 -1
- package/skills/agent-webex/SKILL.md +1 -1
- package/skills/agent-webexbot/SKILL.md +1 -1
- package/skills/agent-wechatbot/SKILL.md +1 -1
- package/skills/agent-whatsapp/SKILL.md +1 -1
- package/skills/agent-whatsappbot/SKILL.md +1 -1
- package/src/platforms/slack/commands/auth.ts +73 -0
- package/src/platforms/slack/ensure-auth.ts +6 -2
- package/src/platforms/slack/index.test.ts +10 -0
- package/src/platforms/slack/index.ts +4 -0
- package/src/platforms/slack/qr-http-login.test.ts +157 -0
- package/src/platforms/slack/qr-http-login.ts +120 -0
- package/src/platforms/slack/qr-login.test.ts +103 -0
- package/src/platforms/slack/qr-login.ts +90 -0
- package/src/vendor/linejs/base/request/mod.js +1 -1
- package/src/vendor/linejs/base/request/mod.test.ts +54 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { describe, expect, it } from 'bun:test'
|
|
2
|
+
|
|
3
|
+
import { InternalError } from '../core/mod.js'
|
|
4
|
+
import { RequestClient } from './mod.js'
|
|
5
|
+
|
|
6
|
+
// Regression: a thrift error response can set hasError (empty res.data[0]) while
|
|
7
|
+
// omitting the exception struct (res.data[1] absent), leaving res.data.e undefined.
|
|
8
|
+
function createClient(readThriftResult: { data: Record<string, unknown> }) {
|
|
9
|
+
const deviceDetails = {
|
|
10
|
+
device: 'TEST',
|
|
11
|
+
appVersion: '0.0.0',
|
|
12
|
+
systemName: 'TEST',
|
|
13
|
+
systemVersion: '0.0.0',
|
|
14
|
+
}
|
|
15
|
+
const stubClient = {
|
|
16
|
+
deviceDetails,
|
|
17
|
+
endpoint: 'legy.line-apps.test',
|
|
18
|
+
authToken: 'expired-token',
|
|
19
|
+
config: { timeout: 1000 },
|
|
20
|
+
storage: { get: async () => undefined },
|
|
21
|
+
log: () => {},
|
|
22
|
+
emit: () => {},
|
|
23
|
+
fetch: async () => ({
|
|
24
|
+
headers: { get: () => null },
|
|
25
|
+
arrayBuffer: async () => new ArrayBuffer(0),
|
|
26
|
+
}),
|
|
27
|
+
thrift: {
|
|
28
|
+
writeThrift: () => new Uint8Array(),
|
|
29
|
+
readThrift: () => readThriftResult,
|
|
30
|
+
rename_data: () => {},
|
|
31
|
+
},
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return new RequestClient(stubClient as never)
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
describe('RequestClient.requestCore error handling', () => {
|
|
38
|
+
it('throws a clean RequestError when hasError is set but no exception struct is present', async () => {
|
|
39
|
+
// given: an error response with an empty success slot and no exception struct
|
|
40
|
+
const client = createClient({ data: { 0: undefined, someField: 1 } })
|
|
41
|
+
|
|
42
|
+
// when / then: the error branch must throw InternalError, not a TypeError
|
|
43
|
+
let thrown: unknown
|
|
44
|
+
try {
|
|
45
|
+
await client.requestCore('/S3', [], 'testMethod', 3)
|
|
46
|
+
} catch (error) {
|
|
47
|
+
thrown = error
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
expect(thrown).toBeInstanceOf(InternalError)
|
|
51
|
+
expect((thrown as InternalError).type).toBe('RequestError')
|
|
52
|
+
expect(thrown).not.toBeInstanceOf(TypeError)
|
|
53
|
+
})
|
|
54
|
+
})
|
|
@@ -45,6 +45,28 @@ This command:
|
|
|
45
45
|
- Stores credentials securely in `~/.config/agent-messenger/`
|
|
46
46
|
- Supports `--browser-profile <path>` for agent-browser profiles, custom Chrome user data dirs, or portable browser profiles
|
|
47
47
|
|
|
48
|
+
### QR Code Sign-In
|
|
49
|
+
|
|
50
|
+
When the desktop app isn't installed (or extraction isn't an option), sign in with a QR code from a device where you're already logged into Slack. This runs entirely over HTTP — no browser automation required:
|
|
51
|
+
|
|
52
|
+
```bash
|
|
53
|
+
# In Slack (desktop or web): your name (top-left) → "Sign in on mobile".
|
|
54
|
+
# Right-click the QR code → "Copy Image Address", then pipe it in:
|
|
55
|
+
pbpaste | agent-slack auth qr
|
|
56
|
+
|
|
57
|
+
# Or pass the data URL directly:
|
|
58
|
+
agent-slack auth qr "data:image/png;base64,iVBORw0KGgo..."
|
|
59
|
+
|
|
60
|
+
# Show each redirect hop while debugging:
|
|
61
|
+
agent-slack auth qr --debug
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
This decodes the QR's one-time `z-app-` login URL, follows Slack's server-side redirect chain to capture the session cookie, retrieves the matching client token, and stores credentials like `auth extract`.
|
|
65
|
+
|
|
66
|
+
- The QR link is **single-use** — generate a fresh one if it expires.
|
|
67
|
+
- No Chrome/Chromium or desktop app is required.
|
|
68
|
+
- Works with workspaces that allow password/email sign-in. SSO-only / Enterprise Grid workspaces that disable the mobile QR flow are not supported.
|
|
69
|
+
|
|
48
70
|
### Multi-Workspace Management
|
|
49
71
|
|
|
50
72
|
```bash
|
|
@@ -39,6 +39,21 @@ const auth = await client.testAuth()
|
|
|
39
39
|
// → { user_id: string, team_id: string, user?: string, team?: string }
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
+
### QR Code Login
|
|
43
|
+
|
|
44
|
+
`loginWithQr` turns a Slack "Sign in on mobile" QR image into a usable session (token + cookie) over plain HTTP — no browser or desktop app. `dataUrl` is the QR image as a `data:image/png;base64,...` string.
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import { loginWithQr, SlackClient } from 'agent-messenger/slack'
|
|
48
|
+
|
|
49
|
+
const session = await loginWithQr(dataUrl)
|
|
50
|
+
// → { token: string, cookie: string, workspace: string }
|
|
51
|
+
|
|
52
|
+
const client = await new SlackClient().login({ token: session.token, cookie: session.cookie })
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`loginWithQr(dataUrl, options)` accepts an optional `{ debug, fetchImpl, maxRedirects }` and throws `SlackError` with codes `qr_session_failed` (link expired / no session) or `qr_token_failed` (token unavailable). Use `decodeSlackQr(dataUrl)` to decode the QR without logging in.
|
|
56
|
+
|
|
42
57
|
### Messages
|
|
43
58
|
|
|
44
59
|
```typescript
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-messenger",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.24.0",
|
|
4
4
|
"description": "Multi-platform messaging CLI for AI agents (Slack, Discord, Teams, Webex, Telegram, Telegram Bot, WhatsApp, LINE, Instagram, KakaoTalk, Channel Talk)",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -178,10 +178,12 @@
|
|
|
178
178
|
"commander": "^11.1.0",
|
|
179
179
|
"crypto-js": "^4.2.0",
|
|
180
180
|
"curve25519-js": "^0.0.4",
|
|
181
|
+
"jsqr": "^1.4.0",
|
|
181
182
|
"node-bignumber": "^1.2.2",
|
|
182
183
|
"node-int64": "^0.4.0",
|
|
183
184
|
"node-jose": "^2.2.0",
|
|
184
185
|
"pino": "^10.3.1",
|
|
186
|
+
"pngjs": "^7.0.0",
|
|
185
187
|
"qrcode": "^1.5.4",
|
|
186
188
|
"thrift": "^0.20.0",
|
|
187
189
|
"tweetnacl": "^1.0.3",
|
|
@@ -196,6 +198,7 @@
|
|
|
196
198
|
"@types/hapi__boom": "^9.0.1",
|
|
197
199
|
"@types/node": "^20.10.6",
|
|
198
200
|
"@types/node-jose": "^1.1.13",
|
|
201
|
+
"@types/pngjs": "^6.0.5",
|
|
199
202
|
"@types/qrcode": "^1.5.6",
|
|
200
203
|
"@types/ws": "^8.18.1",
|
|
201
204
|
"oxfmt": "^0.36.0",
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-channeltalk
|
|
3
3
|
description: Interact with Channel Talk using extracted desktop app or browser credentials - read chats, send messages, search messages, manage groups
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.24.0
|
|
5
5
|
allowed-tools: Bash(agent-channeltalk:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-slack
|
|
3
3
|
description: Interact with Slack workspaces - send messages, read channels, manage reactions
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.24.0
|
|
5
5
|
allowed-tools: Bash(agent-slack:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -39,6 +39,21 @@ On macOS, the system may prompt for your Keychain password the first time (requi
|
|
|
39
39
|
|
|
40
40
|
**IMPORTANT**: Always use `agent-slack auth extract` to obtain tokens. The CLI extracts from the desktop app first, falling back to Chromium browsers if the app isn't installed.
|
|
41
41
|
|
|
42
|
+
### QR Code Sign-In (No Desktop App Required)
|
|
43
|
+
|
|
44
|
+
If the Slack desktop app isn't installed (or extraction fails), you can sign in with a QR code from any device where you're already logged into Slack — no browser automation, fully over HTTP:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
# In Slack (desktop or web): your name (top-left) → "Sign in on mobile".
|
|
48
|
+
# Right-click the QR code → "Copy Image Address", then:
|
|
49
|
+
pbpaste | agent-slack auth qr
|
|
50
|
+
|
|
51
|
+
# Or pass the data URL directly:
|
|
52
|
+
agent-slack auth qr "data:image/png;base64,iVBORw0KGgo..."
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This decodes the QR's one-time login link, establishes a session, and stores the resulting credentials like `auth extract` does. The QR link is single-use — generate a fresh one if it expires.
|
|
56
|
+
|
|
42
57
|
### Multi-Workspace Support
|
|
43
58
|
|
|
44
59
|
```bash
|
|
@@ -145,6 +160,11 @@ agent-slack auth extract --browser-profile ~/work-profile --browser-profile ~/pe
|
|
|
145
160
|
|
|
146
161
|
# --browser-profile accepts repeatable or comma-separated Chromium profile/user-data dirs
|
|
147
162
|
|
|
163
|
+
# Sign in with a QR code from Slack's "Sign in on mobile" screen (no desktop app needed)
|
|
164
|
+
agent-slack auth qr "data:image/png;base64,..."
|
|
165
|
+
pbpaste | agent-slack auth qr
|
|
166
|
+
agent-slack auth qr --debug # show each redirect hop for troubleshooting
|
|
167
|
+
|
|
148
168
|
# Check auth status
|
|
149
169
|
agent-slack auth status
|
|
150
170
|
|
|
@@ -562,6 +582,30 @@ Common errors:
|
|
|
562
582
|
|
|
563
583
|
Credentials stored in `~/.config/agent-messenger/slack-credentials.json` (0600 permissions). See [references/authentication.md](references/authentication.md) for format and security details.
|
|
564
584
|
|
|
585
|
+
## SDK: QR Code Login
|
|
586
|
+
|
|
587
|
+
`loginWithQr` turns a Slack "Sign in on mobile" QR image into a usable session (token + cookie) over plain HTTP — no browser, no desktop app.
|
|
588
|
+
|
|
589
|
+
```typescript
|
|
590
|
+
import { loginWithQr, SlackClient, SlackCredentialManager } from 'agent-messenger/slack'
|
|
591
|
+
|
|
592
|
+
// dataUrl is the QR image as a "data:image/png;base64,..." string
|
|
593
|
+
const session = await loginWithQr(dataUrl)
|
|
594
|
+
|
|
595
|
+
const client = await new SlackClient().login({ token: session.token, cookie: session.cookie })
|
|
596
|
+
const { team_id, team } = await client.testAuth()
|
|
597
|
+
|
|
598
|
+
// Persist for later, like the CLI does
|
|
599
|
+
await new SlackCredentialManager().setWorkspace({
|
|
600
|
+
workspace_id: team_id,
|
|
601
|
+
workspace_name: team ?? session.workspace,
|
|
602
|
+
token: session.token,
|
|
603
|
+
cookie: session.cookie,
|
|
604
|
+
})
|
|
605
|
+
```
|
|
606
|
+
|
|
607
|
+
`loginWithQr(dataUrl, options)` accepts an optional `{ debug, fetchImpl, maxRedirects }`. It throws `SlackError` with codes `qr_session_failed` (link expired / no session) or `qr_token_failed` (token could not be retrieved). To decode the QR without logging in, use `decodeSlackQr(dataUrl)` which returns `{ url, workspace, teamId, userId }`.
|
|
608
|
+
|
|
565
609
|
## SDK: Real-Time Events
|
|
566
610
|
|
|
567
611
|
`SlackListener` connects to Slack's RTM WebSocket for instant event streaming. No polling — events arrive in real time.
|
|
@@ -34,6 +34,35 @@ This command:
|
|
|
34
34
|
|
|
35
35
|
Use `--browser-profile <path>` for agent-browser profiles, custom Chrome user data dirs, or portable browser profiles. The option can be repeated or given comma-separated paths, and explicit paths are included even when desktop credentials are also present.
|
|
36
36
|
|
|
37
|
+
### QR Code Sign-In
|
|
38
|
+
|
|
39
|
+
When the desktop app isn't installed and browser extraction isn't an option, you can sign in with a QR code from a device where you're already logged into Slack. This runs entirely over HTTP — no browser automation:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# In Slack (desktop or web): your name (top-left) → "Sign in on mobile".
|
|
43
|
+
# Right-click the QR code → "Copy Image Address", then pipe it in:
|
|
44
|
+
pbpaste | agent-slack auth qr
|
|
45
|
+
|
|
46
|
+
# Or pass the data URL directly:
|
|
47
|
+
agent-slack auth qr "data:image/png;base64,iVBORw0KGgo..."
|
|
48
|
+
|
|
49
|
+
# Show each redirect hop while debugging:
|
|
50
|
+
agent-slack auth qr --debug
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
How it works:
|
|
54
|
+
|
|
55
|
+
1. Decodes the QR image (a `data:image/png;base64,...` string) to its one-time `z-app-` login URL
|
|
56
|
+
2. Follows Slack's server-side redirect chain, capturing the session `d` cookie (`xoxd-...`)
|
|
57
|
+
3. Retrieves the matching `xoxc-` client token from the established session
|
|
58
|
+
4. Validates against the Slack API and stores credentials like `auth extract`
|
|
59
|
+
|
|
60
|
+
Notes:
|
|
61
|
+
|
|
62
|
+
- The QR link is **single-use** — if it expires, generate a fresh one from Slack's "Sign in on mobile" screen.
|
|
63
|
+
- No Chrome/Chromium or desktop app is required; the flow uses plain HTTP requests.
|
|
64
|
+
- Works with workspaces that allow password/email sign-in. SSO-only / Enterprise Grid workspaces that disable the mobile QR flow are not supported.
|
|
65
|
+
|
|
37
66
|
### Platform-Specific Paths
|
|
38
67
|
|
|
39
68
|
**macOS (Direct Download):**
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-webexbot
|
|
3
3
|
description: Interact with Cisco Webex using bot tokens - send messages, reply in threads, upload and download files, look up people, read spaces, manage memberships, stream real-time events
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.24.0
|
|
5
5
|
allowed-tools: Bash(agent-webexbot:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -9,6 +9,7 @@ import { debug } from '@/shared/utils/stderr'
|
|
|
9
9
|
import { SlackClient, SlackError } from '../client'
|
|
10
10
|
import { CredentialManager } from '../credential-manager'
|
|
11
11
|
import { refreshCookie, tryWebTokenRefresh } from '../ensure-auth'
|
|
12
|
+
import { loginWithQr } from '../qr-http-login'
|
|
12
13
|
import { type ExtractedWorkspace, TokenExtractor } from '../token-extractor'
|
|
13
14
|
|
|
14
15
|
export function formatCredentialDebug(ws: ExtractedWorkspace, showSecrets?: boolean): string {
|
|
@@ -230,6 +231,70 @@ async function statusAction(options: { pretty?: boolean }): Promise<void> {
|
|
|
230
231
|
}
|
|
231
232
|
}
|
|
232
233
|
|
|
234
|
+
async function qrAction(imageArg: string | undefined, options: { pretty?: boolean; debug?: boolean }): Promise<void> {
|
|
235
|
+
try {
|
|
236
|
+
const dataUrl = imageArg ?? (await readStdin())
|
|
237
|
+
if (!dataUrl) {
|
|
238
|
+
console.log(
|
|
239
|
+
formatOutput(
|
|
240
|
+
{
|
|
241
|
+
error: 'No QR image provided. Pass the copied QR image data URL as an argument, or pipe it via stdin.',
|
|
242
|
+
hint: 'In Slack: your name (top-left) → "Sign in on mobile" → right-click the QR → Copy Image Address.',
|
|
243
|
+
},
|
|
244
|
+
options.pretty,
|
|
245
|
+
),
|
|
246
|
+
)
|
|
247
|
+
process.exit(1)
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const debugLog = options.debug ? (msg: string) => debug(`[debug] ${msg}`) : undefined
|
|
251
|
+
const session = await loginWithQr(dataUrl, { debug: debugLog })
|
|
252
|
+
|
|
253
|
+
const client = await new SlackClient().login({ token: session.token, cookie: session.cookie })
|
|
254
|
+
const authInfo = await client.testAuth()
|
|
255
|
+
|
|
256
|
+
const credManager = new CredentialManager()
|
|
257
|
+
const workspace: ExtractedWorkspace = {
|
|
258
|
+
workspace_id: authInfo.team_id,
|
|
259
|
+
workspace_name: authInfo.team || session.workspace,
|
|
260
|
+
token: session.token,
|
|
261
|
+
cookie: session.cookie,
|
|
262
|
+
}
|
|
263
|
+
await credManager.setWorkspace(workspace)
|
|
264
|
+
|
|
265
|
+
const config = await credManager.load()
|
|
266
|
+
if (!config.current_workspace) {
|
|
267
|
+
await credManager.setCurrentWorkspace(workspace.workspace_id)
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
console.log(
|
|
271
|
+
formatOutput(
|
|
272
|
+
{
|
|
273
|
+
workspace: `${workspace.workspace_id}/${workspace.workspace_name}`,
|
|
274
|
+
user: authInfo.user,
|
|
275
|
+
current: (await credManager.load()).current_workspace,
|
|
276
|
+
},
|
|
277
|
+
options.pretty,
|
|
278
|
+
),
|
|
279
|
+
)
|
|
280
|
+
} catch (error) {
|
|
281
|
+
handleError(error as Error)
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function readStdin(): Promise<string> {
|
|
286
|
+
if (process.stdin.isTTY) return Promise.resolve('')
|
|
287
|
+
return new Promise((resolve) => {
|
|
288
|
+
let data = ''
|
|
289
|
+
process.stdin.setEncoding('utf8')
|
|
290
|
+
process.stdin.on('data', (chunk) => {
|
|
291
|
+
data += chunk
|
|
292
|
+
})
|
|
293
|
+
process.stdin.on('end', () => resolve(data))
|
|
294
|
+
process.stdin.on('error', () => resolve(data))
|
|
295
|
+
})
|
|
296
|
+
}
|
|
297
|
+
|
|
233
298
|
export function getExtractionErrorMessage(failureReasons: string[]): string {
|
|
234
299
|
if (failureReasons.includes('missing_cookie')) {
|
|
235
300
|
return 'Cookie extraction failed. Grant Keychain access when prompted, and make sure you are signed into Slack in the desktop app or a supported Chromium browser.'
|
|
@@ -260,6 +325,14 @@ export const authCommand = new Command('auth')
|
|
|
260
325
|
.option('--unsafely-show-secrets', 'Show full token and cookie values in debug output')
|
|
261
326
|
.action((options: BrowserProfileOption & Parameters<typeof extractAction>[0]) => extractAction(options)),
|
|
262
327
|
)
|
|
328
|
+
.addCommand(
|
|
329
|
+
new Command('qr')
|
|
330
|
+
.description('Sign in by pasting a QR code from Slack\u2019s "Sign in on mobile" screen')
|
|
331
|
+
.argument('[image]', 'QR image data URL (data:image/png;base64,...); read from stdin if omitted')
|
|
332
|
+
.option('--pretty', 'Pretty print JSON output')
|
|
333
|
+
.option('--debug', 'Show debug output for troubleshooting')
|
|
334
|
+
.action(qrAction),
|
|
335
|
+
)
|
|
263
336
|
.addCommand(
|
|
264
337
|
new Command('logout')
|
|
265
338
|
.description('Logout from workspace')
|
|
@@ -145,9 +145,13 @@ async function refreshAndVerify(cookie: string, domain: string, fallbackName: st
|
|
|
145
145
|
|
|
146
146
|
const TOKEN_REGEX = /"api_token":"(xoxc-[a-zA-Z0-9-]+)"/
|
|
147
147
|
|
|
148
|
-
export async function refreshTokenFromWeb(
|
|
148
|
+
export async function refreshTokenFromWeb(
|
|
149
|
+
domain: string,
|
|
150
|
+
cookie: string,
|
|
151
|
+
fetchImpl: typeof fetch = fetch,
|
|
152
|
+
): Promise<string | null> {
|
|
149
153
|
try {
|
|
150
|
-
const response = await
|
|
154
|
+
const response = await fetchImpl(`https://${domain}.slack.com/ssb/redirect`, {
|
|
151
155
|
headers: { Cookie: `d=${cookie}` },
|
|
152
156
|
redirect: 'follow',
|
|
153
157
|
})
|
|
@@ -12,6 +12,8 @@ import {
|
|
|
12
12
|
SlackUserSchema,
|
|
13
13
|
WorkspaceCredentialsSchema,
|
|
14
14
|
ConfigSchema,
|
|
15
|
+
decodeSlackQr,
|
|
16
|
+
loginWithQr,
|
|
15
17
|
} from '@/platforms/slack/index'
|
|
16
18
|
|
|
17
19
|
it('SlackClient is exported from barrel', () => {
|
|
@@ -57,3 +59,11 @@ it('WorkspaceCredentialsSchema is exported from barrel', () => {
|
|
|
57
59
|
it('ConfigSchema is exported from barrel', () => {
|
|
58
60
|
expect(typeof ConfigSchema.parse).toBe('function')
|
|
59
61
|
})
|
|
62
|
+
|
|
63
|
+
it('loginWithQr is exported from barrel', () => {
|
|
64
|
+
expect(typeof loginWithQr).toBe('function')
|
|
65
|
+
})
|
|
66
|
+
|
|
67
|
+
it('decodeSlackQr is exported from barrel', () => {
|
|
68
|
+
expect(typeof decodeSlackQr).toBe('function')
|
|
69
|
+
})
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
export { SlackClient, SlackError } from './client'
|
|
2
2
|
export { SlackCredentialManager, CredentialManager } from './credential-manager'
|
|
3
3
|
export { SlackListener } from './listener'
|
|
4
|
+
export { loginWithQr } from './qr-http-login'
|
|
5
|
+
export type { QrLoginOptions, QrSession } from './qr-http-login'
|
|
6
|
+
export { decodeSlackQr } from './qr-login'
|
|
7
|
+
export type { SlackQrLogin } from './qr-login'
|
|
4
8
|
export type {
|
|
5
9
|
SlackBookmark,
|
|
6
10
|
SlackChannel,
|