agent-messenger 2.1.0 → 2.2.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.
Files changed (207) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/.env.template +35 -17
  3. package/README.md +7 -7
  4. package/bun.lock +6 -6
  5. package/dist/package.json +2 -2
  6. package/dist/src/platforms/channeltalk/commands/auth.d.ts.map +1 -1
  7. package/dist/src/platforms/channeltalk/commands/auth.js +35 -28
  8. package/dist/src/platforms/channeltalk/commands/auth.js.map +1 -1
  9. package/dist/src/platforms/channeltalk/ensure-auth.js +6 -6
  10. package/dist/src/platforms/channeltalk/ensure-auth.js.map +1 -1
  11. package/dist/src/platforms/channeltalk/token-extractor.d.ts +23 -1
  12. package/dist/src/platforms/channeltalk/token-extractor.d.ts.map +1 -1
  13. package/dist/src/platforms/channeltalk/token-extractor.js +299 -29
  14. package/dist/src/platforms/channeltalk/token-extractor.js.map +1 -1
  15. package/dist/src/platforms/discord/commands/auth.d.ts.map +1 -1
  16. package/dist/src/platforms/discord/commands/auth.js +57 -49
  17. package/dist/src/platforms/discord/commands/auth.js.map +1 -1
  18. package/dist/src/platforms/discord/ensure-auth.js +3 -3
  19. package/dist/src/platforms/discord/ensure-auth.js.map +1 -1
  20. package/dist/src/platforms/discord/token-extractor.d.ts +6 -1
  21. package/dist/src/platforms/discord/token-extractor.d.ts.map +1 -1
  22. package/dist/src/platforms/discord/token-extractor.js +167 -14
  23. package/dist/src/platforms/discord/token-extractor.js.map +1 -1
  24. package/dist/src/platforms/instagram/client.d.ts +2 -0
  25. package/dist/src/platforms/instagram/client.d.ts.map +1 -1
  26. package/dist/src/platforms/instagram/client.js +2 -2
  27. package/dist/src/platforms/instagram/client.js.map +1 -1
  28. package/dist/src/platforms/instagram/commands/auth.d.ts.map +1 -1
  29. package/dist/src/platforms/instagram/commands/auth.js +107 -14
  30. package/dist/src/platforms/instagram/commands/auth.js.map +1 -1
  31. package/dist/src/platforms/instagram/ensure-auth.d.ts.map +1 -1
  32. package/dist/src/platforms/instagram/ensure-auth.js +57 -11
  33. package/dist/src/platforms/instagram/ensure-auth.js.map +1 -1
  34. package/dist/src/platforms/instagram/index.d.ts +1 -0
  35. package/dist/src/platforms/instagram/index.d.ts.map +1 -1
  36. package/dist/src/platforms/instagram/index.js +1 -0
  37. package/dist/src/platforms/instagram/index.js.map +1 -1
  38. package/dist/src/platforms/instagram/token-extractor.d.ts +44 -0
  39. package/dist/src/platforms/instagram/token-extractor.d.ts.map +1 -0
  40. package/dist/src/platforms/instagram/token-extractor.js +407 -0
  41. package/dist/src/platforms/instagram/token-extractor.js.map +1 -0
  42. package/dist/src/platforms/kakaotalk/client.d.ts.map +1 -1
  43. package/dist/src/platforms/kakaotalk/client.js +2 -1
  44. package/dist/src/platforms/kakaotalk/client.js.map +1 -1
  45. package/dist/src/platforms/kakaotalk/commands/auth.d.ts.map +1 -1
  46. package/dist/src/platforms/kakaotalk/commands/auth.js +14 -13
  47. package/dist/src/platforms/kakaotalk/commands/auth.js.map +1 -1
  48. package/dist/src/platforms/kakaotalk/protocol/connection.d.ts.map +1 -1
  49. package/dist/src/platforms/kakaotalk/protocol/connection.js +2 -1
  50. package/dist/src/platforms/kakaotalk/protocol/connection.js.map +1 -1
  51. package/dist/src/platforms/line/commands/auth.d.ts.map +1 -1
  52. package/dist/src/platforms/line/commands/auth.js +6 -5
  53. package/dist/src/platforms/line/commands/auth.js.map +1 -1
  54. package/dist/src/platforms/slack/commands/auth.d.ts.map +1 -1
  55. package/dist/src/platforms/slack/commands/auth.js +11 -10
  56. package/dist/src/platforms/slack/commands/auth.js.map +1 -1
  57. package/dist/src/platforms/slack/token-extractor.d.ts +9 -0
  58. package/dist/src/platforms/slack/token-extractor.d.ts.map +1 -1
  59. package/dist/src/platforms/slack/token-extractor.js +300 -23
  60. package/dist/src/platforms/slack/token-extractor.js.map +1 -1
  61. package/dist/src/platforms/teams/commands/auth.d.ts.map +1 -1
  62. package/dist/src/platforms/teams/commands/auth.js +9 -8
  63. package/dist/src/platforms/teams/commands/auth.js.map +1 -1
  64. package/dist/src/platforms/teams/ensure-auth.d.ts.map +1 -1
  65. package/dist/src/platforms/teams/ensure-auth.js +2 -1
  66. package/dist/src/platforms/teams/ensure-auth.js.map +1 -1
  67. package/dist/src/platforms/teams/token-extractor.d.ts +5 -0
  68. package/dist/src/platforms/teams/token-extractor.d.ts.map +1 -1
  69. package/dist/src/platforms/teams/token-extractor.js +161 -29
  70. package/dist/src/platforms/teams/token-extractor.js.map +1 -1
  71. package/dist/src/platforms/telegram/client.d.ts.map +1 -1
  72. package/dist/src/platforms/telegram/client.js +25 -7
  73. package/dist/src/platforms/telegram/client.js.map +1 -1
  74. package/dist/src/platforms/telegram/commands/auth.d.ts.map +1 -1
  75. package/dist/src/platforms/telegram/commands/auth.js +6 -5
  76. package/dist/src/platforms/telegram/commands/auth.js.map +1 -1
  77. package/dist/src/platforms/webex/client.d.ts +10 -0
  78. package/dist/src/platforms/webex/client.d.ts.map +1 -1
  79. package/dist/src/platforms/webex/client.js +124 -0
  80. package/dist/src/platforms/webex/client.js.map +1 -1
  81. package/dist/src/platforms/webex/commands/auth.d.ts +4 -0
  82. package/dist/src/platforms/webex/commands/auth.d.ts.map +1 -1
  83. package/dist/src/platforms/webex/commands/auth.js +46 -4
  84. package/dist/src/platforms/webex/commands/auth.js.map +1 -1
  85. package/dist/src/platforms/webex/credential-manager.js +1 -1
  86. package/dist/src/platforms/webex/credential-manager.js.map +1 -1
  87. package/dist/src/platforms/webex/ensure-auth.d.ts.map +1 -1
  88. package/dist/src/platforms/webex/ensure-auth.js +21 -5
  89. package/dist/src/platforms/webex/ensure-auth.js.map +1 -1
  90. package/dist/src/platforms/webex/index.d.ts +2 -0
  91. package/dist/src/platforms/webex/index.d.ts.map +1 -1
  92. package/dist/src/platforms/webex/index.js +1 -0
  93. package/dist/src/platforms/webex/index.js.map +1 -1
  94. package/dist/src/platforms/webex/token-extractor.d.ts +28 -0
  95. package/dist/src/platforms/webex/token-extractor.d.ts.map +1 -0
  96. package/dist/src/platforms/webex/token-extractor.js +344 -0
  97. package/dist/src/platforms/webex/token-extractor.js.map +1 -0
  98. package/dist/src/platforms/webex/types.d.ts +4 -1
  99. package/dist/src/platforms/webex/types.d.ts.map +1 -1
  100. package/dist/src/platforms/webex/types.js +2 -1
  101. package/dist/src/platforms/webex/types.js.map +1 -1
  102. package/dist/src/platforms/whatsapp/client.d.ts.map +1 -1
  103. package/dist/src/platforms/whatsapp/client.js +6 -2
  104. package/dist/src/platforms/whatsapp/client.js.map +1 -1
  105. package/dist/src/shared/utils/derived-key-cache.d.ts +1 -1
  106. package/dist/src/shared/utils/derived-key-cache.d.ts.map +1 -1
  107. package/dist/src/shared/utils/error-handler.d.ts +1 -1
  108. package/dist/src/shared/utils/error-handler.d.ts.map +1 -1
  109. package/dist/src/shared/utils/error-handler.js +3 -2
  110. package/dist/src/shared/utils/error-handler.js.map +1 -1
  111. package/dist/src/shared/utils/stderr.d.ts +5 -0
  112. package/dist/src/shared/utils/stderr.d.ts.map +1 -0
  113. package/dist/src/shared/utils/stderr.js +18 -0
  114. package/dist/src/shared/utils/stderr.js.map +1 -0
  115. package/docs/content/docs/cli/channeltalk.mdx +7 -7
  116. package/docs/content/docs/cli/discord.mdx +3 -3
  117. package/docs/content/docs/cli/instagram.mdx +28 -6
  118. package/docs/content/docs/cli/slack.mdx +2 -2
  119. package/docs/content/docs/cli/teams.mdx +6 -4
  120. package/docs/content/docs/cli/webex.mdx +30 -11
  121. package/e2e/README.md +132 -8
  122. package/e2e/channeltalk.e2e.test.ts +2 -7
  123. package/e2e/channeltalkbot.e2e.test.ts +2 -6
  124. package/e2e/config.ts +172 -10
  125. package/e2e/helpers.ts +7 -0
  126. package/e2e/instagram.e2e.test.ts +97 -0
  127. package/e2e/kakaotalk.e2e.test.ts +74 -0
  128. package/e2e/line.e2e.test.ts +92 -0
  129. package/e2e/teams.e2e.test.ts +46 -1
  130. package/e2e/telegram.e2e.test.ts +84 -0
  131. package/e2e/webex.e2e.test.ts +190 -0
  132. package/e2e/whatsapp.e2e.test.ts +90 -0
  133. package/e2e/whatsappbot.e2e.test.ts +78 -0
  134. package/package.json +2 -2
  135. package/skills/agent-channeltalk/SKILL.md +9 -9
  136. package/skills/agent-channeltalk/references/authentication.md +21 -18
  137. package/skills/agent-channeltalkbot/SKILL.md +1 -1
  138. package/skills/agent-discord/SKILL.md +5 -5
  139. package/skills/agent-discord/references/authentication.md +8 -8
  140. package/skills/agent-discordbot/SKILL.md +1 -1
  141. package/skills/agent-instagram/SKILL.md +51 -9
  142. package/skills/agent-instagram/references/authentication.md +35 -3
  143. package/skills/agent-kakaotalk/SKILL.md +1 -1
  144. package/skills/agent-line/SKILL.md +1 -1
  145. package/skills/agent-slack/SKILL.md +5 -5
  146. package/skills/agent-slack/references/authentication.md +8 -8
  147. package/skills/agent-slackbot/SKILL.md +1 -1
  148. package/skills/agent-teams/SKILL.md +6 -6
  149. package/skills/agent-teams/references/authentication.md +8 -8
  150. package/skills/agent-telegram/SKILL.md +1 -1
  151. package/skills/agent-webex/SKILL.md +35 -15
  152. package/skills/agent-webex/references/authentication.md +62 -9
  153. package/skills/agent-webex/references/common-patterns.md +6 -3
  154. package/skills/agent-whatsapp/SKILL.md +1 -1
  155. package/skills/agent-whatsappbot/SKILL.md +1 -1
  156. package/src/platforms/channeltalk/commands/auth.test.ts +5 -5
  157. package/src/platforms/channeltalk/commands/auth.ts +38 -32
  158. package/src/platforms/channeltalk/ensure-auth.test.ts +6 -6
  159. package/src/platforms/channeltalk/ensure-auth.ts +6 -6
  160. package/src/platforms/channeltalk/token-extractor.test.ts +182 -15
  161. package/src/platforms/channeltalk/token-extractor.ts +344 -30
  162. package/src/platforms/discord/commands/auth.test.ts +3 -3
  163. package/src/platforms/discord/commands/auth.ts +58 -54
  164. package/src/platforms/discord/ensure-auth.test.ts +3 -3
  165. package/src/platforms/discord/ensure-auth.ts +3 -3
  166. package/src/platforms/discord/token-extractor.test.ts +199 -27
  167. package/src/platforms/discord/token-extractor.ts +190 -17
  168. package/src/platforms/instagram/client.ts +2 -2
  169. package/src/platforms/instagram/commands/auth.ts +133 -14
  170. package/src/platforms/instagram/ensure-auth.ts +63 -12
  171. package/src/platforms/instagram/index.ts +1 -0
  172. package/src/platforms/instagram/token-extractor.test.ts +424 -0
  173. package/src/platforms/instagram/token-extractor.ts +478 -0
  174. package/src/platforms/kakaotalk/client.ts +3 -1
  175. package/src/platforms/kakaotalk/commands/auth.ts +14 -13
  176. package/src/platforms/kakaotalk/protocol/connection.ts +3 -1
  177. package/src/platforms/line/commands/auth.ts +7 -6
  178. package/src/platforms/slack/cli.test.ts +6 -5
  179. package/src/platforms/slack/commands/auth.test.ts +11 -7
  180. package/src/platforms/slack/commands/auth.ts +11 -10
  181. package/src/platforms/slack/token-extractor.test.ts +98 -1
  182. package/src/platforms/slack/token-extractor.ts +338 -26
  183. package/src/platforms/teams/commands/auth.ts +9 -8
  184. package/src/platforms/teams/ensure-auth.ts +3 -1
  185. package/src/platforms/teams/token-extractor.test.ts +136 -17
  186. package/src/platforms/teams/token-extractor.ts +182 -31
  187. package/src/platforms/telegram/client.test.ts +134 -0
  188. package/src/platforms/telegram/client.ts +27 -6
  189. package/src/platforms/telegram/commands/auth.ts +6 -5
  190. package/src/platforms/webex/client.test.ts +314 -0
  191. package/src/platforms/webex/client.ts +158 -0
  192. package/src/platforms/webex/commands/auth.ts +67 -4
  193. package/src/platforms/webex/commands/member.test.ts +10 -1
  194. package/src/platforms/webex/commands/message.test.ts +9 -5
  195. package/src/platforms/webex/commands/snapshot.test.ts +13 -4
  196. package/src/platforms/webex/commands/space.test.ts +12 -2
  197. package/src/platforms/webex/credential-manager.ts +1 -1
  198. package/src/platforms/webex/ensure-auth.test.ts +4 -0
  199. package/src/platforms/webex/ensure-auth.ts +23 -4
  200. package/src/platforms/webex/index.ts +2 -0
  201. package/src/platforms/webex/token-extractor.test.ts +327 -0
  202. package/src/platforms/webex/token-extractor.ts +393 -0
  203. package/src/platforms/webex/types.ts +4 -2
  204. package/src/platforms/whatsapp/client.ts +11 -7
  205. package/src/shared/utils/derived-key-cache.ts +1 -1
  206. package/src/shared/utils/error-handler.ts +4 -2
  207. package/src/shared/utils/stderr.ts +22 -0
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-webex
3
3
  description: Interact with Cisco Webex - send messages, read spaces, manage memberships
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  allowed-tools: Bash(agent-webex:*)
6
6
  metadata:
7
7
  openclaw:
@@ -16,12 +16,15 @@ metadata:
16
16
 
17
17
  # Agent Webex
18
18
 
19
- A TypeScript CLI tool that enables AI agents and humans to interact with Cisco Webex through a simple command interface. Uses OAuth Device Grant flow, zero configuration required.
19
+ A TypeScript CLI tool that enables AI agents and humans to interact with Cisco Webex through a simple command interface. Supports browser token extraction (zero-config, sends as you) and OAuth Device Grant flow.
20
20
 
21
21
  ## Quick Start
22
22
 
23
23
  ```bash
24
- # Log in (opens browser automatically)
24
+ # Extract token from browser (Chrome, Edge, Arc, Brave) — messages appear as you
25
+ agent-webex auth extract
26
+
27
+ # Or: Log in via OAuth Device Grant (opens browser, messages show "via agent-messenger")
25
28
  agent-webex auth login
26
29
 
27
30
  # Get workspace snapshot
@@ -36,10 +39,35 @@ agent-webex space list
36
39
 
37
40
  ## Authentication
38
41
 
39
- Webex uses OAuth Device Grant flow with built-in Integration credentials. No tokens to copy, no developer portal setup needed.
42
+ Webex supports two authentication methods:
43
+
44
+ 1. **Browser token extraction** (recommended): Extracts your first-party token from a Chromium browser where you're logged into web.webex.com. Messages appear as you — no "via" label.
45
+ 2. **OAuth Device Grant**: Opens a browser for you to authorize. Messages show "via agent-messenger" label.
46
+
47
+ ### Browser Token Extraction (Recommended)
48
+
49
+ `agent-webex auth extract` reads your Webex session token from Chrome, Edge, Arc, or Brave. You must be logged into web.webex.com in one of these browsers. No configuration needed.
50
+
51
+ ```bash
52
+ # Extract token from browser — messages appear as you
53
+ agent-webex auth extract
54
+
55
+ # With debug output
56
+ agent-webex auth extract --debug
57
+ ```
58
+
59
+ **Supported browsers**: Chrome, Chrome Canary, Edge, Arc, Brave, Vivaldi, Chromium
60
+
61
+ **How it works**: The Webex web client stores its authentication token in the browser's localStorage. This CLI reads it directly from the browser's LevelDB files — no browser automation, no password prompts. The token is stored locally in `~/.config/agent-messenger/`.
62
+
63
+ **When to re-extract**: Browser tokens expire. When your token expires, re-run `agent-webex auth extract` or let auto-extraction handle it (the CLI attempts extraction automatically on each run).
64
+
65
+ ### OAuth Device Grant (Fallback)
40
66
 
41
67
  `agent-webex auth login` starts the Device Grant flow: it displays a verification URL and user code, then opens the browser. You enter the code at the verification page and approve access. The CLI polls for the token automatically. Access and refresh tokens are stored locally, and the access token auto-refreshes via the refresh token.
42
68
 
69
+ Note: Messages sent via OAuth Device Grant show "via agent-messenger" because the token is associated with a third-party Webex Integration.
70
+
43
71
  Optionally, pass `--token <bot-token>` for bot token auth. Or pass `--client-id <id> --client-secret <secret>` to use your own Webex Integration credentials instead of the built-in ones.
44
72
 
45
73
  Env vars `AGENT_WEBEX_CLIENT_ID` / `AGENT_WEBEX_CLIENT_SECRET` can also override the built-in credentials.
@@ -61,22 +89,14 @@ agent-webex auth status
61
89
  agent-webex auth logout
62
90
  ```
63
91
 
64
- ### How Login Works
65
-
66
- 1. Run `agent-webex auth login`
67
- 2. CLI requests a device code from Webex
68
- 3. Browser opens to Webex verification page
69
- 4. Enter the displayed code and sign in
70
- 5. CLI automatically detects approval and stores tokens
71
- 6. Access token auto-refreshes via refresh token
72
-
73
92
  ### Token Types
74
93
 
75
- - **OAuth Device Grant (default)**: Zero-config login. Access token auto-refreshes. Built-in Integration credentials used unless overridden.
94
+ - **Extracted (browser)**: First-party token from web.webex.com. Messages appear as you. Requires re-extraction when expired.
95
+ - **OAuth Device Grant**: Zero-config login. Access token auto-refreshes. Messages show "via agent-messenger".
76
96
  - **Bot Token**: Pass via `--token` flag. Never expires. Best for CI/CD.
77
97
  - **Custom Integration**: Pass `--client-id` + `--client-secret` or set env vars for your own Webex Integration.
78
98
 
79
- **IMPORTANT**: NEVER guide the user to open a web browser, use DevTools, or manually copy tokens from a browser's network inspector. Always use `agent-webex auth login` for interactive authentication.
99
+ **IMPORTANT**: NEVER guide the user to open a web browser, use DevTools, or manually copy tokens from a browser's network inspector. Always use `agent-webex auth extract` or `agent-webex auth login` for authentication.
80
100
 
81
101
  For detailed token management, see [references/authentication.md](references/authentication.md).
82
102
 
@@ -2,17 +2,40 @@
2
2
 
3
3
  ## Overview
4
4
 
5
- agent-webex supports three authentication methods against the Webex REST API (`https://webexapis.com/v1`):
5
+ agent-webex supports four authentication methods against the Webex REST API (`https://webexapis.com/v1`):
6
6
 
7
- 1. **OAuth Device Grant** (default): Zero-config. Run `auth login`, approve in browser, done. Tokens refresh automatically.
8
- 2. **Bot Token**: Pass via `auth login --token`. Never expires. Best for CI/CD.
9
- 3. **Personal Access Token (PAT)**: Pass via `auth login --token`. Expires in 12 hours. For quick testing.
7
+ 1. **Browser Token Extraction**: Extracts your first-party token from a Chromium browser where you're logged into web.webex.com. Currently supports read operations (spaces, members, auth). Zero-config.
8
+ 2. **OAuth Device Grant** (recommended for messaging): Zero-config. Run `auth login`, approve in browser, done. Tokens refresh automatically. Supports all operations including sending messages (shows "via agent-messenger").
9
+ 3. **Bot Token**: Pass via `auth login --token`. Never expires. Best for CI/CD.
10
+ 4. **Personal Access Token (PAT)**: Pass via `auth login --token`. Expires in 12 hours. For quick testing.
10
11
 
