agent-messenger 2.26.0 → 2.27.1
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 -2
- package/.claude-plugin/plugin.json +1 -1
- package/README.md +27 -0
- package/dist/package.json +1 -1
- package/dist/src/platforms/discord/listener.d.ts +9 -4
- package/dist/src/platforms/discord/listener.d.ts.map +1 -1
- package/dist/src/platforms/discord/listener.js +110 -16
- package/dist/src/platforms/discord/listener.js.map +1 -1
- package/dist/src/platforms/discordbot/listener.d.ts +6 -0
- package/dist/src/platforms/discordbot/listener.d.ts.map +1 -1
- package/dist/src/platforms/discordbot/listener.js +78 -2
- package/dist/src/platforms/discordbot/listener.js.map +1 -1
- package/dist/src/platforms/slack/qr-http-login.d.ts.map +1 -1
- package/dist/src/platforms/slack/qr-http-login.js +105 -6
- package/dist/src/platforms/slack/qr-http-login.js.map +1 -1
- package/docs/content/docs/sdk/discord.mdx +1 -9
- package/e2e/README.md +3 -1
- package/package.json +1 -1
- package/skills/agent-channeltalk/SKILL.md +1 -1
- package/skills/agent-channeltalkbot/SKILL.md +1 -1
- package/skills/agent-discord/SKILL.md +37 -8
- package/skills/agent-discord/references/authentication.md +46 -8
- 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 +7 -4
- package/skills/agent-slack/references/authentication.md +8 -3
- 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/discord/listener.test.ts +187 -147
- package/src/platforms/discord/listener.ts +122 -18
- package/src/platforms/discordbot/listener.test.ts +186 -184
- package/src/platforms/discordbot/listener.ts +83 -2
- package/src/platforms/slack/qr-http-login.test.ts +47 -0
- package/src/platforms/slack/qr-http-login.ts +112 -7
|
@@ -7,9 +7,9 @@ export async function loginWithQr(dataUrl, options = {}) {
|
|
|
7
7
|
const login = decodeSlackQr(dataUrl.trim());
|
|
8
8
|
const debug = options.debug;
|
|
9
9
|
debug?.(`Decoded QR for workspace ${login.workspace}`);
|
|
10
|
-
const cookie = await captureDCookie(login.url, options);
|
|
10
|
+
const { cookie, denialReason, ssoProvider } = await captureDCookie(login.url, options);
|
|
11
11
|
if (!cookie) {
|
|
12
|
-
throw new SlackError(
|
|
12
|
+
throw new SlackError(qrSessionFailureMessage(denialReason, { workspace: login.workspace, ssoProvider }), 'qr_session_failed');
|
|
13
13
|
}
|
|
14
14
|
debug?.('Captured session cookie');
|
|
15
15
|
const token = await refreshTokenFromWeb(login.workspace, cookie, options.fetchImpl ?? fetch);
|
|
@@ -19,12 +19,60 @@ export async function loginWithQr(dataUrl, options = {}) {
|
|
|
19
19
|
debug?.('Retrieved client token');
|
|
20
20
|
return { token, cookie, workspace: login.workspace };
|
|
21
21
|
}
|
|
22
|
+
function qrSessionFailureMessage(denialReason, context = {}) {
|
|
23
|
+
const workspace = context.workspace ? `"${context.workspace}"` : 'this workspace';
|
|
24
|
+
if (denialReason === 'sso_required') {
|
|
25
|
+
const via = context.ssoProvider ? ` (via ${context.ssoProvider})` : '';
|
|
26
|
+
return [
|
|
27
|
+
`Slack workspace ${workspace} enforces SSO${via}, so QR sign-in cannot complete here.`,
|
|
28
|
+
'',
|
|
29
|
+
`Why: scanning the QR redirects the login to your identity provider${via}. agent-slack follows the`,
|
|
30
|
+
'redirect chain over plain HTTP and never sends your session cookie off Slack-owned hosts, so it',
|
|
31
|
+
'stops at the IdP and no Slack session (the "d" cookie) is ever issued. Completing SSO requires a',
|
|
32
|
+
'real browser, which a headless environment does not have.',
|
|
33
|
+
'',
|
|
34
|
+
'What to do instead:',
|
|
35
|
+
' On a computer where you are already signed in to Slack (desktop app or a Chromium browser),',
|
|
36
|
+
' run: agent-slack auth extract',
|
|
37
|
+
' This reads your existing session locally — no SSO re-login, no DevTools.',
|
|
38
|
+
'',
|
|
39
|
+
'Background: https://github.com/agent-messenger/agent-messenger/issues/260',
|
|
40
|
+
].join('\n');
|
|
41
|
+
}
|
|
42
|
+
if (denialReason === 'link_expired') {
|
|
43
|
+
return [
|
|
44
|
+
`The Slack QR code for ${workspace} has expired or was already used.`,
|
|
45
|
+
'The z-app sign-in link is single-use and short-lived: it is consumed the first time it is opened',
|
|
46
|
+
'(including previewing or scanning it), and re-using it returns "Link Expired".',
|
|
47
|
+
'',
|
|
48
|
+
'Generate a fresh QR (your name → "Sign in on mobile") and pipe it in immediately, without',
|
|
49
|
+
'previewing or scanning it first.',
|
|
50
|
+
].join('\n');
|
|
51
|
+
}
|
|
52
|
+
if (denialReason === 'awaiting_device_confirmation') {
|
|
53
|
+
return [
|
|
54
|
+
`Slack requires this sign-in to ${workspace} to be confirmed on an already-authenticated device.`,
|
|
55
|
+
'QR sign-in over HTTP cannot complete that approval step.',
|
|
56
|
+
'',
|
|
57
|
+
'Use agent-slack auth extract on a computer already signed in to Slack instead.',
|
|
58
|
+
].join('\n');
|
|
59
|
+
}
|
|
60
|
+
return [
|
|
61
|
+
`Could not establish a Slack session for ${workspace} from the QR code.`,
|
|
62
|
+
'The link may have expired, or the workspace requires a sign-in step that QR-over-HTTP cannot complete.',
|
|
63
|
+
'',
|
|
64
|
+
'Generate a fresh QR and try again, or run agent-slack auth extract on a computer already signed in',
|
|
65
|
+
'to Slack.',
|
|
66
|
+
].join('\n');
|
|
67
|
+
}
|
|
22
68
|
async function captureDCookie(startUrl, options) {
|
|
23
69
|
const doFetch = options.fetchImpl ?? fetch;
|
|
24
70
|
const maxRedirects = options.maxRedirects ?? DEFAULT_MAX_REDIRECTS;
|
|
25
71
|
const debug = options.debug;
|
|
26
72
|
let url = startUrl;
|
|
27
73
|
let dCookie = null;
|
|
74
|
+
let sessionDenialReason = null;
|
|
75
|
+
let ssoProvider = null;
|
|
28
76
|
const cookieJar = [];
|
|
29
77
|
for (let hop = 0; hop < maxRedirects; hop++) {
|
|
30
78
|
// Only ever send captured cookies (including the d session cookie) to Slack
|
|
@@ -43,26 +91,77 @@ async function captureDCookie(startUrl, options) {
|
|
|
43
91
|
...(cookieJar.length ? { Cookie: cookieJar.join('; ') } : {}),
|
|
44
92
|
},
|
|
45
93
|
});
|
|
94
|
+
const cookieNames = [];
|
|
46
95
|
for (const setCookie of getSetCookies(response)) {
|
|
47
96
|
const value = parseDCookie(setCookie);
|
|
48
97
|
if (value)
|
|
49
98
|
dCookie = value;
|
|
99
|
+
const name = setCookie.split('=')[0]?.trim();
|
|
100
|
+
if (name)
|
|
101
|
+
cookieNames.push(name);
|
|
50
102
|
const pair = setCookie.split(';')[0]?.trim();
|
|
51
103
|
if (pair)
|
|
52
104
|
cookieJar.push(pair);
|
|
53
105
|
}
|
|
54
|
-
debug?.(`hop ${hop}: ${response.status}`);
|
|
106
|
+
debug?.(`hop ${hop}: ${response.status} set-cookie=[${cookieNames.join(',') || 'none'}]`);
|
|
55
107
|
const location = response.status >= 300 && response.status < 400 ? response.headers.get('location') : null;
|
|
56
|
-
if (!location)
|
|
108
|
+
if (!location) {
|
|
109
|
+
if (!dCookie)
|
|
110
|
+
sessionDenialReason = await classifySessionDenial(response);
|
|
57
111
|
break;
|
|
112
|
+
}
|
|
58
113
|
const next = new URL(location, url).toString();
|
|
59
114
|
if (!isSlackHost(next)) {
|
|
60
|
-
|
|
115
|
+
// A no-cookie login QR that redirects off Slack is an SSO/IdP hand-off the
|
|
116
|
+
// HTTP flow cannot complete — classify it regardless of whether the IdP
|
|
117
|
+
// host is a recognized provider (custom/vanity SAML domains included).
|
|
118
|
+
if (!dCookie) {
|
|
119
|
+
sessionDenialReason = 'sso_required';
|
|
120
|
+
ssoProvider = ssoProviderFromUrl(next);
|
|
121
|
+
}
|
|
122
|
+
debug?.(`hop ${hop}: redirect target is not a Slack host (${new URL(next).hostname}), stopping`);
|
|
61
123
|
break;
|
|
62
124
|
}
|
|
63
125
|
url = next;
|
|
64
126
|
}
|
|
65
|
-
return dCookie;
|
|
127
|
+
return { cookie: dCookie, denialReason: sessionDenialReason, ssoProvider };
|
|
128
|
+
}
|
|
129
|
+
function ssoProviderFromUrl(rawUrl) {
|
|
130
|
+
let hostname;
|
|
131
|
+
try {
|
|
132
|
+
hostname = new URL(rawUrl).hostname;
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
if (hostname === 'accounts.google.com')
|
|
138
|
+
return 'Google';
|
|
139
|
+
if (hostname.endsWith('.okta.com') || hostname.endsWith('.oktapreview.com'))
|
|
140
|
+
return 'Okta';
|
|
141
|
+
if (hostname === 'login.microsoftonline.com')
|
|
142
|
+
return 'Microsoft';
|
|
143
|
+
if (hostname.endsWith('.onelogin.com'))
|
|
144
|
+
return 'OneLogin';
|
|
145
|
+
if (hostname.endsWith('.pingidentity.com') || hostname.endsWith('.pingone.com'))
|
|
146
|
+
return 'Ping Identity';
|
|
147
|
+
if (hostname.endsWith('.auth0.com'))
|
|
148
|
+
return 'Auth0';
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
async function classifySessionDenial(response) {
|
|
152
|
+
try {
|
|
153
|
+
const body = await response.text();
|
|
154
|
+
if (/Link Expired/i.test(body) || /trouble signing you in/i.test(body)) {
|
|
155
|
+
return 'link_expired';
|
|
156
|
+
}
|
|
157
|
+
if (/sign in on your other device/i.test(body) || /open Slack/i.test(body) || /confirm/i.test(body)) {
|
|
158
|
+
return 'awaiting_device_confirmation';
|
|
159
|
+
}
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
catch {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
66
165
|
}
|
|
67
166
|
export function isSlackHost(rawUrl) {
|
|
68
167
|
try {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"qr-http-login.js","sourceRoot":"","sources":["../../../../src/platforms/slack/qr-http-login.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AACrC,OAAO,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAA;AAc1C,MAAM,kBAAkB,GACtB,uHAAuH,CAAA;AACzH,MAAM,qBAAqB,GAAG,EAAE,CAAA;AAEhC,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,OAAe,EAAE,UAA0B,EAAE;IAC7E,MAAM,KAAK,GAAG,aAAa,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAA;IAC3C,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAA;IAC3B,KAAK,EAAE,CAAC,4BAA4B,KAAK,CAAC,SAAS,EAAE,CAAC,CAAA;IAEtD,MAAM,MAAM,GAAG,MAAM,cAAc,CAAC,KAAK,CAAC,GAAG,EAAE,OAAO,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"qr-http-login.js","sourceRoot":"","sources":["../../../../src/platforms/slack/qr-http-login.ts"],"names":[],"mappings":"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"}
|
|
@@ -257,15 +257,7 @@ await listener.start() // connects via Gateway WebSocket
|
|
|
257
257
|
// listener.stop() // clean shutdown
|
|
258
258
|
```
|
|
259
259
|
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
```typescript
|
|
263
|
-
import { DiscordIntent } from 'agent-messenger/discord'
|
|
264
|
-
|
|
265
|
-
const listener = new DiscordListener(client, {
|
|
266
|
-
intents: DiscordIntent.Guilds | DiscordIntent.GuildMessages | DiscordIntent.MessageContent, // MessageContent is privileged
|
|
267
|
-
})
|
|
268
|
-
```
|
|
260
|
+
The user-account listener does not take intents — Gateway intents are a bot concept. It identifies as a user session and automatically receives full message content (including other users' messages), so no configuration is required.
|
|
269
261
|
|
|
270
262
|
### Available Events
|
|
271
263
|
|
package/e2e/README.md
CHANGED
|
@@ -41,7 +41,7 @@ Before running E2E tests, you need:
|
|
|
41
41
|
|
|
42
42
|
## Running E2E Tests Locally
|
|
43
43
|
|
|
44
|
-
### Step 1:
|
|
44
|
+
### Step 1: Authenticate
|
|
45
45
|
|
|
46
46
|
First, make sure the desktop apps (Slack/Discord) are running and logged into your **test** workspaces.
|
|
47
47
|
|
|
@@ -56,6 +56,8 @@ agent-discord auth extract
|
|
|
56
56
|
agent-teams auth extract
|
|
57
57
|
```
|
|
58
58
|
|
|
59
|
+
> For local runs you can also use QR code sign-in for the **test** accounts (`agent-slack auth qr` / `agent-discord auth qr`) instead of extraction — it's the recommended auth method and doesn't require the desktop app. CI uses the environment variables below (extracted token/cookie values), since QR sign-in is interactive.
|
|
60
|
+
|
|
59
61
|
### Step 2: Switch to Test Workspace
|
|
60
62
|
|
|
61
63
|
Ensure you're targeting the correct test workspace:
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "agent-messenger",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.27.1",
|
|
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",
|
|
@@ -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.27.1
|
|
5
5
|
allowed-tools: Bash(agent-channeltalk:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agent-discord
|
|
3
3
|
description: Interact with Discord servers - send messages, read channels, manage reactions
|
|
4
|
-
version: 2.
|
|
4
|
+
version: 2.27.1
|
|
5
5
|
allowed-tools: Bash(agent-discord:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -39,8 +39,20 @@ On macOS, the system may prompt for your Keychain password the first time (requi
|
|
|
39
39
|
|
|
40
40
|
**Obtaining tokens** — two options:
|
|
41
41
|
|
|
42
|
-
- **`agent-discord auth
|
|
43
|
-
- **`agent-discord auth
|
|
42
|
+
- **`agent-discord auth qr` — recommended**: signs in by scanning a QR code with the Discord mobile app (Settings → Scan QR Code). This is the safest and most reliable method — it authenticates through Discord's official Remote Auth flow instead of reading credentials off disk, and needs no desktop app or browser. Requires a phone to scan, so it cannot run headlessly.
|
|
43
|
+
- **`agent-discord auth extract`**: extracts from the Discord desktop app first, falling back to Chromium browsers if the app isn't installed. Best for automated/headless use where no phone is available to scan.
|
|
44
|
+
|
|
45
|
+
### QR Code Sign-In (Recommended)
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# Generate a QR code and wait for you to scan it with the Discord mobile app
|
|
49
|
+
agent-discord auth qr
|
|
50
|
+
|
|
51
|
+
# Show protocol details while debugging
|
|
52
|
+
agent-discord auth qr --debug
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Open the Discord mobile app → **Settings → Scan QR Code**, scan the printed code, and confirm on your phone. The token (plus all discovered servers) is validated and stored like `auth extract`. The QR code expires after ~150 seconds — re-run the command to generate a fresh one. See [references/authentication.md](references/authentication.md) for the full flow.
|
|
44
56
|
|
|
45
57
|
### Multi-Server Support
|
|
46
58
|
|
|
@@ -418,7 +430,7 @@ All commands return consistent error format:
|
|
|
418
430
|
|
|
419
431
|
Common errors:
|
|
420
432
|
|
|
421
|
-
- `Not authenticated`: No valid token (auto-extraction failed — see Troubleshooting)
|
|
433
|
+
- `Not authenticated`: No valid token (auto-extraction failed — run `auth qr` to sign in, or see Troubleshooting)
|
|
422
434
|
- `No current server set`: Run `server switch <id>` first
|
|
423
435
|
- `Message not found`: Invalid message ID
|
|
424
436
|
- `Unknown Channel`: Invalid channel ID
|
|
@@ -452,6 +464,25 @@ if (!token) {
|
|
|
452
464
|
const client = await new DiscordClient().login({ token })
|
|
453
465
|
```
|
|
454
466
|
|
|
467
|
+
### QR Code Login (Recommended)
|
|
468
|
+
|
|
469
|
+
`loginWithRemoteAuth` signs in by scanning a QR code with the Discord mobile app — no desktop app or token extraction required. It runs Discord's Remote Auth protocol and hands you a QR URL to display; once the user scans and confirms on their phone, you receive the user token.
|
|
470
|
+
|
|
471
|
+
```typescript
|
|
472
|
+
import { DiscordClient, loginWithRemoteAuth } from 'agent-messenger/discord'
|
|
473
|
+
|
|
474
|
+
const session = await loginWithRemoteAuth({
|
|
475
|
+
onQrUrl: (url) => {
|
|
476
|
+
// Render this URL as a QR code (e.g. with the `qrcode` package)
|
|
477
|
+
console.log('Scan this with the Discord mobile app:', url)
|
|
478
|
+
},
|
|
479
|
+
})
|
|
480
|
+
|
|
481
|
+
const client = await new DiscordClient().login({ token: session.token })
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
`session` is `{ token, user }`, where `user` may be `null` if the gateway skips the user payload — call `await client.testAuth()` after login if you need the identity. If Discord requires a captcha on the final token exchange, the call rejects with a `DiscordError` of code `remote_auth_captcha`; fall back to `auth extract` in that case. Requires a phone with the Discord app, so it cannot run headlessly.
|
|
485
|
+
|
|
455
486
|
### Example
|
|
456
487
|
|
|
457
488
|
```typescript
|
|
@@ -478,12 +509,10 @@ await client.sendMessage(thread.id, 'First message in thread')
|
|
|
478
509
|
`DiscordListener` connects to Discord's Gateway WebSocket for instant event streaming:
|
|
479
510
|
|
|
480
511
|
```typescript
|
|
481
|
-
import { DiscordClient, DiscordListener
|
|
512
|
+
import { DiscordClient, DiscordListener } from 'agent-messenger/discord'
|
|
482
513
|
|
|
483
514
|
const client = await new DiscordClient().login()
|
|
484
|
-
const listener = new DiscordListener(client
|
|
485
|
-
intents: DiscordIntent.Guilds | DiscordIntent.GuildMessages | DiscordIntent.MessageContent,
|
|
486
|
-
})
|
|
515
|
+
const listener = new DiscordListener(client)
|
|
487
516
|
|
|
488
517
|
listener.on('message_create', (event) => {
|
|
489
518
|
console.log(`${event.author.username}: ${event.content}`)
|
|
@@ -2,7 +2,40 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
agent-discord
|
|
5
|
+
agent-discord supports two ways to authenticate:
|
|
6
|
+
|
|
7
|
+
- **QR code sign-in (`auth qr`) — recommended.** Scan a QR code with the Discord mobile app to sign in through Discord's own Remote Auth flow. This is the safest and most reliable method: it never reads stored credentials off disk, works without a desktop app or browser, and uses Discord's official login confirmation on your phone.
|
|
8
|
+
- **Token extraction (`auth extract`).** Reads Discord's user token from the desktop application, with automatic fallback to Chromium browser profiles (Chrome, Chrome Canary, Edge, Arc, Brave, Vivaldi, Chromium). Best for automated/headless environments where no phone is available to scan.
|
|
9
|
+
|
|
10
|
+
> **Recommendation:** Prefer `agent-discord auth qr` whenever you can scan a QR code. Use `auth extract` for headless/CI setups where interactive scanning isn't possible.
|
|
11
|
+
|
|
12
|
+
## QR Code Sign-In (Recommended)
|
|
13
|
+
|
|
14
|
+
Sign in by scanning a QR code with the Discord mobile app — no desktop app or browser token extraction required. This runs Discord's official Remote Auth protocol, so you authenticate through Discord's own login confirmation rather than by reading stored credentials.
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Generate a QR code and wait for you to scan it
|
|
18
|
+
agent-discord auth qr
|
|
19
|
+
|
|
20
|
+
# Show protocol details while debugging
|
|
21
|
+
agent-discord auth qr --debug
|
|
22
|
+
|
|
23
|
+
# Human-readable output
|
|
24
|
+
agent-discord auth qr --pretty
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
How it works:
|
|
28
|
+
|
|
29
|
+
1. Opens a WebSocket to Discord's remote-auth gateway and performs an RSA key exchange
|
|
30
|
+
2. Renders a QR code in your terminal (and opens it in the browser as a fallback)
|
|
31
|
+
3. You scan it with the Discord mobile app: **Settings → Scan QR Code**, then confirm on your phone
|
|
32
|
+
4. Discord returns an encrypted ticket, which is exchanged for your user token, validated, and stored like `auth extract` — along with all discovered servers
|
|
33
|
+
|
|
34
|
+
Notes:
|
|
35
|
+
|
|
36
|
+
- **Requires the Discord mobile app** (logged in) to scan. Because it's interactive, it cannot run headlessly — use `auth extract` for CI.
|
|
37
|
+
- The QR code expires after about 150 seconds. Re-run `agent-discord auth qr` to generate a fresh one.
|
|
38
|
+
- If Discord presents a captcha challenge during the token exchange, the CLI surfaces a typed error directing you to fall back to `auth extract`.
|
|
6
39
|
|
|
7
40
|
## Token Extraction
|
|
8
41
|
|
|
@@ -186,7 +219,10 @@ Discord user tokens can be invalidated when:
|
|
|
186
219
|
If commands start failing with auth errors:
|
|
187
220
|
|
|
188
221
|
```bash
|
|
189
|
-
#
|
|
222
|
+
# Recommended: re-authenticate with a QR code
|
|
223
|
+
agent-discord auth qr
|
|
224
|
+
|
|
225
|
+
# Or re-extract credentials from the desktop app / browser
|
|
190
226
|
agent-discord auth extract
|
|
191
227
|
|
|
192
228
|
# Verify it worked
|
|
@@ -216,8 +252,9 @@ This shows:
|
|
|
216
252
|
|
|
217
253
|
**Solution**:
|
|
218
254
|
|
|
219
|
-
1.
|
|
220
|
-
2. Or
|
|
255
|
+
1. Recommended: run `agent-discord auth qr` and scan the QR code with the Discord mobile app — no desktop app or browser required
|
|
256
|
+
2. Or log in to discord.com in a Chromium browser (Chrome, Edge, Arc, Brave) — the CLI will extract from browser automatically
|
|
257
|
+
3. Or install the Discord desktop app, log in, and run `agent-discord auth extract` again
|
|
221
258
|
|
|
222
259
|
### "No Discord token found"
|
|
223
260
|
|
|
@@ -275,10 +312,11 @@ With extracted credentials, agent-discord has the same permissions as you in Dis
|
|
|
275
312
|
|
|
276
313
|
### Best Practices
|
|
277
314
|
|
|
278
|
-
1. **
|
|
279
|
-
2. **
|
|
280
|
-
3. **
|
|
281
|
-
4. **
|
|
315
|
+
1. **Prefer QR sign-in**: Use `auth qr` when possible — it authenticates through Discord's official flow instead of reading credentials off disk
|
|
316
|
+
2. **Protect credentials.json**: Never commit to version control
|
|
317
|
+
3. **Use server switching**: Keep different contexts separate
|
|
318
|
+
4. **Re-authenticate periodically**: Keep tokens fresh
|
|
319
|
+
5. **Revoke if compromised**: Change your Discord password to invalidate tokens
|
|
282
320
|
|
|
283
321
|
## Manual Token Management (Advanced)
|
|
284
322
|
|
|
@@ -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.27.1
|
|
5
5
|
allowed-tools: Bash(agent-slack:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|
|
@@ -37,11 +37,14 @@ Credentials are extracted automatically from the Slack desktop app (or Chromium
|
|
|
37
37
|
|
|
38
38
|
On macOS, the system may prompt for your Keychain password the first time (required to decrypt Slack's stored token). This is a one-time prompt.
|
|
39
39
|
|
|
40
|
-
**
|
|
40
|
+
**Two ways to obtain tokens:**
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
- **`agent-slack auth qr` — recommended.** Sign in with a QR code from Slack's "Sign in on mobile" screen. It's the safest and most reliable method: it authenticates through Slack's own login flow over plain HTTP instead of reading credentials off disk, and needs no desktop app or browser.
|
|
43
|
+
- **`agent-slack auth extract`.** Extracts tokens from the Slack desktop app first, falling back to Chromium browsers if the app isn't installed. Best for automated/headless environments.
|
|
43
44
|
|
|
44
|
-
|
|
45
|
+
### QR Code Sign-In (Recommended)
|
|
46
|
+
|
|
47
|
+
Sign in with a QR code from any device where you're already logged into Slack — no desktop app, no browser automation, fully over HTTP:
|
|
45
48
|
|
|
46
49
|
```bash
|
|
47
50
|
# In Slack (desktop or web): your name (top-left) → "Sign in on mobile".
|
|
@@ -2,7 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
## Overview
|
|
4
4
|
|
|
5
|
-
agent-slack
|
|
5
|
+
agent-slack supports two ways to authenticate:
|
|
6
|
+
|
|
7
|
+
- **QR code sign-in (`auth qr`) — recommended.** Sign in with a QR code from Slack's "Sign in on mobile" screen. This is the safest and most reliable method: it authenticates through Slack's own login flow over plain HTTP instead of reading stored credentials off disk, and works without a desktop app or browser.
|
|
8
|
+
- **Token extraction (`auth extract`).** Reads Slack's web client credentials (xoxc token + xoxd cookie) from the desktop application, with automatic fallback to Chromium browser profiles (Chrome, Chrome Canary, Edge, Arc, Brave, Vivaldi, Chromium). Best for automated/headless environments.
|
|
9
|
+
|
|
10
|
+
> **Recommendation:** Prefer `agent-slack auth qr`. Use `auth extract` for headless/CI setups or when you'd rather reuse an existing desktop/browser session.
|
|
6
11
|
|
|
7
12
|
## Token Extraction
|
|
8
13
|
|
|
@@ -34,9 +39,9 @@ This command:
|
|
|
34
39
|
|
|
35
40
|
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
41
|
|
|
37
|
-
### QR Code Sign-In
|
|
42
|
+
### QR Code Sign-In (Recommended)
|
|
38
43
|
|
|
39
|
-
|
|
44
|
+
The recommended way to authenticate: sign in with a QR code from a device where you're already logged into Slack. This runs entirely over HTTP — no browser automation, and no desktop app required:
|
|
40
45
|
|
|
41
46
|
```bash
|
|
42
47
|
# In Slack (desktop or web): your name (top-left) → "Sign in on mobile".
|
|
@@ -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.27.1
|
|
5
5
|
allowed-tools: Bash(agent-webexbot:*)
|
|
6
6
|
metadata:
|
|
7
7
|
openclaw:
|