Skip to content

Conversation

@rgarcia
Copy link
Contributor

@rgarcia rgarcia commented Jan 1, 2026

Summary

Adds a new kernel claude command group that provides a complete workflow for using the Claude for Chrome extension in Kernel browsers:

  • Extract the Claude extension and authentication data from a local Chrome installation
  • Launch a new Kernel browser with the extension pre-loaded and authenticated
  • Load the extension into an existing Kernel browser
  • Chat interactively with Claude via a TUI
  • Send single messages programmatically (scriptable)
  • Status check the extension's state in a browser

Usage

Step 1: Extract the extension from your local Chrome (run on your Mac/Linux machine with Chrome)

# Extract extension + auth data to a bundle file
kernel claude extract -o claude-bundle.zip

# Extract without auth (will require login in browser)
kernel claude extract -o claude-bundle.zip --no-auth

# Use a specific Chrome profile
kernel claude extract -o claude-bundle.zip --chrome-profile "Profile 1"

Step 2: Launch a Kernel browser with Claude pre-loaded

# Launch browser with Claude extension
kernel claude launch -b claude-bundle.zip

# Launch with custom timeout
kernel claude launch -b claude-bundle.zip -t 7200

# Launch and navigate to a specific URL
kernel claude launch -b claude-bundle.zip --url https://example.com

Step 3: Interact with Claude

# Check extension status
kernel claude status <browser-id>

# Send a single message (scriptable)
kernel claude send <browser-id> "What is 2+2?"

# Send from stdin
echo "Explain this error" | kernel claude send <browser-id>

# Send from file
kernel claude send <browser-id> -f prompt.txt

# Interactive chat TUI
kernel claude chat <browser-id>

Loading into existing browser

# Load Claude into an existing browser session
kernel claude load <browser-id> -b claude-bundle.zip

Implementation Details

  • Uses existing Kernel APIs: browsers.LoadExtensions, browsers.Fs, browsers.Process, browsers.Playwright
  • Embedded Playwright scripts for browser automation (send_message.js, check_status.js, stream_chat.js)
  • Handles Chrome's Preferences file to pin the extension to the toolbar
  • Stops Chrome before modifying preferences, then restarts to apply changes
  • Closes extra tabs opened by the extension and navigates to chrome://newtab

Test plan

  • kernel claude extract works on macOS/Linux with Chrome installed
  • kernel claude launch creates browser with extension loaded and pinned
  • kernel claude load loads extension into existing browser
  • kernel claude status reports extension state
  • kernel claude send sends messages and receives responses
  • kernel claude chat provides interactive TUI
  • Unit tests pass for bundle and path utilities

Note

Adds a focused CLI workflow to run the Claude for Chrome extension inside Kernel browsers.

  • New kernel claude command group: extract, launch, load, status, send, chat
  • Integrates with Kernel APIs (browsers.LoadExtensions, browsers.Fs, browsers.Process, browsers.Playwright) to upload/pin the extension, open the side panel, and automate chat
  • New internal package internal/claude: bundle.go (zip bundle create/extract), paths.go (Chrome/extension paths, profile discovery), loader.go (load/pin, auth storage upload), constants, and embedded scripts (scripts/send_message.js, scripts/check_status.js, scripts/stream_chat.js)
  • Unit tests for bundle and path utilities
  • Registers command in cmd/root.go and documents usage in README.md

Written by Cursor Bugbot for commit ef81888. This will update automatically on new commits. Configure here.

rgarcia added 11 commits January 1, 2026 13:59
Implement a complete workflow for using the Claude for Chrome extension
in Kernel browsers:

Commands:
- kernel claude extract: Extract extension + auth from local Chrome
- kernel claude launch: Create browser with Claude pre-loaded
- kernel claude load: Load extension into existing browser
- kernel claude status: Check extension/auth status
- kernel claude send: Send single message (scriptable)
- kernel claude chat: Interactive TUI chat session

Features:
- Cross-platform Chrome path detection (macOS, Linux, Windows)
- Bundle format with extension and auth storage
- Embedded Playwright scripts for browser automation
- Scriptable send command with stdin/file/JSON support
- Interactive chat with /help, /status, /clear commands
Add retry loop to poll GET /browsers/:id after creation to handle
eventual consistency before attempting to load the extension.
The LevelDB files inside the auth storage directory need to be owned
by the kernel user, not just the directory itself. Use process exec
with chown -R to fix the LOCK file access denied errors.
- Add pinExtension() to update Chrome's Preferences file with the
  extension ID in pinned_extensions array