11
12
  ## Token Types
12
13
 
13
- ### OAuth Device Grant (default)
14
+ ### Browser Token Extraction
14
15
 
15
- The primary authentication method. No credentials to copy, no developer portal setup required.
16
+ Extracts your first-party Webex session token from a Chromium-based browser where you're logged into web.webex.com. Currently supports read operations (authentication, listing spaces/members, snapshots). Sending messages via the REST API is not yet supported because the web client's token lacks `spark:messages_write` scope — the web client uses internal Cisco APIs for messaging instead.
17
+
18
+ - **How it works**: Run `agent-webex auth extract`. The CLI scans Chromium browser profiles for Webex localStorage data (LevelDB files). It finds the `webex-storage` key containing `Credentials.@.supertoken` and extracts the access token. No browser automation, no password prompts.
19
+ - **Supported browsers**: Chrome, Chrome Canary, Edge, Arc, Brave, Vivaldi, Chromium
20
+ - **Token lifetime**: Depends on Webex session policy (typically hours to days). Re-extract when expired.
21
+ - **Auto-extraction**: The CLI attempts browser extraction automatically when no valid token is stored, so you often don't need to run `auth extract` manually.
22
+ - **Best for**: Interactive use, sending messages as yourself without the "via" label
23
+
24
+ ```bash
25
+ # Extract token from browser
26
+ agent-webex auth extract
27
+
28
+ # With debug output
29
+ agent-webex auth extract --debug
30
+ ```
31
+
32
+ **Requirements**: You must be logged into web.webex.com in a supported Chromium browser. The browser does not need to be running — the CLI reads directly from on-disk LevelDB files.
33
+
34
+ **Limitations**: Direct messages (`message dm`) require an existing conversation with the recipient. The extracted token cannot create new 1:1 conversations — start one from the Webex app first, then use the CLI.
35
+
36
+ ### OAuth Device Grant
37
+
38
+ The fallback authentication method when browser extraction is unavailable. No credentials to copy, no developer portal setup required.
16
39
 
17
40
  - **How it works**: Run `agent-webex auth login`. The CLI requests a device code from Webex, opens your browser, and waits for you to approve. Once approved, access and refresh tokens are stored automatically.
18
41
  - **Access token lifetime**: 14 days
@@ -56,7 +79,10 @@ agent-webex auth login --token "YOUR_PAT_HERE"
56
79
  ## Logging In
57
80
 
58
81
  ```bash
59
- # Device Grant (default, zero-config)
82
+ # Browser extraction (recommended — messages appear as you)
83
+ agent-webex auth extract
84
+
85
+ # Device Grant (fallback — messages show "via agent-messenger")
60
86
  agent-webex auth login
61
87
 
62
88
  # With custom Integration credentials
@@ -69,6 +95,8 @@ agent-webex auth login --token <bot-token>
69
95
  agent-webex auth login --token <pat>
70
96
  ```
71
97
 
98
+ When using `auth extract`, the CLI reads your Webex session from the browser's LevelDB storage. No prompts, no browser automation.
99
+
72
100
  When using `--token`, the CLI validates the token against the Webex API before saving. If validation fails, you'll see an error and the token won't be stored.
73
101
 
74
102
  When using Device Grant, the CLI prints a URL and code, opens your browser, then polls until you approve (or the code expires).
@@ -118,6 +146,17 @@ This removes the stored credentials from disk.
118
146
 
119
147
  ### Format
120
148
 
149
+ Extracted credentials (from `auth extract`):
150
+
151
+ ```json
152
+ {
153
+ "accessToken": "...",
154
+ "refreshToken": "...",
155
+ "expiresAt": 1234567890,
156
+ "tokenType": "extracted"
157
+ }
158
+ ```
159
+
121
160
  OAuth credentials (from Device Grant):
122
161
 
123
162
  ```json
@@ -155,6 +194,19 @@ Manual credentials (from `--token`):
155
194
 
156
195
  ## Token Lifecycle
157
196
 
197
+ ### Browser Token Extraction
198
+
199
+ ```
200
+ auth extract -> Scan browser LevelDB -> Extract supertoken -> Access token (session-based)
201
+ |
202
+ Token expires
203
+ |
204
+ Re-run "auth extract"
205
+ (or auto-extraction on next CLI run)
206
+ ```
207
+
208
+ Browser-extracted tokens have no refresh mechanism — when they expire, re-extract from the browser (where your active session keeps them fresh). The CLI attempts auto-extraction on each run, so manual re-extraction is rarely needed.
209
+
158
210
  ### OAuth Device Grant
159
211
 
160
212
  ```
@@ -274,8 +326,9 @@ With a valid token, agent-webex has the same permissions as the token owner:
274
326
 
275
327
  ### Best Practices
276
328
 
277
- 1. **Use Device Grant for interactive work**: Zero-config, auto-refreshing, scoped access
278
- 2. **Use bot tokens for automation**: They don't expire and have scoped access
329
+ 1. **Use browser extraction for interactive work**: Zero-config, messages appear as you, no "via" label
330
+ 2. **Use Device Grant as fallback**: When browser extraction isn't available (no Chromium browser, headless server)
331
+ 3. **Use bot tokens for automation**: They don't expire and have scoped access
279
332
  3. **Protect credentials.json**: Never commit to version control
280
333
  4. **Rotate PATs regularly**: Don't reuse expired tokens. Generate fresh ones
281
334
  5. **Revoke compromised tokens**: Regenerate bot tokens at https://developer.webex.com/my-apps if compromised
@@ -8,14 +8,17 @@ This guide covers typical workflows for AI agents interacting with Cisco Webex u
8
8
 
9
9
  ## Auth Patterns
10
10
 
11
- ### Pattern 1: Log In
11
+ ### Pattern 1: Authenticate
12
12
 
13
13
  **Use case**: First-time setup or token renewal
14
14
 
15
15
  ```bash
16
16
  #!/bin/bash
17
17
 
18
- # Default: Device Grant (zero-config, opens browser)
18
+ # Recommended: Browser extraction (zero-config, sends as you, no "via" label)
19
+ agent-webex auth extract
20
+
21
+ # Fallback: Device Grant (zero-config, opens browser, shows "via agent-messenger")
19
22
  agent-webex auth login
20
23
 
21
24
  # With a bot token (never expires, for CI/CD)
@@ -25,7 +28,7 @@ agent-webex auth login --token "YOUR_BOT_TOKEN_HERE"
25
28
  agent-webex auth login --token "YOUR_PAT_HERE"
26
29
  ```
27
30
 
28
- **When to use**: Before any other command, if not already authenticated.
31
+ **When to use**: Before any other command, if not already authenticated. Browser extraction is preferred — it auto-runs when no valid token is stored.
29
32
 
30
33
  ### Pattern 2: Check Auth Status
31
34
 
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-whatsapp
3
3
  description: Interact with WhatsApp - send messages, read chats, manage conversations
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  allowed-tools: Bash(agent-whatsapp:*)
6
6
  metadata:
7
7
  openclaw:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: agent-whatsappbot
3
3
  description: Interact with WhatsApp using Cloud API credentials - send messages, manage templates
4
- version: 2.1.0
4
+ version: 2.2.0
5
5
  allowed-tools: Bash(agent-whatsappbot:*)
6
6
  metadata:
7
7
  openclaw:
@@ -19,8 +19,8 @@ const mockListChannels = mock(() =>
19
19
  { id: 'ws-2', name: 'Workspace 2' },
20
20
  ]),
21
21
  )
22
- const mockExtract = mock<() => Promise<{ accountCookie: string; sessionCookie: string } | null>>(() =>
23
- Promise.resolve({ accountCookie: 'fresh-account', sessionCookie: 'fresh-session' }),
22
+ const mockExtract = mock(() =>
23
+ Promise.resolve([{ accountCookie: 'fresh-account', sessionCookie: 'fresh-session' }]),
24
24
  )
25
25
 
26
26
  import {
@@ -113,7 +113,7 @@ describe('channel auth commands', () => {
113
113
  { id: 'ws-2', name: 'Workspace 2' },
114
114
  ]),
115
115
  )
116
- mockExtract.mockImplementation(() => Promise.resolve({ accountCookie: 'fresh-account', sessionCookie: 'fresh-session' }))
116
+ mockExtract.mockImplementation(() => Promise.resolve([{ accountCookie: 'fresh-account', sessionCookie: 'fresh-session' }]))
117
117
  })
118
118
 
119
119
  describe('extractAction', () => {
@@ -177,7 +177,7 @@ describe('channel auth commands', () => {
177
177
  })
178
178
 
179
179
  test('returns error when token extraction fails', async () => {
180
- mockExtract.mockImplementation(() => Promise.resolve(null))
180
+ mockExtract.mockImplementation(() => Promise.resolve([]))
181
181
 
182
182
  const result = await extractAction()
183
183
 
@@ -192,7 +192,7 @@ describe('channel auth commands', () => {
192
192
  const result = await extractAction()
193
193
 
194
194
  expect(result).toEqual({
195
- error: 'No workspaces found for this account.',
195
+ error: 'No valid credentials found. Make sure Channel Talk desktop app or browser is logged in.',
196
196
  })
197
197
  })
198
198
  })
@@ -82,45 +82,51 @@ export async function extractAction(options: ActionOptions = {}): Promise<Extrac
82
82
  const extractor = createTokenExtractor()
83
83
  const extracted = await extractor.extract()
84
84
 
85
- if (!extracted) {
85
+ if (extracted.length === 0) {
86
86
  return {
87
87
  error: 'No credentials. Make sure Channel Talk desktop app is installed and logged in.',
88
88
  }
89
89
  }
90
90
 
91
- const client = await createChannelClient(extracted.accountCookie, extracted.sessionCookie)
92
- const account = await client.getAccount()
93
- const channels = await client.listChannels()
94
-
95
- if (channels.length === 0) {
96
- return { error: 'No workspaces found for this account.' }
97
- }
98
-
99
- const previousCurrent = await credManager.getCredentials()
100
-
101
- for (const channel of channels) {
102
- await credManager.setCredentials({
103
- workspace_id: channel.id,
104
- workspace_name: channel.name,
105
- account_id: account.id,
106
- account_name: account.name,
107
- account_cookie: extracted.accountCookie,
108
- session_cookie: extracted.sessionCookie,
109
- })
91
+ for (const cookies of extracted) {
92
+ try {
93
+ const client = await createChannelClient(cookies.accountCookie, cookies.sessionCookie)
94
+ const account = await client.getAccount()
95
+ const channels = await client.listChannels()
96
+
97
+ if (channels.length === 0) continue
98
+
99
+ const previousCurrent = await credManager.getCredentials()
100
+
101
+ for (const channel of channels) {
102
+ await credManager.setCredentials({
103
+ workspace_id: channel.id,
104
+ workspace_name: channel.name,
105
+ account_id: account.id,
106
+ account_name: account.name,
107
+ account_cookie: cookies.accountCookie,
108
+ session_cookie: cookies.sessionCookie,
109
+ })
110
+ }
111
+
112
+ const previousStillExists = previousCurrent && channels.some((ch) => ch.id === previousCurrent.workspace_id)
113
+ const currentId = previousStillExists ? previousCurrent.workspace_id : channels[0].id
114
+ await credManager.setCurrent(currentId)
115
+
116
+ return {
117
+ success: true,
118
+ workspaces: channels.map((ch) => ({
119
+ workspace_id: ch.id,
120
+ workspace_name: ch.name,
121
+ })),
122
+ current_workspace_id: currentId,
123
+ }
124
+ } catch {
125
+ continue
126
+ }
110
127
  }
111
128
 
112
- const previousStillExists = previousCurrent && channels.some((ch) => ch.id === previousCurrent.workspace_id)
113
- const currentId = previousStillExists ? previousCurrent.workspace_id : channels[0].id
114
- await credManager.setCurrent(currentId)
115
-
116
- return {
117
- success: true,
118
- workspaces: channels.map((ch) => ({
119
- workspace_id: ch.id,
120
- workspace_name: ch.name,
121
- })),
122
- current_workspace_id: currentId,
123
- }
129
+ return { error: 'No valid credentials found. Make sure Channel Talk desktop app or browser is logged in.' }
124
130
  } catch (error: unknown) {
125
131
  return { error: (error as Error).message }
126
132
  }
@@ -17,7 +17,7 @@ const mockGetCredentials = mock<() => Promise<
17
17
  >>(() => Promise.resolve(null))
18
18
  const mockSetCredentials = mock(() => Promise.resolve())
19
19
  const mockSetCurrent = mock(() => Promise.resolve(true))
20
- const mockExtract = mock(() => Promise.resolve(null))
20
+ const mockExtract = mock(() => Promise.resolve([]))
21
21
  const mockGetAccount = mock(() => Promise.resolve({ id: 'acct-1', name: 'Alice' }))
22
22
  const mockListChannels = mock(() => Promise.resolve([{ id: 'ws-1', name: 'Workspace 1' }]))
23
23
 
@@ -58,17 +58,17 @@ describe('ensureChannelAuth', () => {
58
58
  mockGetCredentials.mockImplementation(() => Promise.resolve(null))
59
59
  mockSetCredentials.mockImplementation(() => Promise.resolve())
60
60
  mockSetCurrent.mockImplementation(() => Promise.resolve(true))
61
- mockExtract.mockImplementation(() => Promise.resolve(null))
61
+ mockExtract.mockImplementation(() => Promise.resolve([]))
62
62
  mockGetAccount.mockImplementation(() => Promise.resolve({ id: 'acct-1', name: 'Alice' }))
63
63
  mockListChannels.mockImplementation(() => Promise.resolve([{ id: 'ws-1', name: 'Workspace 1' }]))
64
64
  })
65
65
 
66
66
  test('extracts and saves workspaces when no credentials exist', async () => {
67
67
  mockExtract.mockImplementation(() =>
68
- Promise.resolve({
68
+ Promise.resolve([{
69
69
  accountCookie: 'account-cookie',
70
70
  sessionCookie: 'session-cookie',
71
- }),
71
+ }]),
72
72
  )
73
73
  mockListChannels.mockImplementation(() =>
74
74
  Promise.resolve([
@@ -128,10 +128,10 @@ describe('ensureChannelAuth', () => {
128
128
  )
129
129
  mockGetAccount.mockImplementationOnce(() => Promise.reject(new Error('Unauthorized')))
130
130
  mockExtract.mockImplementation(() =>
131
- Promise.resolve({
131
+ Promise.resolve([{
132
132
  accountCookie: 'fresh-account',
133
133
  sessionCookie: 'fresh-session',
134
- }),
134
+ }]),
135
135
  )
136
136
 
137
137
  await ensureChannelAuth()
@@ -58,11 +58,11 @@ export async function ensureChannelAuth(): Promise<void> {
58
58
 
59
59
  const extractor = createTokenExtractor()
60
60
  const extracted = await extractor.extract()
61
- if (!extracted) {
61
+ if (extracted.length === 0) {
62
62
  return
63
63
  }
64
64
 
65
- const client = await createChannelClient(extracted.accountCookie, extracted.sessionCookie)
65
+ const client = await createChannelClient(extracted[0].accountCookie, extracted[0].sessionCookie)
66
66
  const account = await client.getAccount()
67
67
  const channels = await client.listChannels()
68
68
  if (channels.length === 0) {
@@ -76,8 +76,8 @@ export async function ensureChannelAuth(): Promise<void> {
76
76
  workspace_name: currentChannel.name,
77
77
  account_id: account.id,
78
78
  account_name: account.name,
79
- account_cookie: extracted.accountCookie,
80
- session_cookie: extracted.sessionCookie,
79
+ account_cookie: extracted[0].accountCookie,
80
+ session_cookie: extracted[0].sessionCookie,
81
81
  })
82
82
 
83
83
  for (const channel of otherChannels) {
@@ -86,8 +86,8 @@ export async function ensureChannelAuth(): Promise<void> {
86
86
  workspace_name: channel.name,
87
87
  account_id: account.id,
88
88
  account_name: account.name,
89
- account_cookie: extracted.accountCookie,
90
- session_cookie: extracted.sessionCookie,
89
+ account_cookie: extracted[0].accountCookie,
90
+ session_cookie: extracted[0].sessionCookie,
91
91
  })
92
92
  }
93
93