Compare commits
20 Commits
v1.0.0
...
8d3c2dc9fc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8d3c2dc9fc | ||
|
|
eede8cdee5 | ||
|
|
8783ae0dc2 | ||
|
|
9ad89e7248 | ||
|
|
9dcbd230c5 | ||
|
|
daa4b55ee5 | ||
|
|
0b09201160 | ||
|
|
99d31b15c8 | ||
|
|
a83df06513 | ||
|
|
05559b8b1d | ||
|
|
ca1a88776d | ||
|
|
14be38dd16 | ||
|
|
aa78576ad7 | ||
|
|
f57010971a | ||
|
|
e40805801a | ||
|
|
c3c77c1e04 | ||
|
|
23eda72d91 | ||
|
|
b131e05513 | ||
|
|
019df3b3b0 | ||
|
|
42e465045c |
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2025 OpenDia Team
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
150
README.md
@@ -1,9 +1,12 @@
|
||||
# OpenDia <img src="opendia-extension/icon-128.png" alt="OpenDia" width="32" height="32">
|
||||
# OpenDia <img src="opendia-extension/icons/icon-128.png" alt="OpenDia" width="32" height="32">
|
||||
|
||||
> **The open alternative to Dia**
|
||||
> Connect your browser to AI models. No browser switching needed—works seamlessly with any Chromium browser including Chrome & Arc.
|
||||
**The open alternative to Dia / Perplexity Comet**
|
||||
Connect your browser to AI models.
|
||||
No browser switching needed—works seamlessly with Chrome, Firefox, and any Chromium browser. Private, local-first & MCP focused.
|
||||
If you are not technical / never used MCPs before, we recommend using **[Perplexity Comet](https://pplx.ai/leosimon)**.
|
||||
|
||||
[](https://badge.fury.io/js/opendia)
|
||||
[](https://www.npmjs.com/package/opendia)
|
||||
[](https://github.com/aaronjmars/opendia/releases/latest)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
## 📺 See it in Action
|
||||
@@ -15,7 +18,7 @@
|
||||
OpenDia lets AI models control your browser automatically. **The key advantage? It leverages everything you already have**—your logged-in accounts, saved passwords, cookies, wallets, and browsing history. No need to start from scratch or switch contexts.
|
||||
|
||||
**🔑 Use Your Existing Digital Life:**
|
||||
- ✅ **Logged-in accounts**: Post to Twitter / X, LinkedIn, Facebook with your existing sessions
|
||||
- ✅ **Logged-in accounts**: Post to Twitter/X, LinkedIn, Facebook with your existing sessions
|
||||
- ✅ **Browser data**: Access your bookmarks, history, and saved passwords
|
||||
- ✅ **Extensions & wallets**: Use MetaMask, password managers, or any installed extensions
|
||||
- ✅ **Cookies & sessions**: Stay authenticated across all your favorite sites
|
||||
@@ -30,16 +33,16 @@ OpenDia lets AI models control your browser automatically. **The key advantage?
|
||||
|
||||
## 🌐 Browser Support
|
||||
|
||||
Works with **any Chromium-based browser**:
|
||||
Works with **Chrome, Firefox, and any Chromium-based browser**:
|
||||
- ✅ **Google Chrome**
|
||||
- ✅ **Arc Browser**
|
||||
- ✅ **Arc**
|
||||
- ✅ **Mozilla Firefox**
|
||||
- ✅ **Microsoft Edge**
|
||||
- ✅ **Brave Browser**
|
||||
- ✅ **Brave**
|
||||
- ✅ **Opera**
|
||||
- ✅ **Vivaldi**
|
||||
- ✅ **Any Chromium variant**
|
||||
- ✅ **Any Chromium based browser**
|
||||
|
||||
Perfect for **Cursor users** who want to automate their local testing and development workflows!
|
||||
Also perfect for **Cursor users** who want to automate their local testing and development workflows!
|
||||
|
||||
## 🎬 What You Can Do
|
||||
|
||||
@@ -69,21 +72,42 @@ Perfect for **Cursor users** who want to automate their local testing and develo
|
||||
- **"Monitor this webpage and notify me when the content changes"**
|
||||
- **"Automatically bookmark interesting articles I'm reading"**
|
||||
|
||||
### 🎨 Visual Customization & Fun
|
||||
- **"Apply a cyberpunk theme to this documentation site to make it more engaging"**
|
||||
- **"Make this page dark mode with green text for late-night reading"**
|
||||
- **"Add rainbow party effects to celebrate finishing this project"**
|
||||
- **"Transform this boring form with a retro 80s theme while I fill it out"**
|
||||
- **"Use high contrast styling so I can read this better"**
|
||||
|
||||
## ⚡ Quick Start
|
||||
|
||||
### 1. Start the Server
|
||||
```bash
|
||||
npx opendia
|
||||
```
|
||||
### 1. Install the Browser Extension
|
||||
|
||||
### 2. Install the Browser Extension
|
||||
1. Download from [releases](https://github.com/aaronjmars/opendia/releases)
|
||||
2. Go to `chrome://extensions/` (or your browser's extension page)
|
||||
3. Enable "Developer mode"
|
||||
4. Click "Load unpacked" and select the extension folder
|
||||
**For Chrome/Chromium browsers:**
|
||||
1. Download `opendia-chrome-1.1.0.zip` from [releases](https://github.com/aaronjmars/opendia/releases)
|
||||
2. Extract the zip file to a folder
|
||||
3. Go to `chrome://extensions/` (or your browser's extension page)
|
||||
4. Enable "Developer mode"
|
||||
5. Click "Load unpacked" and select the extracted folder
|
||||
|
||||
### 3. Connect to Your AI
|
||||
**For Claude Desktop**, add to your configuration:
|
||||
**For Firefox:**
|
||||
1. Download `opendia-firefox-1.1.0.zip` from [releases](https://github.com/aaronjmars/opendia/releases)
|
||||
2. Extract the zip file to a folder
|
||||
3. Go to `about:debugging#/runtime/this-firefox`
|
||||
4. Click "Load Temporary Add-on..."
|
||||
5. Select the `manifest.json` file from the extracted folder
|
||||
|
||||
> **Note**: Firefox extensions are loaded as temporary add-ons and will be removed when Firefox restarts. This is a Firefox limitation for unsigned extensions.
|
||||
|
||||
### 2. Connect to Your AI
|
||||
|
||||
**Option 1: Double-click Installation (Recommended)**
|
||||
1. Download the `opendia.dxt` file from [releases](https://github.com/aaronjmars/opendia/releases)
|
||||
2. Double-click the `.dxt` file to install automatically
|
||||
3. The MCP will be added to your Claude Desktop configuration
|
||||
|
||||
**Option 2: Manual Configuration**
|
||||
Add to your Claude Desktop configuration:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
@@ -97,9 +121,67 @@ npx opendia
|
||||
|
||||
**For Cursor or other AI tools**, use the same configuration or follow their specific setup instructions.
|
||||
|
||||
## Usage Modes
|
||||
|
||||
### Local Mode (Default)
|
||||
```bash
|
||||
npx opendia
|
||||
```
|
||||
- Chrome extension: ws://localhost:5555 (auto-discovery enabled)
|
||||
- Claude Desktop: stdio (existing config)
|
||||
- Local SSE: http://localhost:5556/sse
|
||||
|
||||
### Port Configuration
|
||||
```bash
|
||||
# Use custom ports
|
||||
npx opendia --port=6000 # Uses 6000 (WebSocket) + 6001 (HTTP)
|
||||
npx opendia --ws-port=5555 --http-port=5556 # Specify individually
|
||||
|
||||
# Handle port conflicts
|
||||
# Note: Existing OpenDia processes are automatically terminated on startup
|
||||
```
|
||||
|
||||
### Auto-Tunnel Mode
|
||||
```bash
|
||||
npx opendia --tunnel
|
||||
```
|
||||
- Automatically creates ngrok tunnel
|
||||
- Copy URL for ChatGPT/online AI services
|
||||
- Local functionality preserved
|
||||
|
||||
**Note**: For auto-tunneling to work, you need ngrok installed:
|
||||
|
||||
**macOS:**
|
||||
```bash
|
||||
brew install ngrok
|
||||
```
|
||||
|
||||
**Windows:**
|
||||
```bash
|
||||
# Using Chocolatey
|
||||
choco install ngrok
|
||||
|
||||
# Or download from https://ngrok.com/download
|
||||
```
|
||||
|
||||
**Linux:**
|
||||
```bash
|
||||
# Ubuntu/Debian
|
||||
curl -s https://ngrok-agent.s3.amazonaws.com/ngrok.asc | sudo tee /etc/apt/trusted.gpg.d/ngrok.asc >/dev/null
|
||||
echo "deb https://ngrok-agent.s3.amazonaws.com buster main" | sudo tee /etc/apt/sources.list.d/ngrok.list
|
||||
sudo apt update && sudo apt install ngrok
|
||||
|
||||
# Or download from https://ngrok.com/download
|
||||
```
|
||||
|
||||
Then get your free authtoken from https://dashboard.ngrok.com/get-started/your-authtoken and run:
|
||||
```bash
|
||||
ngrok config add-authtoken YOUR_TOKEN_HERE
|
||||
```
|
||||
|
||||
## 🛠️ Capabilities
|
||||
|
||||
OpenDia gives AI models **17 powerful browser tools**:
|
||||
OpenDia gives AI models **18 powerful browser tools**:
|
||||
|
||||
### 🎯 Smart Page Understanding
|
||||
- **Analyze any webpage** - AI automatically finds buttons, forms, and interactive elements
|
||||
@@ -127,6 +209,13 @@ OpenDia gives AI models **17 powerful browser tools**:
|
||||
- **Natural interactions** - Mimics human behavior to avoid triggering security measures
|
||||
- **Reliable automation** - Works consistently even on sites that block typical automation tools
|
||||
|
||||
### 🎨 Page Styling & Customization
|
||||
- **Transform any website** - Apply fun themes, custom colors, and visual effects
|
||||
- **Preset themes** - Dark hacker, retro 80s, rainbow party, minimalist zen, and more
|
||||
- **AI mood styling** - Describe a mood and get matching visual design
|
||||
- **Interactive effects** - Matrix rain, floating particles, neon glow, and cursor trails
|
||||
- **Accessibility themes** - High contrast and readable designs for better visibility
|
||||
|
||||
## 💬 Example Prompts to Try
|
||||
|
||||
Once everything is set up, try asking your AI:
|
||||
@@ -149,6 +238,15 @@ Once everything is set up, try asking your AI:
|
||||
**Personal Assistant:**
|
||||
> *"Find that GitHub repo I was looking at yesterday about React components and bookmark it for later"*
|
||||
|
||||
**Page Styling & Fun:**
|
||||
> *"Apply a dark hacker theme to this page to make it look more interesting"*
|
||||
|
||||
> *"Make this boring documentation page feel like a cozy coffee shop"*
|
||||
|
||||
> *"Add some matrix rain effects to this page for 30 seconds for a cool screenshot"*
|
||||
|
||||
> *"Transform this page with a high contrast theme for better readability"*
|
||||
|
||||
## 🏗️ How It Works
|
||||
|
||||
```mermaid
|
||||
@@ -188,8 +286,10 @@ cd opendia-mcp
|
||||
npm install
|
||||
npm start
|
||||
|
||||
# Load extension in your browser
|
||||
# Go to chrome://extensions/ → Developer mode → Load unpacked: ./opendia-extension
|
||||
# Load extension in your browser
|
||||
# Chrome: Go to chrome://extensions/ → Developer mode → Load unpacked: ./opendia-extension/dist/chrome
|
||||
# Firefox: Go to about:debugging#/runtime/this-firefox → Load Temporary Add-on → ./opendia-extension/dist/firefox/manifest.json
|
||||
# Extension will auto-connect to server on localhost:5555
|
||||
```
|
||||
|
||||
### Ways to Contribute
|
||||
|
||||
401
build-dxt.sh
Executable file
@@ -0,0 +1,401 @@
|
||||
#!/bin/bash
|
||||
|
||||
# OpenDia DXT Build Script - Fixed Version
|
||||
# Run this from the OpenDia project root directory
|
||||
|
||||
set -e # Exit on any error
|
||||
|
||||
echo "🚀 Building OpenDia DXT package..."
|
||||
|
||||
# Check if we're in the right directory
|
||||
if [ ! -f "opendia-mcp/package.json" ]; then
|
||||
echo "❌ Error: Please run this script from the OpenDia project root directory"
|
||||
echo " Expected to find: opendia-mcp/package.json"
|
||||
echo " Current directory: $(pwd)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ ! -d "opendia-extension" ]; then
|
||||
echo "❌ Error: opendia-extension directory not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Clean and create dist directory
|
||||
echo "🧹 Cleaning previous build..."
|
||||
rm -rf dist
|
||||
mkdir -p dist/opendia-dxt
|
||||
|
||||
echo "📦 Setting up package..."
|
||||
|
||||
# Copy server files
|
||||
cp opendia-mcp/server.js dist/opendia-dxt/
|
||||
|
||||
# Create optimized package.json for DXT
|
||||
cat > dist/opendia-dxt/package.json << 'EOF'
|
||||
{
|
||||
"name": "opendia",
|
||||
"version": "1.1.0",
|
||||
"description": "🎯 OpenDia - The open alternative to Dia. Connect your browser to AI models with anti-detection bypass for Twitter/X, LinkedIn, Facebook",
|
||||
"main": "server.js",
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
},
|
||||
"keywords": [
|
||||
"mcp",
|
||||
"browser",
|
||||
"automation",
|
||||
"ai",
|
||||
"claude",
|
||||
"chrome",
|
||||
"firefox",
|
||||
"extension",
|
||||
"twitter",
|
||||
"linkedin",
|
||||
"facebook",
|
||||
"anti-detection",
|
||||
"dxt"
|
||||
],
|
||||
"author": "Aaron Elijah Mars <aaronjmars@proton.me>",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.2",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
}
|
||||
EOF
|
||||
|
||||
# Install dependencies
|
||||
echo "⬇️ Installing dependencies..."
|
||||
cd dist/opendia-dxt
|
||||
npm install --production --silent
|
||||
cd ../..
|
||||
|
||||
# Copy browser extension
|
||||
echo "🌐 Copying browser extension..."
|
||||
cp -r opendia-extension dist/opendia-dxt/extension
|
||||
|
||||
# Copy logo/icon files for DXT - try multiple sources
|
||||
echo "🎨 Copying logo files..."
|
||||
LOGO_COPIED=false
|
||||
|
||||
# Try different icon files from the extension
|
||||
for icon_file in "icon-128.png" "icon-48.png" "icon-32.png" "icon-16.png" "icon.png"; do
|
||||
if [ -f "opendia-extension/$icon_file" ]; then
|
||||
cp "opendia-extension/$icon_file" dist/opendia-dxt/icon.png
|
||||
echo "✅ Logo copied from extension/$icon_file"
|
||||
LOGO_COPIED=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# If no extension icon found, check root directory
|
||||
if [ "$LOGO_COPIED" = false ]; then
|
||||
for icon_file in "icon.png" "logo.png" "opendia.png"; do
|
||||
if [ -f "$icon_file" ]; then
|
||||
cp "$icon_file" dist/opendia-dxt/icon.png
|
||||
echo "✅ Logo copied from $icon_file"
|
||||
LOGO_COPIED=true
|
||||
break
|
||||
fi
|
||||
done
|
||||
fi
|
||||
|
||||
# Create a simple placeholder if no icon found
|
||||
if [ "$LOGO_COPIED" = false ]; then
|
||||
echo "⚠️ No logo file found, you may need to add icon.png manually to dist/opendia-dxt/"
|
||||
fi
|
||||
|
||||
# Create DXT manifest - CORRECT FORMAT BASED ON WORKING EXAMPLES
|
||||
echo "📋 Creating DXT manifest..."
|
||||
cat > dist/opendia-dxt/manifest.json << 'EOF'
|
||||
{
|
||||
"dxt_version": "0.1",
|
||||
"name": "opendia",
|
||||
"display_name": "OpenDia - Browser Automation",
|
||||
"version": "1.1.0",
|
||||
"description": "🎯 OpenDia - The open alternative to Dia. Connect your browser to AI models with anti-detection bypass for Twitter/X, LinkedIn, Facebook + universal automation",
|
||||
"author": {
|
||||
"name": "Aaron Elijah Mars",
|
||||
"email": "aaronjmars@proton.me",
|
||||
"url": "https://github.com/aaronjmars/opendia"
|
||||
},
|
||||
"homepage": "https://github.com/aaronjmars/opendia",
|
||||
"license": "MIT",
|
||||
"keywords": ["browser", "automation", "mcp", "ai", "claude", "chrome", "firefox", "extension", "twitter", "linkedin", "facebook", "anti-detection"],
|
||||
"icon": "icon.png",
|
||||
|
||||
"server": {
|
||||
"type": "node",
|
||||
"entry_point": "server.js",
|
||||
"mcp_config": {
|
||||
"command": "node",
|
||||
"args": ["${__dirname}/server.js"],
|
||||
"env": {
|
||||
"NODE_ENV": "production",
|
||||
"WS_PORT": "${user_config.ws_port}",
|
||||
"HTTP_PORT": "${user_config.http_port}",
|
||||
"ENABLE_TUNNEL": "${user_config.enable_tunnel}",
|
||||
"SAFETY_MODE": "${user_config.safety_mode}"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
"user_config": {
|
||||
"ws_port": {
|
||||
"type": "number",
|
||||
"title": "WebSocket Port",
|
||||
"description": "Port for Chrome/Firefox extension connection",
|
||||
"default": 5555,
|
||||
"min": 1024,
|
||||
"max": 65535
|
||||
},
|
||||
"http_port": {
|
||||
"type": "number",
|
||||
"title": "HTTP Port",
|
||||
"description": "Port for HTTP/SSE server",
|
||||
"default": 5556,
|
||||
"min": 1024,
|
||||
"max": 65535
|
||||
},
|
||||
"enable_tunnel": {
|
||||
"type": "boolean",
|
||||
"title": "Auto-Tunnel",
|
||||
"description": "Automatically create ngrok tunnel for online AI access (requires ngrok)",
|
||||
"default": false
|
||||
},
|
||||
"safety_mode": {
|
||||
"type": "boolean",
|
||||
"title": "Safety Mode",
|
||||
"description": "Block write/edit tools (element_click, element_fill) by default",
|
||||
"default": false
|
||||
}
|
||||
},
|
||||
|
||||
"tools": [
|
||||
{
|
||||
"name": "page_analyze",
|
||||
"description": "🔍 Analyze any tab without switching! Two-phase intelligent page analysis"
|
||||
},
|
||||
{
|
||||
"name": "page_extract_content",
|
||||
"description": "📄 Extract content from any tab without switching!"
|
||||
},
|
||||
{
|
||||
"name": "element_click",
|
||||
"description": "🖱️ Click elements with anti-detection bypass for social platforms"
|
||||
},
|
||||
{
|
||||
"name": "element_fill",
|
||||
"description": "✏️ Fill forms with anti-detection bypass for Twitter/X, LinkedIn, Facebook"
|
||||
},
|
||||
{
|
||||
"name": "page_navigate",
|
||||
"description": "🧭 Navigate to URLs with wait conditions"
|
||||
},
|
||||
{
|
||||
"name": "page_wait_for",
|
||||
"description": "⏳ Wait for elements or conditions"
|
||||
},
|
||||
{
|
||||
"name": "tab_create",
|
||||
"description": "📱 Create single or multiple tabs with batch support"
|
||||
},
|
||||
{
|
||||
"name": "tab_close",
|
||||
"description": "❌ Close specific tab(s) by ID"
|
||||
},
|
||||
{
|
||||
"name": "tab_list",
|
||||
"description": "📋 Get list of all open tabs with IDs"
|
||||
},
|
||||
{
|
||||
"name": "tab_switch",
|
||||
"description": "🔄 Switch to specific tab by ID"
|
||||
},
|
||||
{
|
||||
"name": "element_get_state",
|
||||
"description": "🔍 Get detailed element state information"
|
||||
},
|
||||
{
|
||||
"name": "get_bookmarks",
|
||||
"description": "📚 Get all bookmarks or search for specific ones"
|
||||
},
|
||||
{
|
||||
"name": "add_bookmark",
|
||||
"description": "➕ Add new bookmark"
|
||||
},
|
||||
{
|
||||
"name": "get_history",
|
||||
"description": "🕒 Search browser history with comprehensive filters"
|
||||
},
|
||||
{
|
||||
"name": "get_selected_text",
|
||||
"description": "📝 Get selected text from any tab"
|
||||
},
|
||||
{
|
||||
"name": "page_scroll",
|
||||
"description": "📜 Scroll any tab without switching"
|
||||
},
|
||||
{
|
||||
"name": "get_page_links",
|
||||
"description": "🔗 Get all hyperlinks with smart filtering"
|
||||
},
|
||||
{
|
||||
"name": "page_style",
|
||||
"description": "🎨 Transform page appearance with themes and effects"
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
# Validate JSON syntax
|
||||
echo "🔍 Validating manifest.json..."
|
||||
if ! python3 -m json.tool dist/opendia-dxt/manifest.json > /dev/null 2>&1; then
|
||||
echo "❌ Error: Invalid JSON in manifest.json"
|
||||
exit 1
|
||||
fi
|
||||
echo "✅ Manifest JSON is valid"
|
||||
|
||||
# Copy documentation
|
||||
echo "📝 Adding documentation..."
|
||||
cp README.md dist/opendia-dxt/ 2>/dev/null || echo "⚠️ README.md not found, skipping"
|
||||
cp LICENSE dist/opendia-dxt/ 2>/dev/null || echo "⚠️ LICENSE not found, skipping"
|
||||
|
||||
# Create extension installation guide
|
||||
cat > dist/opendia-dxt/EXTENSION_INSTALL.md << 'EOF'
|
||||
# OpenDia Browser Extension Installation
|
||||
|
||||
**🔗 Official Repository:** https://github.com/aaronjmars/opendia
|
||||
|
||||
## Complete Installation Guide
|
||||
|
||||
### Step 1: Install the DXT Package (Already Done!)
|
||||
✅ You've successfully installed the OpenDia DXT package in Claude Desktop
|
||||
|
||||
### Step 2: Install Browser Extension
|
||||
|
||||
**📦 Get Latest Extension:**
|
||||
Download the latest extension from: https://github.com/aaronjmars/opendia/releases
|
||||
|
||||
**Or use the included extension in this DXT package:**
|
||||
|
||||
#### For Chrome/Chromium Browsers
|
||||
|
||||
1. **Enable Developer Mode**
|
||||
- Go to `chrome://extensions/`
|
||||
- Toggle "Developer mode" in the top right
|
||||
|
||||
2. **Install Extension**
|
||||
- Click "Load unpacked"
|
||||
- Select the `extension/` folder from this DXT package
|
||||
- Extension should appear in your extensions list with OpenDia icon
|
||||
|
||||
#### For Firefox
|
||||
|
||||
1. **Load Temporary Add-on**
|
||||
- Go to `about:debugging#/runtime/this-firefox`
|
||||
- Click "Load Temporary Add-on..."
|
||||
- Select the `manifest-firefox.json` file from the `extension/` folder
|
||||
|
||||
> **Firefox Note**: Extensions are loaded as temporary add-ons and will be removed when Firefox restarts. For permanent installation, use the signed extension from GitHub releases.
|
||||
|
||||
### Step 3: Verify Everything Works
|
||||
|
||||
1. **Check Extension Status**
|
||||
- Click the OpenDia extension icon in your browser
|
||||
- Should show "Connected to MCP server"
|
||||
- Green status indicator means ready to use
|
||||
|
||||
2. **Test in Claude Desktop**
|
||||
- Ask Claude: "List my open browser tabs"
|
||||
- Should return your current tabs if working correctly
|
||||
|
||||
## Supported Browsers
|
||||
- ✅ **Mozilla Firefox** (Manifest V2)
|
||||
- ✅ **Google Chrome** (Manifest V3)
|
||||
- ✅ **Arc Browser, Microsoft Edge, Brave Browser, Opera**
|
||||
- ✅ **Any Chromium-based browser**
|
||||
|
||||
## Key Features
|
||||
🎯 **Anti-detection bypass** for Twitter/X, LinkedIn, Facebook
|
||||
📱 **Smart automation** and intelligent page analysis
|
||||
🔧 **Form filling** with enhanced compatibility
|
||||
📊 **Multi-tab workflows** and background operations
|
||||
🎨 **Page styling** and visual customization
|
||||
🔒 **Privacy-first** - everything runs locally
|
||||
|
||||
## Getting Help
|
||||
|
||||
- **📖 Full Documentation:** https://github.com/aaronjmars/opendia
|
||||
- **🐛 Report Issues:** https://github.com/aaronjmars/opendia/issues
|
||||
- **💬 Discussions:** https://github.com/aaronjmars/opendia/discussions
|
||||
|
||||
## What's Next?
|
||||
|
||||
Try these example prompts in Claude Desktop:
|
||||
- "List my open browser tabs"
|
||||
- "Analyze the content of my current tab"
|
||||
- "Apply a dark theme to this webpage"
|
||||
- "Extract the main article text from this page"
|
||||
|
||||
**🚀 Ready to supercharge your browser with AI!**
|
||||
EOF
|
||||
|
||||
# Verify structure before zipping
|
||||
echo "🔍 Verifying DXT structure..."
|
||||
echo "📋 Files in DXT directory:"
|
||||
ls -la dist/opendia-dxt/
|
||||
|
||||
# Check if icon exists and show its details
|
||||
if [ -f "dist/opendia-dxt/icon.png" ]; then
|
||||
echo "✅ Icon file found:"
|
||||
file dist/opendia-dxt/icon.png
|
||||
ls -lh dist/opendia-dxt/icon.png
|
||||
else
|
||||
echo "❌ Icon file missing!"
|
||||
fi
|
||||
|
||||
# Verify manifest.json exists and is at root level
|
||||
if [ ! -f "dist/opendia-dxt/manifest.json" ]; then
|
||||
echo "❌ Error: manifest.json not found!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "✅ Structure verified"
|
||||
|
||||
# Create the DXT archive - CRITICAL: ZIP from inside the directory
|
||||
echo "🗜️ Creating DXT archive..."
|
||||
cd dist/opendia-dxt
|
||||
zip -r ../opendia.dxt . -q
|
||||
cd ../..
|
||||
|
||||
# Verify the DXT file structure
|
||||
echo "🔍 Verifying DXT file contents..."
|
||||
if ! unzip -l dist/opendia.dxt | grep -q "manifest.json"; then
|
||||
echo "❌ Error: manifest.json not found in DXT file!"
|
||||
echo "DXT contents:"
|
||||
unzip -l dist/opendia.dxt
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get file size
|
||||
DXT_SIZE=$(du -h dist/opendia.dxt | cut -f1)
|
||||
|
||||
echo ""
|
||||
echo "✅ DXT package created successfully!"
|
||||
echo "📦 File: dist/opendia.dxt"
|
||||
echo "💾 Size: $DXT_SIZE"
|
||||
echo ""
|
||||
echo "📋 DXT Contents:"
|
||||
unzip -l dist/opendia.dxt | head -10
|
||||
echo ""
|
||||
echo "🚀 Installation:"
|
||||
echo "1. Double-click the .dxt file"
|
||||
echo "2. Or: Claude Desktop Settings → Extensions → Install Extension"
|
||||
echo "3. Install Chrome/Firefox extension from extension/ folder"
|
||||
echo ""
|
||||
echo "🎯 Features ready: Anti-detection bypass + universal automation"
|
||||
2
opendia-extension/.gitignore
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
node_modules/
|
||||
dist/
|
||||
312
opendia-extension/README.md
Normal file
@@ -0,0 +1,312 @@
|
||||
# OpenDia Cross-Browser Extension
|
||||
|
||||
A dual-manifest browser extension supporting both Chrome MV3 and Firefox MV2 for comprehensive browser automation through the Model Context Protocol (MCP).
|
||||
|
||||
## 🌐 Browser Support
|
||||
|
||||
| Browser | Manifest | Background | Connection | Store |
|
||||
|---------|----------|------------|------------|-------|
|
||||
| Chrome | V3 | Service Worker | Temporary | Chrome Web Store |
|
||||
| Firefox | V2 | Background Page | Persistent | Firefox Add-ons |
|
||||
| Edge | V3 | Service Worker | Temporary | Microsoft Store |
|
||||
| Safari | - | - | - | Coming Soon |
|
||||
|
||||
## 🚀 Quick Start
|
||||
|
||||
### Development Setup
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Build for all browsers
|
||||
npm run build
|
||||
|
||||
# Build for specific browser
|
||||
npm run build:chrome
|
||||
npm run build:firefox
|
||||
|
||||
# Create distribution packages
|
||||
npm run package:chrome
|
||||
npm run package:firefox
|
||||
|
||||
# Test builds
|
||||
node test-extension.js
|
||||
```
|
||||
|
||||
### Installation
|
||||
|
||||
#### Chrome/Edge/Brave
|
||||
1. Build the extension: `npm run build:chrome`
|
||||
2. Open `chrome://extensions/` (or equivalent)
|
||||
3. Enable "Developer mode"
|
||||
4. Click "Load unpacked" and select `dist/chrome`
|
||||
|
||||
#### Firefox
|
||||
1. Build the extension: `npm run build:firefox`
|
||||
2. Open `about:debugging#/runtime/this-firefox`
|
||||
3. Click "Load Temporary Add-on"
|
||||
4. Select any file in `dist/firefox` directory
|
||||
|
||||
## 🏗️ Architecture
|
||||
|
||||
### Dual-Manifest Strategy
|
||||
|
||||
OpenDia uses a dual-manifest approach to maximize browser compatibility:
|
||||
|
||||
- **Chrome MV3**: Required for Chrome Web Store, uses service workers
|
||||
- **Firefox MV2**: Enhanced capabilities, persistent background pages
|
||||
|
||||
### Connection Management
|
||||
|
||||
```javascript
|
||||
// Chrome MV3: Temporary connections
|
||||
class ServiceWorkerManager {
|
||||
async ensureConnection() {
|
||||
// Create fresh connection for each operation
|
||||
await this.createTemporaryConnection();
|
||||
}
|
||||
}
|
||||
|
||||
// Firefox MV2: Persistent connections
|
||||
class BackgroundPageManager {
|
||||
constructor() {
|
||||
this.persistentSocket = null;
|
||||
this.setupPersistentConnection();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Cross-Browser Compatibility
|
||||
|
||||
The extension uses WebExtension polyfill for consistent API usage:
|
||||
|
||||
```javascript
|
||||
// Polyfill setup
|
||||
if (typeof browser === 'undefined' && typeof chrome !== 'undefined') {
|
||||
globalThis.browser = chrome;
|
||||
}
|
||||
|
||||
// Unified API usage
|
||||
const tabs = await browser.tabs.query({active: true, currentWindow: true});
|
||||
```
|
||||
|
||||
## 🔧 Build System
|
||||
|
||||
### Build Configuration
|
||||
|
||||
The build system creates browser-specific packages:
|
||||
|
||||
```javascript
|
||||
// build.js
|
||||
async function buildForBrowser(browser) {
|
||||
// Copy common files
|
||||
await fs.copy('src', path.join(buildDir, 'src'));
|
||||
|
||||
// Copy browser-specific manifest
|
||||
await fs.copy(`manifest-${browser}.json`, path.join(buildDir, 'manifest.json'));
|
||||
|
||||
// Copy WebExtension polyfill
|
||||
await fs.copy('node_modules/webextension-polyfill/dist/browser-polyfill.min.js',
|
||||
path.join(buildDir, 'src/polyfill/browser-polyfill.min.js'));
|
||||
}
|
||||
```
|
||||
|
||||
### Build Validation
|
||||
|
||||
```bash
|
||||
# Validate all builds
|
||||
npm run build && node build.js validate
|
||||
|
||||
# Check specific browser
|
||||
node build.js validate chrome
|
||||
node build.js validate firefox
|
||||
```
|
||||
|
||||
## 🧪 Testing
|
||||
|
||||
### Automated Testing
|
||||
|
||||
```bash
|
||||
# Run comprehensive tests
|
||||
node test-extension.js
|
||||
|
||||
# Test specific components
|
||||
node test-extension.js --manifest
|
||||
node test-extension.js --background
|
||||
node test-extension.js --content
|
||||
```
|
||||
|
||||
### Manual Testing
|
||||
|
||||
1. **Connection Test**: Extension popup should show "Connected to MCP server"
|
||||
2. **Background Tab Test**: Use `tab_list` with `check_content_script: true`
|
||||
3. **Cross-Browser Test**: Same functionality on both Chrome and Firefox
|
||||
|
||||
## 📁 Directory Structure
|
||||
|
||||
```
|
||||
opendia-extension/
|
||||
├── src/
|
||||
│ ├── background/
|
||||
│ │ └── background.js # Cross-browser background script
|
||||
│ ├── content/
|
||||
│ │ └── content.js # Content script with polyfill
|
||||
│ ├── popup/
|
||||
│ │ ├── popup.html # Extension popup
|
||||
│ │ └── popup.js # Popup logic with browser APIs
|
||||
│ └── polyfill/
|
||||
│ └── browser-polyfill.min.js # WebExtension polyfill
|
||||
├── icons/ # Extension icons
|
||||
├── dist/ # Build output
|
||||
│ ├── chrome/ # Chrome MV3 build
|
||||
│ ├── firefox/ # Firefox MV2 build
|
||||
│ ├── opendia-chrome.zip # Chrome package
|
||||
│ └── opendia-firefox.zip # Firefox package
|
||||
├── manifest-chrome.json # Chrome MV3 manifest
|
||||
├── manifest-firefox.json # Firefox MV2 manifest
|
||||
├── build.js # Build system
|
||||
├── test-extension.js # Test suite
|
||||
└── package.json # Dependencies and scripts
|
||||
```
|
||||
|
||||
## 🔗 Integration
|
||||
|
||||
### MCP Server Connection
|
||||
|
||||
The extension automatically discovers and connects to the MCP server:
|
||||
|
||||
```javascript
|
||||
// Port discovery
|
||||
const commonPorts = [5556, 5557, 5558, 3001, 6001, 6002, 6003];
|
||||
const response = await fetch(`http://localhost:${port}/ports`);
|
||||
const portInfo = await response.json();
|
||||
```
|
||||
|
||||
### Background Tab Support
|
||||
|
||||
All tools support background tab targeting:
|
||||
|
||||
```javascript
|
||||
// Target specific tab
|
||||
await browser.tabs.sendMessage(tabId, {
|
||||
action: 'page_analyze',
|
||||
data: { intent_hint: 'login', tab_id: 12345 }
|
||||
});
|
||||
```
|
||||
|
||||
## 🛠️ Development
|
||||
|
||||
### Adding New Features
|
||||
|
||||
1. **Cross-Browser First**: Use `browser` API throughout
|
||||
2. **Connection Aware**: Handle both temporary and persistent connections
|
||||
3. **Test Both Browsers**: Validate on Chrome and Firefox
|
||||
4. **Update Both Manifests**: Ensure compatibility
|
||||
|
||||
### Browser-Specific Handling
|
||||
|
||||
```javascript
|
||||
// Detect browser environment
|
||||
const browserInfo = {
|
||||
isFirefox: typeof browser !== 'undefined' && browser.runtime.getManifest().applications?.gecko,
|
||||
isChrome: typeof chrome !== 'undefined' && !browser.runtime.getManifest().applications?.gecko,
|
||||
isServiceWorker: typeof importScripts === 'function',
|
||||
manifestVersion: browser.runtime.getManifest().manifest_version
|
||||
};
|
||||
|
||||
// Handle differences
|
||||
if (browserInfo.isServiceWorker) {
|
||||
// Chrome MV3 service worker behavior
|
||||
} else {
|
||||
// Firefox MV2 background page behavior
|
||||
}
|
||||
```
|
||||
|
||||
### API Compatibility
|
||||
|
||||
| Feature | Chrome MV3 | Firefox MV2 | Implementation |
|
||||
|---------|------------|-------------|----------------|
|
||||
| Background | Service Worker | Background Page | Connection Manager |
|
||||
| Script Injection | `browser.scripting` | `browser.tabs.executeScript` | Feature detection |
|
||||
| Persistent State | ❌ | ✅ | Browser-specific storage |
|
||||
| WebRequest Blocking | Limited | Full | Firefox advantage |
|
||||
| Store Distribution | Required | Optional | Both supported |
|
||||
|
||||
## 🚀 Distribution
|
||||
|
||||
### Chrome Web Store
|
||||
|
||||
```bash
|
||||
# Build and package
|
||||
npm run package:chrome
|
||||
|
||||
# Upload dist/opendia-chrome.zip to Chrome Web Store
|
||||
```
|
||||
|
||||
### Firefox Add-ons (AMO)
|
||||
|
||||
```bash
|
||||
# Build and package
|
||||
npm run package:firefox
|
||||
|
||||
# Upload dist/opendia-firefox.zip to addons.mozilla.org
|
||||
```
|
||||
|
||||
### GitHub Releases
|
||||
|
||||
```bash
|
||||
# Create both packages
|
||||
npm run package:chrome
|
||||
npm run package:firefox
|
||||
|
||||
# Upload both files to GitHub releases
|
||||
```
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
1. **Test Both Browsers**: Always test Chrome and Firefox
|
||||
2. **Use Browser APIs**: Avoid `chrome.*` direct usage
|
||||
3. **Update Both Manifests**: Keep manifests in sync
|
||||
4. **Validate Builds**: Run test suite before committing
|
||||
|
||||
## 📚 Resources
|
||||
|
||||
- [WebExtension API Documentation](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions)
|
||||
- [Chrome Extension MV3 Guide](https://developer.chrome.com/docs/extensions/mv3/)
|
||||
- [Firefox Extension Development](https://extensionworkshop.com/)
|
||||
- [WebExtension Polyfill](https://github.com/mozilla/webextension-polyfill)
|
||||
|
||||
## 🔧 Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
1. **Connection Fails**: Check MCP server is running (`npm start` in `opendia-mcp/`)
|
||||
2. **Chrome Service Worker**: Extensions may need manual restart in `chrome://extensions`
|
||||
3. **Firefox Temporary**: Extension reloads required after Firefox restart
|
||||
4. **Build Errors**: Ensure all dependencies installed (`npm install`)
|
||||
|
||||
### Debug Commands
|
||||
|
||||
```bash
|
||||
# Check server status
|
||||
curl http://localhost:5556/ping
|
||||
|
||||
# Validate builds
|
||||
node build.js validate
|
||||
|
||||
# Test extension compatibility
|
||||
node test-extension.js
|
||||
|
||||
# Check extension logs
|
||||
# Chrome: chrome://extensions -> OpenDia -> service worker
|
||||
# Firefox: about:debugging -> OpenDia -> Inspect
|
||||
```
|
||||
|
||||
## 🎯 Future Enhancements
|
||||
|
||||
- [ ] Safari extension support
|
||||
- [ ] Edge-specific optimizations
|
||||
- [ ] WebExtension Manifest V3 migration for Firefox
|
||||
- [ ] Enhanced anti-detection features
|
||||
- [ ] Performance optimizations for service workers
|
||||
193
opendia-extension/build.js
Normal file
@@ -0,0 +1,193 @@
|
||||
const fs = require('fs-extra');
|
||||
const path = require('path');
|
||||
|
||||
async function buildForBrowser(browser) {
|
||||
const buildDir = `dist/${browser}`;
|
||||
|
||||
console.log(`🔧 Building ${browser} extension...`);
|
||||
|
||||
// Clean and create build directory
|
||||
await fs.remove(buildDir);
|
||||
await fs.ensureDir(buildDir);
|
||||
|
||||
// Copy common files
|
||||
await fs.copy('src', path.join(buildDir, 'src'));
|
||||
await fs.copy('icons', path.join(buildDir, 'icons'));
|
||||
|
||||
// Copy logo files for animated popup
|
||||
if (await fs.pathExists('logo.mp4')) {
|
||||
await fs.copy('logo.mp4', path.join(buildDir, 'logo.mp4'));
|
||||
}
|
||||
if (await fs.pathExists('logo.webm')) {
|
||||
await fs.copy('logo.webm', path.join(buildDir, 'logo.webm'));
|
||||
}
|
||||
|
||||
// Copy browser-specific manifest
|
||||
await fs.copy(
|
||||
`manifest-${browser}.json`,
|
||||
path.join(buildDir, 'manifest.json')
|
||||
);
|
||||
|
||||
// Copy polyfill
|
||||
await fs.copy(
|
||||
'node_modules/webextension-polyfill/dist/browser-polyfill.min.js',
|
||||
path.join(buildDir, 'src/polyfill/browser-polyfill.min.js')
|
||||
);
|
||||
|
||||
// Browser-specific post-processing
|
||||
if (browser === 'chrome') {
|
||||
console.log('📦 Chrome MV3: Service worker mode enabled');
|
||||
// No additional processing needed for Chrome
|
||||
} else if (browser === 'firefox') {
|
||||
console.log('🦊 Firefox MV2: Background page mode enabled');
|
||||
// No additional processing needed for Firefox
|
||||
}
|
||||
|
||||
console.log(`✅ ${browser} extension built successfully in ${buildDir}`);
|
||||
}
|
||||
|
||||
async function buildAll() {
|
||||
console.log('🚀 Building extensions for all browsers...');
|
||||
|
||||
try {
|
||||
await buildForBrowser('chrome');
|
||||
await buildForBrowser('firefox');
|
||||
|
||||
console.log('🎉 All extensions built successfully!');
|
||||
console.log('');
|
||||
console.log('📁 Build outputs:');
|
||||
console.log(' Chrome MV3: dist/chrome/');
|
||||
console.log(' Firefox MV2: dist/firefox/');
|
||||
console.log('');
|
||||
console.log('🧪 Testing instructions:');
|
||||
console.log(' Chrome: Load dist/chrome in chrome://extensions (Developer mode)');
|
||||
console.log(' Firefox: Load dist/firefox in about:debugging#/runtime/this-firefox');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Build failed:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function createPackages() {
|
||||
console.log('📦 Creating distribution packages...');
|
||||
|
||||
const { execSync } = require('child_process');
|
||||
|
||||
try {
|
||||
// Create Chrome package
|
||||
console.log('Creating Chrome package...');
|
||||
execSync('cd dist/chrome && zip -r ../opendia-chrome.zip .', { stdio: 'inherit' });
|
||||
|
||||
// Create Firefox package (using web-ext if available)
|
||||
console.log('Creating Firefox package...');
|
||||
try {
|
||||
execSync('cd dist/firefox && web-ext build --overwrite-dest', { stdio: 'inherit' });
|
||||
console.log('✅ Firefox package created with web-ext');
|
||||
} catch (e) {
|
||||
// Fallback to zip if web-ext is not available
|
||||
console.log('⚠️ web-ext not available, using zip fallback');
|
||||
execSync('cd dist/firefox && zip -r ../opendia-firefox.zip .', { stdio: 'inherit' });
|
||||
}
|
||||
|
||||
console.log('📦 Distribution packages created:');
|
||||
console.log(' Chrome: dist/opendia-chrome.zip');
|
||||
console.log(' Firefox: dist/opendia-firefox.zip (or .xpi)');
|
||||
|
||||
} catch (error) {
|
||||
console.error('❌ Package creation failed:', error);
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
async function validateBuild(browser) {
|
||||
const buildDir = `dist/${browser}`;
|
||||
const manifestPath = path.join(buildDir, 'manifest.json');
|
||||
|
||||
console.log(`🔍 Validating ${browser} build...`);
|
||||
|
||||
try {
|
||||
// Check manifest exists and is valid JSON
|
||||
const manifest = await fs.readJson(manifestPath);
|
||||
|
||||
// Check required files exist
|
||||
const requiredFiles = [
|
||||
'src/background/background.js',
|
||||
'src/content/content.js',
|
||||
'src/popup/popup.html',
|
||||
'src/popup/popup.js',
|
||||
'src/polyfill/browser-polyfill.min.js'
|
||||
];
|
||||
|
||||
for (const file of requiredFiles) {
|
||||
const filePath = path.join(buildDir, file);
|
||||
if (!await fs.pathExists(filePath)) {
|
||||
throw new Error(`Missing required file: ${file}`);
|
||||
}
|
||||
}
|
||||
|
||||
// Browser-specific validation
|
||||
if (browser === 'chrome') {
|
||||
if (manifest.manifest_version !== 3) {
|
||||
throw new Error('Chrome build must use manifest version 3');
|
||||
}
|
||||
if (!manifest.background?.service_worker) {
|
||||
throw new Error('Chrome build must specify service_worker in background');
|
||||
}
|
||||
} else if (browser === 'firefox') {
|
||||
if (manifest.manifest_version !== 2) {
|
||||
throw new Error('Firefox build must use manifest version 2');
|
||||
}
|
||||
if (!manifest.background?.scripts) {
|
||||
throw new Error('Firefox build must specify scripts in background');
|
||||
}
|
||||
}
|
||||
|
||||
console.log(`✅ ${browser} build validation passed`);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ ${browser} build validation failed:`, error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function validateAllBuilds() {
|
||||
console.log('🔍 Validating all builds...');
|
||||
|
||||
const chromeValid = await validateBuild('chrome');
|
||||
const firefoxValid = await validateBuild('firefox');
|
||||
|
||||
if (chromeValid && firefoxValid) {
|
||||
console.log('✅ All builds validated successfully');
|
||||
return true;
|
||||
} else {
|
||||
console.error('❌ Build validation failed');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// CLI usage
|
||||
if (require.main === module) {
|
||||
const args = process.argv.slice(2);
|
||||
const command = args[0];
|
||||
|
||||
switch (command) {
|
||||
case 'chrome':
|
||||
buildForBrowser('chrome');
|
||||
break;
|
||||
case 'firefox':
|
||||
buildForBrowser('firefox');
|
||||
break;
|
||||
case 'validate':
|
||||
validateAllBuilds();
|
||||
break;
|
||||
case 'package':
|
||||
buildAll().then(() => createPackages());
|
||||
break;
|
||||
default:
|
||||
buildAll();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { buildForBrowser, buildAll, createPackages, validateBuild, validateAllBuilds };
|
||||
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
|
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 6.0 KiB After Width: | Height: | Size: 6.0 KiB |
46
opendia-extension/manifest-chrome.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "OpenDia",
|
||||
"version": "1.1.0",
|
||||
"description": "Connect your browser to AI models",
|
||||
"icons": {
|
||||
"16": "icons/icon-16.png",
|
||||
"32": "icons/icon-32.png",
|
||||
"48": "icons/icon-48.png",
|
||||
"128": "icons/icon-128.png"
|
||||
},
|
||||
"permissions": [
|
||||
"tabs",
|
||||
"activeTab",
|
||||
"storage",
|
||||
"scripting",
|
||||
"webNavigation",
|
||||
"notifications",
|
||||
"bookmarks",
|
||||
"history"
|
||||
],
|
||||
"host_permissions": [
|
||||
"<all_urls>"
|
||||
],
|
||||
"background": {
|
||||
"service_worker": "src/background/background.js",
|
||||
"type": "module"
|
||||
},
|
||||
"action": {
|
||||
"default_popup": "src/popup/popup.html",
|
||||
"default_title": "OpenDia"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["src/polyfill/browser-polyfill.min.js", "src/content/content.js"],
|
||||
"run_at": "document_idle"
|
||||
}
|
||||
],
|
||||
"web_accessible_resources": [
|
||||
{
|
||||
"resources": ["src/polyfill/browser-polyfill.min.js"],
|
||||
"matches": ["<all_urls>"]
|
||||
}
|
||||
]
|
||||
}
|
||||
51
opendia-extension/manifest-firefox.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "OpenDia",
|
||||
"version": "1.1.0",
|
||||
"description": "Connect your browser to AI models",
|
||||
"applications": {
|
||||
"gecko": {
|
||||
"id": "opendia@aaronjmars.com",
|
||||
"strict_min_version": "109.0"
|
||||
}
|
||||
},
|
||||
"icons": {
|
||||
"16": "icons/icon-16.png",
|
||||
"32": "icons/icon-32.png",
|
||||
"48": "icons/icon-48.png",
|
||||
"128": "icons/icon-128.png"
|
||||
},
|
||||
"permissions": [
|
||||
"tabs",
|
||||
"activeTab",
|
||||
"storage",
|
||||
"webNavigation",
|
||||
"notifications",
|
||||
"bookmarks",
|
||||
"history",
|
||||
"webRequest",
|
||||
"webRequestBlocking",
|
||||
"<all_urls>"
|
||||
],
|
||||
"background": {
|
||||
"scripts": [
|
||||
"src/polyfill/browser-polyfill.min.js",
|
||||
"src/background/background.js"
|
||||
],
|
||||
"persistent": false
|
||||
},
|
||||
"browser_action": {
|
||||
"default_popup": "src/popup/popup.html",
|
||||
"default_title": "OpenDia"
|
||||
},
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["src/polyfill/browser-polyfill.min.js", "src/content/content.js"],
|
||||
"run_at": "document_idle"
|
||||
}
|
||||
],
|
||||
"web_accessible_resources": [
|
||||
"src/polyfill/browser-polyfill.min.js"
|
||||
]
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"manifest_version": 3,
|
||||
"name": "OpenDia",
|
||||
"version": "1.0.0",
|
||||
"description": "Browser automation through Model Context Protocol",
|
||||
"version": "1.1.0",
|
||||
"description": "Connect your browser to AI models",
|
||||
"icons": {
|
||||
"16": "icon-16.png",
|
||||
"32": "icon-32.png",
|
||||
|
||||
4319
opendia-extension/package-lock.json
generated
Normal file
28
opendia-extension/package.json
Normal file
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "opendia-extension",
|
||||
"version": "1.1.0",
|
||||
"description": "Connect your browser to AI models",
|
||||
"scripts": {
|
||||
"build": "node build.js",
|
||||
"build:chrome": "node build.js chrome",
|
||||
"build:firefox": "node build.js firefox",
|
||||
"dev:chrome": "npm run build:chrome && echo 'Load dist/chrome in Chrome'",
|
||||
"dev:firefox": "npm run build:firefox && web-ext run --source-dir=dist/firefox",
|
||||
"package:chrome": "npm run build:chrome && cd dist/chrome && zip -r ../opendia-chrome.zip .",
|
||||
"package:firefox": "npm run build:firefox && cd dist/firefox && web-ext build --overwrite-dest"
|
||||
},
|
||||
"keywords": [
|
||||
"webextension",
|
||||
"mcp",
|
||||
"browser-automation"
|
||||
],
|
||||
"author": "Aaron J Mars",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"webextension-polyfill": "^0.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"fs-extra": "^11.3.0",
|
||||
"web-ext": "^8.8.0"
|
||||
}
|
||||
}
|
||||
BIN
opendia-extension/releases/opendia-chrome-1.0.6.zip
Normal file
BIN
opendia-extension/releases/opendia-firefox-1.0.6.zip
Normal file
2047
opendia-extension/src/background/background.js
Normal file
@@ -1,4 +1,15 @@
|
||||
// Enhanced Browser Automation Content Script with Anti-Detection
|
||||
// Import WebExtension polyfill for cross-browser compatibility
|
||||
if (typeof browser === 'undefined' && typeof chrome !== 'undefined') {
|
||||
globalThis.browser = chrome;
|
||||
}
|
||||
|
||||
// Prevent multiple injections - especially important for Firefox
|
||||
if (typeof window.OpenDiaContentScriptLoaded !== 'undefined') {
|
||||
console.log("OpenDia content script already loaded, skipping re-injection");
|
||||
} else {
|
||||
window.OpenDiaContentScriptLoaded = true;
|
||||
|
||||
console.log("OpenDia enhanced content script loaded");
|
||||
|
||||
// Enhanced Pattern Database with Twitter-First Priority
|
||||
@@ -228,7 +239,7 @@ class BrowserAutomation {
|
||||
}
|
||||
|
||||
setupMessageListener() {
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
this.handleMessage(message)
|
||||
.then(sendResponse)
|
||||
.catch((error) => {
|
||||
@@ -283,6 +294,13 @@ class BrowserAutomation {
|
||||
case "page_scroll":
|
||||
result = await this.scrollPage(data);
|
||||
break;
|
||||
case "page_style":
|
||||
result = await this.handlePageStyle(data);
|
||||
break;
|
||||
case "ping":
|
||||
// Health check for background tab content script readiness
|
||||
result = { status: "ready", timestamp: Date.now(), url: window.location.href };
|
||||
break;
|
||||
default:
|
||||
throw new Error(`Unknown action: ${action}`);
|
||||
}
|
||||
@@ -2503,7 +2521,377 @@ class BrowserAutomation {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// 🎨 Page Styling System
|
||||
async handlePageStyle(data) {
|
||||
const { mode, theme, background, text_color, font, font_size, mood, intensity, effect, duration, remember } = data;
|
||||
|
||||
// Remove existing custom styles
|
||||
const existingStyle = document.getElementById('opendia-custom-style');
|
||||
if (existingStyle) existingStyle.remove();
|
||||
|
||||
let css = '';
|
||||
let description = '';
|
||||
|
||||
try {
|
||||
switch (mode) {
|
||||
case 'preset':
|
||||
const themeData = THEME_PRESETS[theme];
|
||||
if (!themeData) throw new Error(`Unknown theme: ${theme}`);
|
||||
css = themeData.css;
|
||||
description = `Applied ${themeData.name} theme`;
|
||||
break;
|
||||
|
||||
case 'custom':
|
||||
css = this.buildCustomCSS({ background, text_color, font, font_size });
|
||||
description = 'Applied custom styling';
|
||||
break;
|
||||
|
||||
case 'ai_mood':
|
||||
css = this.generateMoodCSS(mood, intensity);
|
||||
description = `Applied AI-generated style for mood: "${mood}"`;
|
||||
break;
|
||||
|
||||
case 'effect':
|
||||
css = this.applyEffect(effect, duration);
|
||||
description = `Applied ${effect} effect for ${duration}s`;
|
||||
break;
|
||||
|
||||
case 'reset':
|
||||
// CSS already removed above
|
||||
description = 'Reset page to original styling';
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Error(`Unknown styling mode: ${mode}`);
|
||||
}
|
||||
|
||||
if (css) {
|
||||
const styleElement = document.createElement('style');
|
||||
styleElement.id = 'opendia-custom-style';
|
||||
styleElement.textContent = css;
|
||||
document.head.appendChild(styleElement);
|
||||
}
|
||||
|
||||
// Remember preference if requested
|
||||
if (remember && mode !== 'reset') {
|
||||
const domain = window.location.hostname;
|
||||
chrome.storage.local.set({ [`style_${domain}`]: { mode, theme, css } });
|
||||
}
|
||||
|
||||
return {
|
||||
success: true,
|
||||
description,
|
||||
applied_css: css.length,
|
||||
mode,
|
||||
theme: theme || 'custom',
|
||||
remember_enabled: remember,
|
||||
effect_duration: duration,
|
||||
mood,
|
||||
intensity
|
||||
};
|
||||
|
||||
} catch (error) {
|
||||
return {
|
||||
success: false,
|
||||
error: error.message,
|
||||
mode,
|
||||
theme,
|
||||
applied_css: 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
buildCustomCSS({ background, text_color, font, font_size }) {
|
||||
let css = '';
|
||||
|
||||
if (background || text_color || font || font_size) {
|
||||
css += '* { ';
|
||||
if (background) css += `background: ${background} !important; `;
|
||||
if (text_color) css += `color: ${text_color} !important; `;
|
||||
if (font) css += `font-family: ${font} !important; `;
|
||||
if (font_size) css += `font-size: ${font_size} !important; `;
|
||||
css += '}';
|
||||
}
|
||||
|
||||
return css;
|
||||
}
|
||||
|
||||
generateMoodCSS(mood, intensity) {
|
||||
const moodMap = {
|
||||
'cozy coffee shop': {
|
||||
background: '#2c1810',
|
||||
text: '#f4e4bc',
|
||||
accent: '#d4af37',
|
||||
font: 'Georgia, serif'
|
||||
},
|
||||
'energetic': {
|
||||
background: 'linear-gradient(45deg, #ff6b35, #f7931e)',
|
||||
text: '#ffffff',
|
||||
effects: 'animation: energyPulse 1s infinite;'
|
||||
},
|
||||
'calm ocean': {
|
||||
background: 'linear-gradient(to bottom, #87ceeb, #4682b4)',
|
||||
text: '#ffffff',
|
||||
effects: 'animation: gentleWave 4s ease-in-out infinite;'
|
||||
},
|
||||
'dark professional': {
|
||||
background: '#1a1a1a',
|
||||
text: '#e0e0e0',
|
||||
accent: '#0066cc'
|
||||
},
|
||||
'warm sunset': {
|
||||
background: 'linear-gradient(to bottom, #ff7e5f, #feb47b)',
|
||||
text: '#ffffff'
|
||||
}
|
||||
};
|
||||
|
||||
const style = moodMap[mood.toLowerCase()] || moodMap['cozy coffee shop'];
|
||||
return this.buildMoodCSS(style, intensity);
|
||||
}
|
||||
|
||||
buildMoodCSS(style, intensity) {
|
||||
const opacity = intensity === 'subtle' ? '0.3' : intensity === 'medium' ? '0.6' : '0.9';
|
||||
|
||||
let css = `
|
||||
body {
|
||||
background: ${style.background} !important;
|
||||
color: ${style.text} !important;
|
||||
${style.font ? `font-family: ${style.font} !important;` : ''}
|
||||
}
|
||||
|
||||
* {
|
||||
color: ${style.text} !important;
|
||||
}
|
||||
|
||||
a {
|
||||
color: ${style.accent || style.text} !important;
|
||||
}
|
||||
`;
|
||||
|
||||
if (style.effects) {
|
||||
css += style.effects;
|
||||
}
|
||||
|
||||
// Add animation keyframes if needed
|
||||
if (style.effects && style.effects.includes('energyPulse')) {
|
||||
css += `
|
||||
@keyframes energyPulse {
|
||||
0%, 100% { filter: hue-rotate(0deg); }
|
||||
50% { filter: hue-rotate(180deg); }
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
if (style.effects && style.effects.includes('gentleWave')) {
|
||||
css += `
|
||||
@keyframes gentleWave {
|
||||
0%, 100% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
return css;
|
||||
}
|
||||
|
||||
applyEffect(effect, duration) {
|
||||
const effects = {
|
||||
matrix_rain: `
|
||||
body::after {
|
||||
content: '';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y="10" font-size="8" fill="%2300ff00">0</text><text y="20" font-size="8" fill="%2300ff00">1</text><text y="30" font-size="8" fill="%2300ff00">0</text><text y="40" font-size="8" fill="%2300ff00">1</text></svg>');
|
||||
animation: matrixFall 2s linear infinite;
|
||||
pointer-events: none;
|
||||
z-index: 9999;
|
||||
opacity: 0.7;
|
||||
}
|
||||
@keyframes matrixFall {
|
||||
from { transform: translateY(-100px); }
|
||||
to { transform: translateY(100vh); }
|
||||
}
|
||||
`,
|
||||
floating_particles: `
|
||||
body::before {
|
||||
content: '✨ 🌟 ⭐ 💫';
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
animation: floatParticles 6s ease-in-out infinite;
|
||||
pointer-events: none;
|
||||
z-index: 9999;
|
||||
font-size: 20px;
|
||||
}
|
||||
@keyframes floatParticles {
|
||||
0%, 100% { transform: translateY(100vh) rotate(0deg); }
|
||||
50% { transform: translateY(-100px) rotate(180deg); }
|
||||
}
|
||||
`,
|
||||
cursor_trail: `
|
||||
body {
|
||||
cursor: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"><circle cx="10" cy="10" r="8" fill="rgba(255,0,255,0.5)"/></svg>'), auto !important;
|
||||
}
|
||||
`,
|
||||
neon_glow: `
|
||||
* {
|
||||
text-shadow: 0 0 10px #00ffff, 0 0 20px #00ffff, 0 0 30px #00ffff !important;
|
||||
}
|
||||
|
||||
a, button {
|
||||
box-shadow: 0 0 15px #ff00ff !important;
|
||||
}
|
||||
`,
|
||||
typing_effect: `
|
||||
* {
|
||||
animation: typewriter 2s steps(40, end) infinite !important;
|
||||
}
|
||||
@keyframes typewriter {
|
||||
from { width: 0; }
|
||||
to { width: 100%; }
|
||||
}
|
||||
`
|
||||
};
|
||||
|
||||
const css = effects[effect] || '';
|
||||
|
||||
// Auto-remove effect after duration
|
||||
if (duration && duration > 0) {
|
||||
setTimeout(() => {
|
||||
const effectStyle = document.getElementById('opendia-custom-style');
|
||||
if (effectStyle) effectStyle.remove();
|
||||
}, duration * 1000);
|
||||
}
|
||||
|
||||
return css;
|
||||
}
|
||||
}
|
||||
|
||||
// Theme Presets Database
|
||||
const THEME_PRESETS = {
|
||||
"dark_hacker": {
|
||||
name: "🖤 Dark Hacker",
|
||||
css: `
|
||||
* {
|
||||
background: #0a0a0a !important;
|
||||
color: #00ff00 !important;
|
||||
font-family: 'Courier New', monospace !important;
|
||||
}
|
||||
a { color: #00ffff !important; }
|
||||
body::before {
|
||||
content: ''; position: fixed; top: 0; left: 0; width: 100%; height: 100%;
|
||||
background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text y="50" font-size="10" fill="%23003300">01010101</text></svg>');
|
||||
opacity: 0.1; pointer-events: none; z-index: -1;
|
||||
}
|
||||
`
|
||||
},
|
||||
"retro_80s": {
|
||||
name: "📼 Retro 80s",
|
||||
css: `
|
||||
* {
|
||||
background: linear-gradient(45deg, #ff0080, #8000ff) !important;
|
||||
color: #ffffff !important;
|
||||
font-family: 'Arial Black', sans-serif !important;
|
||||
text-shadow: 2px 2px 4px #000000 !important;
|
||||
}
|
||||
body { animation: retroPulse 2s infinite; }
|
||||
@keyframes retroPulse { 0%, 100% { filter: hue-rotate(0deg); } 50% { filter: hue-rotate(180deg); } }
|
||||
`
|
||||
},
|
||||
"rainbow_party": {
|
||||
name: "🌈 Rainbow Party",
|
||||
css: `
|
||||
body {
|
||||
background: linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet) !important;
|
||||
background-size: 400% 400% !important;
|
||||
animation: rainbowShift 3s ease infinite !important;
|
||||
}
|
||||
@keyframes rainbowShift {
|
||||
0%, 100% { background-position: 0% 50%; }
|
||||
50% { background-position: 100% 50%; }
|
||||
}
|
||||
* { color: white !important; text-shadow: 1px 1px 2px black !important; }
|
||||
`
|
||||
},
|
||||
"minimalist_zen": {
|
||||
name: "🧘 Minimalist Zen",
|
||||
css: `
|
||||
* {
|
||||
background: #f8f8f8 !important;
|
||||
color: #333333 !important;
|
||||
font-family: 'Georgia', serif !important;
|
||||
line-height: 1.6 !important;
|
||||
}
|
||||
body { max-width: 800px; margin: 0 auto; padding: 20px; }
|
||||
`
|
||||
},
|
||||
"high_contrast": {
|
||||
name: "🔍 High Contrast",
|
||||
css: `
|
||||
* {
|
||||
background: #000000 !important;
|
||||
color: #ffffff !important;
|
||||
font-family: Arial, sans-serif !important;
|
||||
font-weight: bold !important;
|
||||
}
|
||||
a { color: #ffff00 !important; }
|
||||
`
|
||||
},
|
||||
"cyberpunk": {
|
||||
name: "🤖 Cyberpunk",
|
||||
css: `
|
||||
* {
|
||||
background: #0d1117 !important;
|
||||
color: #ff006e !important;
|
||||
font-family: 'Courier New', monospace !important;
|
||||
}
|
||||
a { color: #00ffff !important; }
|
||||
body {
|
||||
background-image:
|
||||
linear-gradient(90deg, transparent 79px, #abced4 79px, #abced4 81px, transparent 81px),
|
||||
linear-gradient(#eee .1em, transparent .1em);
|
||||
background-size: 81px 1.2em;
|
||||
}
|
||||
`
|
||||
},
|
||||
"pastel_dream": {
|
||||
name: "🌸 Pastel Dream",
|
||||
css: `
|
||||
* {
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%) !important;
|
||||
color: #2c3e50 !important;
|
||||
font-family: 'Comic Sans MS', cursive !important;
|
||||
}
|
||||
body { filter: sepia(20%) saturate(80%); }
|
||||
`
|
||||
},
|
||||
"newspaper": {
|
||||
name: "📰 Newspaper",
|
||||
css: `
|
||||
* {
|
||||
background: #ffffff !important;
|
||||
color: #000000 !important;
|
||||
font-family: 'Times New Roman', serif !important;
|
||||
line-height: 1.4 !important;
|
||||
}
|
||||
body {
|
||||
column-count: 2;
|
||||
column-gap: 2em;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
`
|
||||
}
|
||||
};
|
||||
|
||||
// Initialize the automation system
|
||||
const browserAutomation = new BrowserAutomation();
|
||||
|
||||
} // End of injection guard
|
||||
8
opendia-extension/src/polyfill/browser-polyfill.min.js
vendored
Normal file
@@ -236,14 +236,88 @@
|
||||
button:active {
|
||||
transform: translateY(0);
|
||||
}
|
||||
|
||||
.safety-mode {
|
||||
background: rgba(255, 255, 255, 0.6);
|
||||
padding: 16px;
|
||||
border-radius: 10px;
|
||||
margin-bottom: 16px;
|
||||
border: 1px solid rgba(255, 255, 255, 0.8);
|
||||
backdrop-filter: blur(20px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
||||
}
|
||||
|
||||
.safety-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.safety-label {
|
||||
color: #374151;
|
||||
font-weight: 600;
|
||||
font-size: 0.875rem;
|
||||
}
|
||||
|
||||
.safety-toggle {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 50px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.safety-toggle input {
|
||||
opacity: 0;
|
||||
width: 0;
|
||||
height: 0;
|
||||
}
|
||||
|
||||
.safety-slider {
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background-color: #e5e7eb;
|
||||
transition: 0.3s;
|
||||
border-radius: 24px;
|
||||
border: 1px solid rgba(0, 129, 247, 0.2);
|
||||
}
|
||||
|
||||
.safety-slider:before {
|
||||
position: absolute;
|
||||
content: "";
|
||||
height: 18px;
|
||||
width: 18px;
|
||||
left: 2px;
|
||||
bottom: 2px;
|
||||
background-color: white;
|
||||
transition: 0.3s;
|
||||
border-radius: 50%;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
input:checked + .safety-slider {
|
||||
background: linear-gradient(135deg, #0081F7, #1d4ed8);
|
||||
border-color: rgba(0, 129, 247, 0.4);
|
||||
}
|
||||
|
||||
input:checked + .safety-slider:before {
|
||||
transform: translateX(26px);
|
||||
}
|
||||
|
||||
.safety-slider:hover {
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="header">
|
||||
<div class="logo">
|
||||
<video autoplay loop muted playsinline>
|
||||
<source src="logo.webm" type="video/webm">
|
||||
<source src="logo.mp4" type="video/mp4">
|
||||
<source src="../../logo.webm" type="video/webm">
|
||||
<source src="../../logo.mp4" type="video/mp4">
|
||||
<span>OD</span>
|
||||
</video>
|
||||
</div>
|
||||
@@ -255,9 +329,9 @@
|
||||
<span id="statusText" class="tooltip">
|
||||
Checking connection...
|
||||
<span class="tooltip-content">
|
||||
Make sure your MCP server is connected.
|
||||
If it's the case, click on Reconnect.
|
||||
If it still don't work, kill your 3000 port & try again.
|
||||
Start server with: npx opendia
|
||||
Auto-discovery will find the correct ports.
|
||||
Existing processes are automatically terminated on startup
|
||||
</span>
|
||||
</span>
|
||||
</div>
|
||||
@@ -265,7 +339,7 @@
|
||||
<div class="info">
|
||||
<div class="info-row">
|
||||
<span class="info-label">Server</span>
|
||||
<span class="info-value" id="serverUrl">ws://localhost:3000</span>
|
||||
<span class="info-value" id="serverUrl">Auto-Discovery</span>
|
||||
</div>
|
||||
<div class="info-row">
|
||||
<span class="info-label">Available Tools</span>
|
||||
@@ -277,6 +351,21 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="safety-mode">
|
||||
<div class="safety-row">
|
||||
<span class="safety-label tooltip">
|
||||
Safety Mode
|
||||
<span class="tooltip-content">
|
||||
When enabled, blocks write/edit tools: element_click, element_fill
|
||||
</span>
|
||||
</span>
|
||||
<label class="safety-toggle">
|
||||
<input type="checkbox" id="safetyMode">
|
||||
<span class="safety-slider"></span>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="button-group">
|
||||
<button id="reconnectBtn">Reconnect</button>
|
||||
</div>
|
||||
@@ -1,8 +1,18 @@
|
||||
// OpenDia Popup
|
||||
// Import WebExtension polyfill for cross-browser compatibility
|
||||
if (typeof browser === 'undefined' && typeof chrome !== 'undefined') {
|
||||
globalThis.browser = chrome;
|
||||
}
|
||||
|
||||
// Cross-browser compatibility layer
|
||||
const runtimeAPI = browser.runtime;
|
||||
const tabsAPI = browser.tabs;
|
||||
const storageAPI = browser.storage;
|
||||
let statusIndicator = document.getElementById("statusIndicator");
|
||||
let statusText = document.getElementById("statusText");
|
||||
let toolCount = document.getElementById("toolCount");
|
||||
let currentPage = document.getElementById("currentPage");
|
||||
let serverUrl = document.getElementById("serverUrl");
|
||||
|
||||
// Get dynamic tool count from background script
|
||||
function updateToolCount() {
|
||||
@@ -13,9 +23,9 @@ function updateToolCount() {
|
||||
"get_bookmarks", "add_bookmark", "get_history", "get_selected_text", "get_page_links"
|
||||
];
|
||||
|
||||
if (chrome.runtime?.id) {
|
||||
chrome.runtime.sendMessage({ action: "getToolCount" }, (response) => {
|
||||
if (!chrome.runtime.lastError && response?.toolCount) {
|
||||
if (runtimeAPI?.id) {
|
||||
runtimeAPI.sendMessage({ action: "getToolCount" }, (response) => {
|
||||
if (!runtimeAPI.lastError && response?.toolCount) {
|
||||
toolCount.innerHTML = `<span class="tooltip">${response.toolCount}
|
||||
<span class="tooltip-content">Available MCP Tools:\npage_analyze • page_extract_content • element_click • element_fill • element_get_state • page_navigate • page_wait_for • page_scroll • tab_create • tab_close • tab_list • tab_switch • get_bookmarks • add_bookmark • get_history • get_selected_text • get_page_links</span>
|
||||
</span>`;
|
||||
@@ -31,9 +41,9 @@ function updateToolCount() {
|
||||
|
||||
// Check connection status and get page info
|
||||
function checkStatus() {
|
||||
if (chrome.runtime?.id) {
|
||||
chrome.runtime.sendMessage({ action: "getStatus" }, (response) => {
|
||||
if (chrome.runtime.lastError) {
|
||||
if (runtimeAPI?.id) {
|
||||
runtimeAPI.sendMessage({ action: "getStatus" }, (response) => {
|
||||
if (runtimeAPI.lastError) {
|
||||
updateStatus(false);
|
||||
} else {
|
||||
updateStatus(response?.connected || false);
|
||||
@@ -44,7 +54,7 @@ function checkStatus() {
|
||||
updateToolCount();
|
||||
|
||||
// Get current page info
|
||||
chrome.tabs.query({active: true, currentWindow: true}, (tabs) => {
|
||||
tabsAPI.query({active: true, currentWindow: true}, (tabs) => {
|
||||
if (tabs[0]) {
|
||||
const url = new URL(tabs[0].url);
|
||||
currentPage.textContent = url.hostname;
|
||||
@@ -59,24 +69,39 @@ function checkStatus() {
|
||||
checkStatus();
|
||||
setInterval(checkStatus, 2000);
|
||||
|
||||
// Update server URL display
|
||||
function updateServerUrl() {
|
||||
if (runtimeAPI?.id) {
|
||||
runtimeAPI.sendMessage({ action: "getPorts" }, (response) => {
|
||||
if (!runtimeAPI.lastError && response?.websocketUrl) {
|
||||
serverUrl.textContent = response.websocketUrl;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Update server URL periodically
|
||||
updateServerUrl();
|
||||
setInterval(updateServerUrl, 5000);
|
||||
|
||||
// Update UI based on connection status
|
||||
function updateStatus(connected) {
|
||||
if (connected) {
|
||||
statusIndicator.className = "status-indicator connected";
|
||||
statusText.innerHTML = `Connected to MCP server
|
||||
<span class="tooltip-content">Make sure your MCP server is connected. If it's the case, click on Reconnect. If it still don't work, kill your 3000 port & try again.</span>`;
|
||||
<span class="tooltip-content">Connected successfully! Server auto-discovery is working. Default ports: WebSocket=5555, HTTP=5556</span>`;
|
||||
} else {
|
||||
statusIndicator.className = "status-indicator disconnected";
|
||||
statusText.innerHTML = `Disconnected from MCP server
|
||||
<span class="tooltip-content">Make sure your MCP server is connected. If it's the case, click on Reconnect. If it still don't work, kill your 3000 port & try again.</span>`;
|
||||
<span class="tooltip-content">Start server with: npx opendia. Auto-discovery will find the correct ports. Existing processes are automatically terminated on startup</span>`;
|
||||
}
|
||||
}
|
||||
|
||||
// Reconnect button
|
||||
document.getElementById("reconnectBtn").addEventListener("click", () => {
|
||||
if (chrome.runtime?.id) {
|
||||
chrome.runtime.sendMessage({ action: "reconnect" }, (response) => {
|
||||
if (!chrome.runtime.lastError) {
|
||||
if (runtimeAPI?.id) {
|
||||
runtimeAPI.sendMessage({ action: "reconnect" }, (response) => {
|
||||
if (!runtimeAPI.lastError) {
|
||||
setTimeout(checkStatus, 1000);
|
||||
}
|
||||
});
|
||||
@@ -84,8 +109,31 @@ document.getElementById("reconnectBtn").addEventListener("click", () => {
|
||||
});
|
||||
|
||||
|
||||
// Safety Mode Management
|
||||
const safetyModeToggle = document.getElementById("safetyMode");
|
||||
|
||||
// Load safety mode state from storage
|
||||
storageAPI.local.get(['safetyMode'], (result) => {
|
||||
const safetyEnabled = result.safetyMode || false; // Default to false (safety off)
|
||||
safetyModeToggle.checked = safetyEnabled;
|
||||
});
|
||||
|
||||
// Handle safety mode toggle changes
|
||||
safetyModeToggle.addEventListener('change', () => {
|
||||
const safetyEnabled = safetyModeToggle.checked;
|
||||
|
||||
// Save to storage
|
||||
storageAPI.local.set({ safetyMode: safetyEnabled });
|
||||
|
||||
// Notify background script
|
||||
runtimeAPI.sendMessage({
|
||||
action: "setSafetyMode",
|
||||
enabled: safetyEnabled
|
||||
});
|
||||
});
|
||||
|
||||
// Listen for updates from background script
|
||||
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
||||
runtimeAPI.onMessage.addListener((message, sender, sendResponse) => {
|
||||
if (message.type === "statusUpdate") {
|
||||
updateStatus(message.connected);
|
||||
}
|
||||
182
opendia-extension/test-extension.js
Normal file
@@ -0,0 +1,182 @@
|
||||
// Simple test script to verify browser extension compatibility
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
function testManifestStructure(browser) {
|
||||
console.log(`\n🔍 Testing ${browser} extension structure...`);
|
||||
|
||||
const buildDir = `dist/${browser}`;
|
||||
const manifestPath = path.join(buildDir, 'manifest.json');
|
||||
|
||||
try {
|
||||
// Read and parse manifest
|
||||
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
||||
|
||||
// Check manifest version
|
||||
console.log(` Manifest version: ${manifest.manifest_version}`);
|
||||
|
||||
// Check background configuration
|
||||
if (browser === 'chrome') {
|
||||
console.log(` Background: Service Worker (${manifest.background.service_worker})`);
|
||||
console.log(` Action: ${manifest.action ? 'Present' : 'Missing'}`);
|
||||
console.log(` Host permissions: ${manifest.host_permissions ? manifest.host_permissions.length : 0}`);
|
||||
} else {
|
||||
console.log(` Background: Scripts (${manifest.background.scripts.length} files)`);
|
||||
console.log(` Browser action: ${manifest.browser_action ? 'Present' : 'Missing'}`);
|
||||
console.log(` Gecko ID: ${manifest.applications?.gecko?.id || 'Not set'}`);
|
||||
}
|
||||
|
||||
// Check permissions
|
||||
console.log(` Permissions: ${manifest.permissions.length} total`);
|
||||
|
||||
// Check content scripts
|
||||
console.log(` Content scripts: ${manifest.content_scripts?.length || 0} configured`);
|
||||
|
||||
// Check polyfill inclusion
|
||||
const polyfillPath = path.join(buildDir, 'src/polyfill/browser-polyfill.min.js');
|
||||
const polyfillExists = fs.existsSync(polyfillPath);
|
||||
console.log(` WebExtension polyfill: ${polyfillExists ? 'Present' : 'Missing'}`);
|
||||
|
||||
// Check if polyfill is included in content scripts
|
||||
const hasPolyfillInContent = manifest.content_scripts?.[0]?.js?.includes('src/polyfill/browser-polyfill.min.js');
|
||||
console.log(` Polyfill in content scripts: ${hasPolyfillInContent ? 'Yes' : 'No'}`);
|
||||
|
||||
// Check if polyfill is included in background (Firefox only)
|
||||
if (browser === 'firefox') {
|
||||
const hasPolyfillInBackground = manifest.background?.scripts?.includes('src/polyfill/browser-polyfill.min.js');
|
||||
console.log(` Polyfill in background: ${hasPolyfillInBackground ? 'Yes' : 'No'}`);
|
||||
}
|
||||
|
||||
console.log(`✅ ${browser} extension structure looks good!`);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Error testing ${browser} extension:`, error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function testBackgroundScript(browser) {
|
||||
console.log(`\n🔍 Testing ${browser} background script...`);
|
||||
|
||||
const scriptPath = `dist/${browser}/src/background/background.js`;
|
||||
|
||||
try {
|
||||
const script = fs.readFileSync(scriptPath, 'utf8');
|
||||
|
||||
// Check for browser polyfill usage
|
||||
const usesBrowserAPI = script.includes('browser.') || script.includes('globalThis.browser');
|
||||
console.log(` Uses browser API: ${usesBrowserAPI ? 'Yes' : 'No'}`);
|
||||
|
||||
// Check for connection manager
|
||||
const hasConnectionManager = script.includes('ConnectionManager');
|
||||
console.log(` Has connection manager: ${hasConnectionManager ? 'Yes' : 'No'}`);
|
||||
|
||||
// Check for browser detection
|
||||
const hasBrowserDetection = script.includes('browserInfo') || script.includes('isFirefox') || script.includes('isServiceWorker');
|
||||
console.log(` Has browser detection: ${hasBrowserDetection ? 'Yes' : 'No'}`);
|
||||
|
||||
// Check for WebSocket management
|
||||
const hasWebSocketManagement = script.includes('WebSocket') && script.includes('connect');
|
||||
console.log(` Has WebSocket management: ${hasWebSocketManagement ? 'Yes' : 'No'}`);
|
||||
|
||||
console.log(`✅ ${browser} background script looks good!`);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Error testing ${browser} background script:`, error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function testContentScript(browser) {
|
||||
console.log(`\n🔍 Testing ${browser} content script...`);
|
||||
|
||||
const scriptPath = `dist/${browser}/src/content/content.js`;
|
||||
|
||||
try {
|
||||
const script = fs.readFileSync(scriptPath, 'utf8');
|
||||
|
||||
// Check for browser polyfill usage
|
||||
const usesBrowserAPI = script.includes('browser.') || script.includes('globalThis.browser');
|
||||
console.log(` Uses browser API: ${usesBrowserAPI ? 'Yes' : 'No'}`);
|
||||
|
||||
// Check for message handling
|
||||
const hasMessageHandling = script.includes('onMessage') && script.includes('sendResponse');
|
||||
console.log(` Has message handling: ${hasMessageHandling ? 'Yes' : 'No'}`);
|
||||
|
||||
console.log(`✅ ${browser} content script looks good!`);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Error testing ${browser} content script:`, error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function testPopupScript(browser) {
|
||||
console.log(`\n🔍 Testing ${browser} popup script...`);
|
||||
|
||||
const scriptPath = `dist/${browser}/src/popup/popup.js`;
|
||||
|
||||
try {
|
||||
const script = fs.readFileSync(scriptPath, 'utf8');
|
||||
|
||||
// Check for browser polyfill usage
|
||||
const usesBrowserAPI = script.includes('browser.') || script.includes('globalThis.browser');
|
||||
console.log(` Uses browser API: ${usesBrowserAPI ? 'Yes' : 'No'}`);
|
||||
|
||||
// Check for API abstraction
|
||||
const hasAPIAbstraction = script.includes('runtimeAPI') || script.includes('tabsAPI') || script.includes('storageAPI');
|
||||
console.log(` Has API abstraction: ${hasAPIAbstraction ? 'Yes' : 'No'}`);
|
||||
|
||||
console.log(`✅ ${browser} popup script looks good!`);
|
||||
return true;
|
||||
|
||||
} catch (error) {
|
||||
console.error(`❌ Error testing ${browser} popup script:`, error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function runAllTests() {
|
||||
console.log('🚀 Testing cross-browser extension compatibility...\n');
|
||||
|
||||
const browsers = ['chrome', 'firefox'];
|
||||
let allPassed = true;
|
||||
|
||||
for (const browser of browsers) {
|
||||
console.log(`\n🌐 Testing ${browser.toUpperCase()} extension:`);
|
||||
console.log('='.repeat(40));
|
||||
|
||||
const tests = [
|
||||
testManifestStructure,
|
||||
testBackgroundScript,
|
||||
testContentScript,
|
||||
testPopupScript
|
||||
];
|
||||
|
||||
for (const test of tests) {
|
||||
if (!test(browser)) {
|
||||
allPassed = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
console.log('\n' + '='.repeat(50));
|
||||
if (allPassed) {
|
||||
console.log('🎉 All tests passed! Cross-browser extension is ready.');
|
||||
console.log('\n📦 Distribution packages:');
|
||||
console.log(' Chrome: dist/opendia-chrome.zip');
|
||||
console.log(' Firefox: dist/opendia-firefox.zip');
|
||||
console.log('\n🧪 Manual testing:');
|
||||
console.log(' 1. Chrome: Load dist/chrome in chrome://extensions');
|
||||
console.log(' 2. Firefox: Load dist/firefox in about:debugging');
|
||||
console.log(' 3. Both should connect to MCP server on localhost:5555/5556');
|
||||
} else {
|
||||
console.log('❌ Some tests failed. Please check the output above.');
|
||||
}
|
||||
}
|
||||
|
||||
// Run tests
|
||||
runAllTests();
|
||||
208
opendia-mcp/README.md
Normal file
@@ -0,0 +1,208 @@
|
||||
# OpenDia <img src="opendia-extension/icon-128.png" alt="OpenDia" width="32" height="32">
|
||||
|
||||
> **The open alternative to Dia**
|
||||
> Connect your browser to AI models. No browser switching needed—works seamlessly with any Chromium browser including Chrome & Arc.
|
||||
|
||||
[](https://badge.fury.io/js/opendia)
|
||||
[](https://opensource.org/licenses/MIT)
|
||||
|
||||
## 📺 See it in Action
|
||||
|
||||

|
||||
|
||||
## 🚀 What is OpenDia?
|
||||
|
||||
OpenDia lets AI models control your browser automatically. **The key advantage? It leverages everything you already have**—your logged-in accounts, saved passwords, cookies, wallets, and browsing history. No need to start from scratch or switch contexts.
|
||||
|
||||
**🔑 Use Your Existing Digital Life:**
|
||||
- ✅ **Logged-in accounts**: Post to Twitter / X, LinkedIn, Facebook with your existing sessions
|
||||
- ✅ **Browser data**: Access your bookmarks, history, and saved passwords
|
||||
- ✅ **Extensions & wallets**: Use MetaMask, password managers, or any installed extensions
|
||||
- ✅ **Cookies & sessions**: Stay authenticated across all your favorite sites
|
||||
- ✅ **Local testing**: Perfect for development with Cursor - test with real user sessions
|
||||
|
||||
**✨ Key Benefits:**
|
||||
- 🔄 **Universal AI Support**: Works with Claude, ChatGPT, Cursor and even local models
|
||||
- 🎯 **Anti-Detection**: Specialized bypasses for Twitter/X, LinkedIn, Facebook
|
||||
- 📱 **Smart Automation**: AI understands your pages and finds the right elements
|
||||
- 🛡️ **Privacy-First**: Everything runs locally, your data stays with you
|
||||
- ⚡ **Zero Setup**: Get started with one command
|
||||
|
||||
## 🌐 Browser Support
|
||||
|
||||
Works with **any Chromium-based browser**:
|
||||
- ✅ **Google Chrome**
|
||||
- ✅ **Arc Browser**
|
||||
- ✅ **Microsoft Edge**
|
||||
- ✅ **Brave Browser**
|
||||
- ✅ **Opera**
|
||||
- ✅ **Vivaldi**
|
||||
- ✅ **Any Chromium variant**
|
||||
|
||||
Perfect for **Cursor users** who want to automate their local testing and development workflows!
|
||||
|
||||
## 🎬 What You Can Do
|
||||
|
||||
**Real workflows you can try today:**
|
||||
|
||||
### 📰 Content & Social Media
|
||||
- **"Summarize all the articles I read today and post a Twitter thread about the key insights"**
|
||||
- **"Find interesting articles related to AI from my bookmarks and create a reading list"**
|
||||
- **"Read this article and post a thoughtful comment on the LinkedIn version"**
|
||||
- **"Check my recent Twitter bookmarks and summarize the main themes"**
|
||||
|
||||
### 📧 Productivity & Research
|
||||
- **"Browse my latest emails and tell me what needs urgent attention"**
|
||||
- **"Find all the GitHub repos I visited this week and create a summary report"**
|
||||
- **"Extract the main points from this research paper and save them to my notes"**
|
||||
- **"Search my browsing history for that article about AI safety I read last month"**
|
||||
|
||||
### 🤖 Development & Testing (Perfect for Cursor!)
|
||||
- **"Test my web app's signup flow and take screenshots at each step"**
|
||||
- **"Fill out this form with test data and check if validation works"**
|
||||
- **"Navigate through my app and check if all the buttons work properly"**
|
||||
- **"Use my connected wallet to test this DeFi interface"**
|
||||
|
||||
### 🔄 Advanced Automation
|
||||
- **"Open tabs for all my daily news sources and summarize the top stories"**
|
||||
- **"Draft replies to my unread messages based on the context"**
|
||||
- **"Monitor this webpage and notify me when the content changes"**
|
||||
- **"Automatically bookmark interesting articles I'm reading"**
|
||||
|
||||
## ⚡ Quick Start
|
||||
|
||||
### 1. Start the Server
|
||||
```bash
|
||||
npx opendia
|
||||
```
|
||||
|
||||
### 2. Install the Browser Extension
|
||||
1. Download from [releases](https://github.com/aaronjmars/opendia/releases)
|
||||
2. Go to `chrome://extensions/` (or your browser's extension page)
|
||||
3. Enable "Developer mode"
|
||||
4. Click "Load unpacked" and select the extension folder
|
||||
|
||||
### 3. Connect to Your AI
|
||||
**For Claude Desktop**, add to your configuration:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"opendia": {
|
||||
"command": "npx",
|
||||
"args": ["opendia"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**For Cursor or other AI tools**, use the same configuration or follow their specific setup instructions.
|
||||
|
||||
## 🛠️ Capabilities
|
||||
|
||||
OpenDia gives AI models **17 powerful browser tools**:
|
||||
|
||||
### 🎯 Smart Page Understanding
|
||||
- **Analyze any webpage** - AI automatically finds buttons, forms, and interactive elements
|
||||
- **Extract content intelligently** - Get clean text from articles, social posts, or search results
|
||||
- **Understand context** - AI knows what type of page it's looking at and how to interact with it
|
||||
|
||||
### 🖱️ Natural Interactions
|
||||
- **Click anything** - Buttons, links, menus - AI finds and clicks the right elements
|
||||
- **Fill forms smartly** - Works even on complex sites like Twitter, LinkedIn, Facebook
|
||||
- **Navigate seamlessly** - Go to pages, scroll, wait for content to load
|
||||
- **Handle modern web apps** - Bypasses detection on social platforms
|
||||
|
||||
### 📑 Tab & Window Management
|
||||
- **Multi-tab workflows** - Open, close, switch between tabs automatically
|
||||
- **Organize your workspace** - Let AI manage your browser tabs efficiently
|
||||
- **Coordinate complex tasks** - Work across multiple sites simultaneously
|
||||
|
||||
### 📊 Access Your Browser Data
|
||||
- **Bookmarks & History** - Find that article you read last week
|
||||
- **Current page content** - Get selected text, links, or full page content
|
||||
- **Real-time information** - Work with whatever's currently on your screen
|
||||
|
||||
### 🛡️ Anti-Detection Features
|
||||
- **Social media posting** - Bypass automation detection on Twitter/X, LinkedIn, Facebook
|
||||
- **Natural interactions** - Mimics human behavior to avoid triggering security measures
|
||||
- **Reliable automation** - Works consistently even on sites that block typical automation tools
|
||||
|
||||
## 💬 Example Prompts to Try
|
||||
|
||||
Once everything is set up, try asking your AI:
|
||||
|
||||
**Content Creation:**
|
||||
> *"Read the article on this page and create a Twitter thread summarizing the main points"*
|
||||
|
||||
**Research & Analysis:**
|
||||
> *"Look through my browser history from this week and find articles about machine learning. Summarize the key trends."*
|
||||
|
||||
**Social Media Management:**
|
||||
> *"Check my Twitter bookmarks and organize them into categories. Create a summary of each category."*
|
||||
|
||||
**Productivity:**
|
||||
> *"Open tabs for my usual morning reading sites and give me a briefing of today's top stories"*
|
||||
|
||||
**Development Testing:**
|
||||
> *"Fill out this contact form with test data and check if the submission works properly"*
|
||||
|
||||
**Personal Assistant:**
|
||||
> *"Find that GitHub repo I was looking at yesterday about React components and bookmark it for later"*
|
||||
|
||||
## 🏗️ How It Works
|
||||
|
||||
```mermaid
|
||||
graph LR
|
||||
A[AI Model] --> B[OpenDia Server]
|
||||
B --> C[Browser Extension]
|
||||
C --> D[Your Browser]
|
||||
D --> E[Any Website]
|
||||
```
|
||||
|
||||
1. **You ask** your AI to do something browser-related
|
||||
2. **AI calls** OpenDia tools to understand and interact with pages
|
||||
3. **OpenDia controls** your browser through the extension
|
||||
4. **You get results** - AI can see what happened and respond intelligently
|
||||
|
||||
## 🔒 Security & Privacy
|
||||
|
||||
**Your data stays private**:
|
||||
- ✅ **Everything runs locally** - No cloud processing of your browsing data
|
||||
- ✅ **You control access** - Extension only works when you want it to
|
||||
- ✅ **Open source** - Full transparency of what the code does
|
||||
- ✅ **No tracking** - We don't collect or store any of your information
|
||||
|
||||
**Important**: This tool requires broad browser permissions to function. Only use with AI models you trust, and in environments where you're comfortable with browser automation.
|
||||
|
||||
## 🤝 Contributing
|
||||
|
||||
Love to have your help making OpenDia better!
|
||||
|
||||
### Quick Development Setup
|
||||
```bash
|
||||
git clone https://github.com/aaronjmars/opendia.git
|
||||
cd opendia
|
||||
|
||||
# Start the server
|
||||
cd opendia-mcp
|
||||
npm install
|
||||
npm start
|
||||
|
||||
# Load extension in your browser
|
||||
# Go to chrome://extensions/ → Developer mode → Load unpacked: ./opendia-extension
|
||||
```
|
||||
|
||||
### Ways to Contribute
|
||||
- 🐛 **Report bugs** via [GitHub Issues](https://github.com/aaronjmars/opendia/issues)
|
||||
- 💡 **Share it on social medias**
|
||||
- 🔧 **Add new browser capabilities**
|
||||
- 📖 **Improve documentation**
|
||||
- 🧪 **Test with different AI models**
|
||||
|
||||
## 📝 License
|
||||
|
||||
MIT License - see [LICENSE](LICENSE) for details.
|
||||
|
||||
---
|
||||
|
||||
**Ready to supercharge your browser with AI? Get started with `npx opendia`! 🚀**
|
||||
44
opendia-mcp/package-lock.json
generated
@@ -1,15 +1,23 @@
|
||||
{
|
||||
"name": "opendia-server",
|
||||
"version": "1.0.0",
|
||||
"name": "opendia",
|
||||
"version": "1.0.6",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "opendia-server",
|
||||
"version": "1.0.0",
|
||||
"name": "opendia",
|
||||
"version": "1.0.6",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"express": "^4.x.x",
|
||||
"ws": "^8.x.x"
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.2",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"bin": {
|
||||
"opendia": "server.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/accepts": {
|
||||
@@ -78,7 +86,7 @@
|
||||
}
|
||||
},
|
||||
"node_modules/call-bound": {
|
||||
"version": "1.0.4",
|
||||
"version": "1.0.5",
|
||||
"resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
|
||||
"integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
|
||||
"license": "MIT",
|
||||
@@ -129,6 +137,19 @@
|
||||
"integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/cors": {
|
||||
"version": "2.8.5",
|
||||
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
|
||||
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"object-assign": "^4",
|
||||
"vary": "^1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/debug": {
|
||||
"version": "2.6.9",
|
||||
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
|
||||
@@ -522,6 +543,15 @@
|
||||
"node": ">= 0.6"
|
||||
}
|
||||
},
|
||||
"node_modules/object-assign": {
|
||||
"version": "4.1.1",
|
||||
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
|
||||
"integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/object-inspect": {
|
||||
"version": "1.13.4",
|
||||
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
|
||||
|
||||
@@ -1,13 +1,51 @@
|
||||
{
|
||||
"name": "opendia-server",
|
||||
"version": "1.0.0",
|
||||
"description": "MCP Server for OpenDia Browser Bridge",
|
||||
"name": "opendia",
|
||||
"version": "1.1.0",
|
||||
"description": "🎯 OpenDia - The open alternative to Dia. Connect your browser to AI models with anti-detection bypass for Twitter/X, LinkedIn, Facebook",
|
||||
"main": "server.js",
|
||||
"bin": {
|
||||
"opendia": "./server.js"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node server.js"
|
||||
"start": "node server.js",
|
||||
"tunnel": "node server.js --tunnel",
|
||||
"sse-only": "node server.js --sse-only",
|
||||
"tunnel-sse": "node server.js --tunnel --sse-only",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"keywords": [
|
||||
"mcp",
|
||||
"browser",
|
||||
"automation",
|
||||
"ai",
|
||||
"claude",
|
||||
"chrome",
|
||||
"extension",
|
||||
"twitter",
|
||||
"linkedin",
|
||||
"facebook",
|
||||
"anti-detection"
|
||||
],
|
||||
"author": "OpenDia Team",
|
||||
"license": "MIT",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/aaronjmars/opendia.git"
|
||||
},
|
||||
"homepage": "https://github.com/aaronjmars/opendia",
|
||||
"bugs": {
|
||||
"url": "https://github.com/aaronjmars/opendia/issues"
|
||||
},
|
||||
"dependencies": {
|
||||
"ws": "^8.x.x",
|
||||
"express": "^4.x.x"
|
||||
}
|
||||
}
|
||||
"cors": "^2.8.5",
|
||||
"express": "^4.21.2",
|
||||
"ws": "^8.18.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=16.0.0"
|
||||
},
|
||||
"files": [
|
||||
"server.js",
|
||||
"README.md"
|
||||
]
|
||||
}
|
||||
|
||||