- Restart Chromium via supervisorctl to pick up the new preference
- Use Spawn (fire and forget) since supervisorctl restart takes time
Chrome overwrites the Preferences file on exit, so we need to:
1. Stop Chromium via supervisorctl stop
2. Update the Preferences file with pinned_extensions
3. Start Chromium via supervisorctl start

This ensures Chrome reads our updated Preferences on startup.
The Claude extension opens claude.ai by default, which shows a login
prompt. After loading the extension and restarting Chrome, navigate
all tabs to chrome://newtab for a cleaner initial state.
The Claude extension opens a second tab to claude.ai. Close all but
the first tab before navigating to chrome://newtab.
The SDK's Playwright.Execute has built-in retry, so we can fire and
forget the supervisorctl start command instead of waiting for it.
pterm.Info.Println(" 2. The Claude for Chrome extension is installed")
pterm.Info.Println(" 3. You're using the correct Chrome profile (use --list-profiles to see available profiles)")
return nil
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extract command returns success on extension not found

When GetChromeExtensionPath fails (Chrome or extension not installed), the function prints error messages via pterm.Error but then returns nil instead of the error. This causes the command to exit with status code 0 (success), which breaks scripting and automation workflows that check exit codes. Other error handling in the same function correctly uses return fmt.Errorf(...). This should return the error or a wrapped version of it.

Fix in Cursor Fix in Web

return err
}
continue
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Symlink target not validated during zip extraction

The unzip function validates destPath against zip-slip attacks (line 195-196), but when extracting symlinks, the linkTarget read from the zip file is not validated. A malicious bundle could contain a symlink like extension/config -> /etc/passwd that passes the destination path check but points outside the extraction directory. When files are later read through this symlink, sensitive files could be accessed or overwritten.

Fix in Cursor Fix in Web

pterm.Info.Printf("Navigating to: %s\n", startURL)
_, err := client.Browsers.Playwright.Execute(ctx, browser.SessionID, kernel.BrowserPlaywrightExecuteParams{
Code: fmt.Sprintf(`await page.goto('%s');`, startURL),
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

URL not escaped in JavaScript code execution

The startURL is directly interpolated into JavaScript code using string formatting without proper escaping. If a URL contains single quotes or is crafted maliciously (e.g., http://x'); malicious_code(); //), it can break out of the string literal and execute arbitrary JavaScript in the Playwright context. The code uses fmt.Sprintf with '%s' but doesn't escape the URL first using something like jsonMarshalString which is available and used elsewhere in this codebase.

Fix in Cursor Fix in Web

The Playwright execution API expects inline code, not CommonJS modules.
Remove the async function wrapper and module.exports, making the scripts
execute directly in the Playwright context.
- Use div.claude-response for Claude's response messages
- Use [contenteditable].ProseMirror for the chat input
- These match the actual DOM structure of the Claude extension
Slash commands in Claude require an extra Enter press to confirm.
Detect if message starts with '/' and press Enter a second time.
Instead of navigating to the sidepanel URL (which opens it as a broken
full tab), click on the pinned extension icon at coordinates (1775, 55)
to open the proper side panel.

- Add OpenSidePanel() function that uses Computer.ClickMouse API
- Add ExtensionIconX/Y constants for the click coordinates
- Update send, status, and chat commands to call OpenSidePanel first
- Update Playwright scripts to wait for the side panel to appear
  instead of trying to navigate to it
return client.Browsers.Computer.ClickMouse(ctx, browserID, kernel.BrowserComputerClickMouseParams{
X: ExtensionIconX,
Y: ExtensionIconY,
})
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hardcoded click coordinates fail for non-1920x1080 viewports

OpenSidePanel uses hardcoded coordinates (ExtensionIconX = 1775, ExtensionIconY = 55) that are explicitly documented as being for 1920x1080 resolution. When users launch a browser with a different viewport via the --viewport flag (e.g., 1280x720), the extension icon will be at a different X position. The click will miss the icon entirely (or click outside the visible area), causing send, chat, and status commands to fail to open the side panel.

Additional Locations (1)

Fix in Cursor Fix in Web

Check if a page with sidepanel.html exists before clicking. This
prevents accidentally clicking the extensions button when the side
panel is already visible.
Claude for Chrome has its own slash commands (like /hn-summary).
Only handle /quit, /exit, /clear locally; pass everything else to Claude.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants