mirror of https://github.com/garrytan/gstack.git
feat: auto-load Chrome extension when $B connect launches Chrome
Extension auto-loads via --load-extension flag — no manual chrome://extensions install needed. findExtensionPath() checks repo root, global install, and dev paths. Also adds bin/gstack-extension helper for manual install in regular Chrome, and rewrites BROWSER.md install docs with auto-load as primary path. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
64e8cd4fa5
commit
594e0c7bae
67
BROWSER.md
67
BROWSER.md
|
|
@ -164,54 +164,43 @@ The window has a subtle green shimmer line at the top edge and a floating "gstac
|
||||||
|
|
||||||
### Chrome extension (Side Panel)
|
### Chrome extension (Side Panel)
|
||||||
|
|
||||||
A Chrome extension that shows a live activity feed of browse commands in a Side Panel, plus @ref overlays on the page. Works in any Chrome-based browser (Chrome, Comet, Edge).
|
A Chrome extension that shows a live activity feed of browse commands in a Side Panel, plus @ref overlays on the page.
|
||||||
|
|
||||||
#### Step-by-step install
|
#### Automatic install (recommended)
|
||||||
|
|
||||||
**1. Open the extensions page**
|
When you run `$B connect`, the extension **auto-loads** into the Playwright-controlled Chrome window. No manual steps needed — the Side Panel is immediately available.
|
||||||
|
|
||||||
Type `chrome://extensions` in Chrome's address bar and press Enter.
|
|
||||||
|
|
||||||
**2. Enable Developer mode**
|
|
||||||
|
|
||||||
In the top-right corner of the extensions page, toggle the **Developer mode** switch ON. You'll see new buttons appear: "Load unpacked", "Pack extension", and "Update".
|
|
||||||
|
|
||||||
**3. Load the extension**
|
|
||||||
|
|
||||||
Click **Load unpacked**. A file picker dialog opens.
|
|
||||||
|
|
||||||
A file picker opens. You need to navigate to the `extension/` folder inside gstack, but macOS hides folders starting with `.` by default. The easiest way: press **Cmd+Shift+G** in the file picker to open "Go to folder", then paste one of these paths:
|
|
||||||
|
|
||||||
- Global install: `~/.claude/skills/gstack/extension`
|
|
||||||
- Project install: `<your-repo>/.claude/skills/gstack/extension`
|
|
||||||
- Dev/source: `<gstack-repo>/extension`
|
|
||||||
|
|
||||||
Press Enter, then click **Select** (select the `extension/` folder itself, not a file inside it).
|
|
||||||
|
|
||||||
Alternatively, press **Cmd+Shift+.** (period) in the file picker to reveal hidden files, then navigate to `.claude/skills/gstack/extension` manually.
|
|
||||||
|
|
||||||
**4. Pin the extension**
|
|
||||||
|
|
||||||
Click the puzzle piece icon (Extensions) in Chrome's toolbar. Find "gstack browse" and click the pin icon so it's always visible.
|
|
||||||
|
|
||||||
**5. Configure the port**
|
|
||||||
|
|
||||||
Click the gstack icon in the toolbar. A popup appears with a port input field.
|
|
||||||
|
|
||||||
Find your browse server port — run `$B status` or check `.gstack/browse.json` in your project root:
|
|
||||||
```bash
|
```bash
|
||||||
cat .gstack/browse.json | grep port
|
$B connect # launches Chrome with extension pre-loaded
|
||||||
# or
|
# Click the gstack icon in toolbar → Open Side Panel
|
||||||
$B status # shows the port in the output
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Enter the port number and press Enter. The popup saves it and starts polling.
|
The port is auto-configured. You're done.
|
||||||
|
|
||||||
**6. Open the Side Panel**
|
#### Manual install (for your regular Chrome)
|
||||||
|
|
||||||
Click the gstack icon again, then click **Open Side Panel**. The Side Panel slides open on the right side of Chrome showing a live activity feed.
|
If you want the extension in your everyday Chrome (not the Playwright-controlled one), run:
|
||||||
|
|
||||||
Alternatively, right-click the gstack icon and choose "Open side panel."
|
```bash
|
||||||
|
bin/gstack-extension # opens chrome://extensions, copies path to clipboard
|
||||||
|
```
|
||||||
|
|
||||||
|
Or do it manually:
|
||||||
|
|
||||||
|
1. **Go to `chrome://extensions`** in Chrome's address bar
|
||||||
|
2. **Toggle "Developer mode" ON** (top-right corner)
|
||||||
|
3. **Click "Load unpacked"** — a file picker opens
|
||||||
|
4. **Navigate to the extension folder:** Press **Cmd+Shift+G** in the file picker to open "Go to folder", then paste one of these paths:
|
||||||
|
- Global install: `~/.claude/skills/gstack/extension`
|
||||||
|
- Dev/source: `<gstack-repo>/extension`
|
||||||
|
|
||||||
|
Press Enter, then click **Select**.
|
||||||
|
|
||||||
|
(Tip: macOS hides folders starting with `.` — press **Cmd+Shift+.** in the file picker to reveal them if you prefer to navigate manually.)
|
||||||
|
|
||||||
|
5. **Pin it:** Click the puzzle piece icon (Extensions) in the toolbar → pin "gstack browse"
|
||||||
|
6. **Set the port:** Click the gstack icon → enter the port from `$B status` or `.gstack/browse.json`
|
||||||
|
7. **Open Side Panel:** Click the gstack icon → "Open Side Panel"
|
||||||
|
|
||||||
#### What you get
|
#### What you get
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,65 @@
|
||||||
|
#!/bin/bash
|
||||||
|
# gstack-extension — helper to install the Chrome extension
|
||||||
|
#
|
||||||
|
# When using $B connect, the extension auto-loads. This script is for
|
||||||
|
# installing it in your regular Chrome (not the Playwright-controlled one).
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
||||||
|
|
||||||
|
# Find the extension directory
|
||||||
|
EXT_DIR=""
|
||||||
|
if [ -f "$REPO_ROOT/extension/manifest.json" ]; then
|
||||||
|
EXT_DIR="$REPO_ROOT/extension"
|
||||||
|
elif [ -f "$HOME/.claude/skills/gstack/extension/manifest.json" ]; then
|
||||||
|
EXT_DIR="$HOME/.claude/skills/gstack/extension"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -z "$EXT_DIR" ]; then
|
||||||
|
echo "Error: extension/ directory not found."
|
||||||
|
echo "Expected at: $REPO_ROOT/extension/ or ~/.claude/skills/gstack/extension/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Copy path to clipboard
|
||||||
|
echo -n "$EXT_DIR" | pbcopy 2>/dev/null
|
||||||
|
|
||||||
|
# Get browse server port
|
||||||
|
PORT=""
|
||||||
|
STATE_FILE="$REPO_ROOT/.gstack/browse.json"
|
||||||
|
if [ -f "$STATE_FILE" ]; then
|
||||||
|
PORT=$(grep -o '"port":[0-9]*' "$STATE_FILE" | grep -o '[0-9]*')
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "gstack Chrome Extension Setup"
|
||||||
|
echo "=============================="
|
||||||
|
echo ""
|
||||||
|
echo "Extension path (copied to clipboard):"
|
||||||
|
echo " $EXT_DIR"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
if [ -n "$PORT" ]; then
|
||||||
|
echo "Browse server port: $PORT"
|
||||||
|
echo ""
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Quick install (if using \$B connect):"
|
||||||
|
echo " The extension auto-loads when you run \$B connect."
|
||||||
|
echo " No manual installation needed!"
|
||||||
|
echo ""
|
||||||
|
echo "Manual install (for your regular Chrome):"
|
||||||
|
echo ""
|
||||||
|
echo " 1. Opening chrome://extensions now..."
|
||||||
|
|
||||||
|
# Open chrome://extensions
|
||||||
|
osascript -e 'tell application "Google Chrome" to open location "chrome://extensions"' 2>/dev/null || \
|
||||||
|
open "chrome://extensions" 2>/dev/null || \
|
||||||
|
echo " Could not open Chrome. Navigate to chrome://extensions manually."
|
||||||
|
|
||||||
|
echo " 2. Toggle 'Developer mode' ON (top-right)"
|
||||||
|
echo " 3. Click 'Load unpacked'"
|
||||||
|
echo " 4. In the file picker: Cmd+Shift+G → paste (path is in your clipboard) → Enter → Select"
|
||||||
|
echo " 5. Click the gstack puzzle icon in toolbar → enter port: ${PORT:-<check \$B status>}"
|
||||||
|
echo " 6. Click 'Open Side Panel'"
|
||||||
|
|
@ -70,6 +70,39 @@ export class BrowserManager {
|
||||||
|
|
||||||
getConnectionMode(): 'launched' | 'cdp' { return this.connectionMode; }
|
getConnectionMode(): 'launched' | 'cdp' { return this.connectionMode; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Find the gstack Chrome extension directory.
|
||||||
|
* Checks: repo root /extension, global install, dev install.
|
||||||
|
*/
|
||||||
|
private findExtensionPath(): string | null {
|
||||||
|
const fs = require('fs');
|
||||||
|
const path = require('path');
|
||||||
|
const candidates = [
|
||||||
|
// Relative to this source file (dev mode: browse/src/ -> ../../extension)
|
||||||
|
path.resolve(__dirname, '..', '..', 'extension'),
|
||||||
|
// Global gstack install
|
||||||
|
path.join(process.env.HOME || '', '.claude', 'skills', 'gstack', 'extension'),
|
||||||
|
// Git repo root (detected via BROWSE_STATE_FILE location)
|
||||||
|
(() => {
|
||||||
|
const stateFile = process.env.BROWSE_STATE_FILE || '';
|
||||||
|
if (stateFile) {
|
||||||
|
const repoRoot = path.resolve(path.dirname(stateFile), '..');
|
||||||
|
return path.join(repoRoot, '.claude', 'skills', 'gstack', 'extension');
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
})(),
|
||||||
|
].filter(Boolean);
|
||||||
|
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
try {
|
||||||
|
if (fs.existsSync(path.join(candidate, 'manifest.json'))) {
|
||||||
|
return candidate;
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the ref map for external consumers (e.g., /refs endpoint).
|
* Get the ref map for external consumers (e.g., /refs endpoint).
|
||||||
*/
|
*/
|
||||||
|
|
@ -125,12 +158,20 @@ export class BrowserManager {
|
||||||
this.refMap.clear();
|
this.refMap.clear();
|
||||||
this.nextTabId = 1;
|
this.nextTabId = 1;
|
||||||
|
|
||||||
|
// Find the gstack extension directory for auto-loading
|
||||||
|
const extensionPath = this.findExtensionPath();
|
||||||
|
const launchArgs = ['--restore-last-session'];
|
||||||
|
if (extensionPath) {
|
||||||
|
launchArgs.push(`--disable-extensions-except=${extensionPath}`);
|
||||||
|
launchArgs.push(`--load-extension=${extensionPath}`);
|
||||||
|
}
|
||||||
|
|
||||||
// Launch real Chrome via Playwright's channel protocol
|
// Launch real Chrome via Playwright's channel protocol
|
||||||
// This uses the system Chrome binary, headed, with real window
|
// This uses the system Chrome binary, headed, with real window
|
||||||
this.browser = await chromium.launch({
|
this.browser = await chromium.launch({
|
||||||
channel: 'chrome',
|
channel: 'chrome',
|
||||||
headless: false,
|
headless: false,
|
||||||
args: ['--restore-last-session'],
|
args: launchArgs,
|
||||||
});
|
});
|
||||||
this.connectionMode = 'cdp';
|
this.connectionMode = 'cdp';
|
||||||
this.intentionalDisconnect = false;
|
this.intentionalDisconnect = false;
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue