From 35daac8317ecbede7e427edbc9595e6e35fdde20 Mon Sep 17 00:00:00 2001 From: JP Richardson Date: Sun, 10 May 2026 20:19:46 -0500 Subject: [PATCH 1/2] fix(auth): clearer import-brave error + add to README/skill docs extractFromBrave() wrapped its body in `try { ... } catch { return null }`, which swallowed the specific "Allow JavaScript from Apple Events is turned off" failure behind a generic "ensure you're logged in" message. That toggle is OFF by default in Brave, so first-run import-brave consistently looked like an auth problem instead of a one-time browser setting. - Detect the AppleScript-disabled error in osascript() and throw a typed BraveAppleScriptDisabledError with an actionable message (menu path + re-run hint). Other failure modes keep the existing null-return behavior. - import-brave's command description and fallback error now both reference the View -> Developer -> Allow JavaScript from Apple Events requirement. - README: list import-brave in the command tree and manual-imports block, plus a NOTE explaining the Chromium AppleScript-JS prereq for import-brave / import-chrome. - Skill docs: add import-brave to SKILL.md (fallback section) and references/commands.md (auth command list). SKILL.md's fallback intro updated to "Brave/Chrome/Firefox" since Brave is in the auto-detection chain (context-client-resolver.ts). Co-Authored-By: Claude Opus 4.7 (1M context) --- README.md | 7 +++- skills/agent-slack/SKILL.md | 9 ++++- skills/agent-slack/references/commands.md | 1 + src/auth/brave.ts | 48 ++++++++++++++++++++--- src/cli/auth-command.ts | 7 +++- 5 files changed, 62 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 7f3cc8f..b1c063c 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ agent-slack │ ├── whoami │ ├── test │ ├── import-desktop +│ ├── import-brave │ ├── import-chrome │ ├── import-firefox │ └── parse-curl @@ -109,18 +110,22 @@ Notes: On macOS and Windows, authentication happens automatically: - Default: reads Slack Desktop local data (no need to quit Slack) -- Fallbacks: if that fails, tries Chrome/Firefox extraction (macOS) +- Fallbacks: if that fails, tries Brave/Chrome/Firefox extraction (macOS) You can also run manual imports: ```bash agent-slack auth whoami agent-slack auth import-desktop +agent-slack auth import-brave agent-slack auth import-chrome agent-slack auth import-firefox agent-slack auth test ``` +> [!NOTE] +> `import-brave` / `import-chrome` read tokens from a logged-in Slack tab via AppleScript. Both browsers ship with **Allow JavaScript from Apple Events** disabled by default — enable it in **View → Developer** before running these commands. macOS will prompt for your password the first time. + Alternatively, set env vars: ```bash diff --git a/skills/agent-slack/SKILL.md b/skills/agent-slack/SKILL.md index 8711385..407913c 100644 --- a/skills/agent-slack/SKILL.md +++ b/skills/agent-slack/SKILL.md @@ -46,7 +46,7 @@ Claude Code's permission checker has security heuristics that force manual appro ## Quick start (auth) -Authentication is automatic on macOS and Windows (Slack Desktop first, then Chrome/Firefox fallbacks on macOS). +Authentication is automatic on macOS and Windows (Slack Desktop first, then Brave/Chrome/Firefox fallbacks on macOS). If credentials aren’t available, run one of: @@ -57,6 +57,13 @@ agent-slack auth import-desktop agent-slack auth test ``` +- Brave fallback (requires View → Developer → Allow JavaScript from Apple Events): + +```bash +agent-slack auth import-brave +agent-slack auth test +``` + - Chrome fallback: ```bash diff --git a/skills/agent-slack/references/commands.md b/skills/agent-slack/references/commands.md index 8cb7491..d7693ed 100644 --- a/skills/agent-slack/references/commands.md +++ b/skills/agent-slack/references/commands.md @@ -7,6 +7,7 @@ Run `agent-slack --help` (or `agent-slack --help`) for the full option - `agent-slack auth whoami` — show configured workspaces + token sources (secrets redacted) - `agent-slack auth test [--workspace ]` — verify credentials (`auth.test`) - `agent-slack auth import-desktop` — import browser-style creds from Slack Desktop (macOS/Windows) +- `agent-slack auth import-brave` — import creds from Brave (macOS; requires View → Developer → Allow JavaScript from Apple Events) - `agent-slack auth import-chrome` — import creds from Chrome (macOS) - `agent-slack auth import-firefox` — import creds from Firefox profile storage (macOS/Linux) - `agent-slack auth parse-curl` — read a copied Slack cURL command from stdin and save creds diff --git a/src/auth/brave.ts b/src/auth/brave.ts index f6f0a1f..382a4e1 100644 --- a/src/auth/brave.ts +++ b/src/auth/brave.ts @@ -15,14 +15,47 @@ export type BraveExtracted = { const IS_MACOS = platform() === "darwin"; +// Brave (like Chrome) ships with `Allow JavaScript from Apple Events` OFF. +// Without it, `execute javascript` from osascript fails — and that's how this +// module reads `localStorage.localConfig_v2` from a Slack tab. We detect that +// specific failure and surface an actionable message instead of swallowing it. +export class BraveAppleScriptDisabledError extends Error { + constructor() { + super( + "Brave is blocking JavaScript from Apple Events.\n" + + "Enable it: Brave menu → View → Developer → Allow JavaScript from Apple Events\n" + + "(macOS will prompt for your password the first time.)\n" + + "Then re-run: agent-slack auth import-brave", + ); + this.name = "BraveAppleScriptDisabledError"; + } +} + // --- AppleScript helpers (for extracting teams from Brave tabs) --- +const APPLESCRIPT_JS_DISABLED_MARKER = "Executing JavaScript through AppleScript is turned off"; + function osascript(script: string): string { - return execFileSync("osascript", ["-e", script], { - encoding: "utf8", - timeout: 7000, - stdio: ["ignore", "pipe", "pipe"], - }).trim(); + try { + return execFileSync("osascript", ["-e", script], { + encoding: "utf8", + timeout: 7000, + stdio: ["ignore", "pipe", "pipe"], + }).trim(); + } catch (err: unknown) { + const stderr = + err && typeof err === "object" && "stderr" in err + ? String((err as { stderr: unknown }).stderr ?? "") + : ""; + const message = err instanceof Error ? err.message : String(err); + if ( + stderr.includes(APPLESCRIPT_JS_DISABLED_MARKER) || + message.includes(APPLESCRIPT_JS_DISABLED_MARKER) + ) { + throw new BraveAppleScriptDisabledError(); + } + throw err; + } } const TEAM_JSON_PATHS = [ @@ -179,7 +212,10 @@ export async function extractFromBrave(): Promise { } return { cookie_d, teams }; - } catch { + } catch (err) { + if (err instanceof BraveAppleScriptDisabledError) { + throw err; + } return null; } } diff --git a/src/cli/auth-command.ts b/src/cli/auth-command.ts index 10079fb..f4f3127 100644 --- a/src/cli/auth-command.ts +++ b/src/cli/auth-command.ts @@ -105,13 +105,16 @@ export function registerAuthCommand(input: { program: Command; ctx: CliContext } auth .command("import-brave") - .description("Import xoxc/xoxd from a logged-in Slack tab in Brave Browser (macOS)") + .description( + "Import xoxc/xoxd from a logged-in Slack tab in Brave Browser (macOS). Requires View → Developer → Allow JavaScript from Apple Events to be enabled in Brave.", + ) .action(async () => { try { const extracted = await input.ctx.importBrave(); if (!extracted) { throw new Error( - "Could not extract tokens from Brave. Open Slack in Brave and ensure you're logged in.", + "Could not extract tokens from Brave. Open Slack in Brave and ensure you're logged in. " + + "If Brave is open with a Slack tab, also confirm View → Developer → Allow JavaScript from Apple Events is enabled.", ); } From 4e463f5566ec46aa5f88512edc6260cae2a47237 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Sat, 30 May 2026 00:13:06 -0700 Subject: [PATCH 2/2] fix(auth): keep Brave setup error from blocking fallbacks --- README.md | 2 +- skills/agent-slack/SKILL.md | 2 +- src/cli/context-client-resolver.ts | 14 ++++++++++---- 3 files changed, 12 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index b1c063c..c7197f4 100644 --- a/README.md +++ b/README.md @@ -110,7 +110,7 @@ Notes: On macOS and Windows, authentication happens automatically: - Default: reads Slack Desktop local data (no need to quit Slack) -- Fallbacks: if that fails, tries Brave/Chrome/Firefox extraction (macOS) +- Fallbacks: if that fails, tries Chrome/Brave/Firefox extraction (macOS) You can also run manual imports: diff --git a/skills/agent-slack/SKILL.md b/skills/agent-slack/SKILL.md index 407913c..3eab946 100644 --- a/skills/agent-slack/SKILL.md +++ b/skills/agent-slack/SKILL.md @@ -46,7 +46,7 @@ Claude Code's permission checker has security heuristics that force manual appro ## Quick start (auth) -Authentication is automatic on macOS and Windows (Slack Desktop first, then Brave/Chrome/Firefox fallbacks on macOS). +Authentication is automatic on macOS and Windows (Slack Desktop first, then Chrome/Brave/Firefox fallbacks on macOS). If credentials aren’t available, run one of: diff --git a/src/cli/context-client-resolver.ts b/src/cli/context-client-resolver.ts index aea1a68..d9beb8b 100644 --- a/src/cli/context-client-resolver.ts +++ b/src/cli/context-client-resolver.ts @@ -1,4 +1,4 @@ -import { extractFromBrave } from "../auth/brave.ts"; +import { BraveAppleScriptDisabledError, extractFromBrave } from "../auth/brave.ts"; import { extractFromChrome } from "../auth/chrome.ts"; import { extractFromSlackDesktop } from "../auth/desktop.ts"; import { extractFromFirefox } from "../auth/firefox.ts"; @@ -144,9 +144,15 @@ export async function getClientForWorkspace(workspaceUrl?: string): Promise<{ if (chromeResult && chromeResult.teams.length > 0) { browserSources.push(chromeResult); } - const braveResult = await extractFromBrave(); - if (braveResult && braveResult.teams.length > 0) { - browserSources.push(braveResult); + try { + const braveResult = await extractFromBrave(); + if (braveResult && braveResult.teams.length > 0) { + browserSources.push(braveResult); + } + } catch (err) { + if (!(err instanceof BraveAppleScriptDisabledError)) { + throw err; + } } const firefoxResult = await extractFromFirefox(); if (firefoxResult && firefoxResult.teams.length > 0) {