feat: Integrated Vision & Robust Translation Layer, Secured Repo (removed keys)

This commit is contained in:
Gemini AI
2025-12-15 04:53:51 +04:00
Unverified
parent a8436c91a3
commit 2407c42eb9
38 changed files with 7786 additions and 3776 deletions

View File

@@ -0,0 +1,219 @@
// Background script for Qwen AI Automation Extension
let isAuthenticated = false;
let qwenToken = null;
// Handle extension installation
chrome.runtime.onInstalled.addListener(() => {
console.log('Qwen AI Automation Extension installed');
});
// Handle messages from popup
chrome.runtime.onMessage.addListener(async (message, sender, sendResponse) => {
switch (message.action) {
case 'checkAuth':
sendResponse({ authenticated: isAuthenticated });
break;
case 'openAuth':
// Open Qwen authentication in a new tab
try {
await chrome.tabs.create({
url: 'https://chat.qwen.ai'
});
sendResponse({ success: true });
} catch (error) {
sendResponse({ success: false, error: error.message });
}
break;
case 'executeTask':
if (!isAuthenticated) {
sendResponse({ error: 'Not authenticated with Qwen' });
return true;
}
try {
const result = await executeBrowserTask(message.task);
sendResponse({ success: true, result: result });
} catch (error) {
sendResponse({ success: false, error: error.message });
}
break;
case 'updateAuthStatus':
isAuthenticated = message.authenticated;
qwenToken = message.token || null;
// Notify popup about auth status change
chrome.runtime.sendMessage({ action: 'authStatusUpdated' });
sendResponse({ success: true });
break;
}
return true; // Keep message channel open for async response
});
// Execute browser automation task
async function executeBrowserTask(task) {
// Get current active tab
const [activeTab] = await chrome.tabs.query({
active: true,
currentWindow: true
});
if (!activeTab) {
throw new Error('No active tab found');
}
try {
// Analyze the task and determine appropriate automation steps
const automationSteps = await analyzeTaskWithQwen(task, activeTab.url);
// Execute each step
let results = [];
for (const step of automationSteps) {
const result = await executeAutomationStep(step, activeTab.id);
results.push(result);
}
return `Task completed successfully. Performed ${automationSteps.length} steps.`;
} catch (error) {
throw new Error(`Task execution failed: ${error.message}`);
}
}
// Analyze task with Qwen AI (simplified for this example)
async function analyzeTaskWithQwen(task, currentUrl) {
// This would normally call the Qwen API
// For now, we'll use a simple rule-based approach
// In a real implementation, this would send the task to Qwen API
console.log(`Analyzing task: ${task} on page: ${currentUrl}`);
// Simple rule-based analysis (would be replaced with Qwen API call)
if (task.toLowerCase().includes('search') || task.toLowerCase().includes('google')) {
return [
{
action: 'fill',
selector: 'textarea[name="q"], input[name="q"], [name="search"], #search',
value: extractSearchQuery(task)
},
{
action: 'press',
key: 'Enter'
}
];
} else if (task.toLowerCase().includes('click') || task.toLowerCase().includes('click on')) {
const element = extractElementFromTask(task);
return [
{
action: 'click',
selector: element
}
];
} else {
// Default: just return the task as is for Qwen to process
return [
{
action: 'analyze',
task: task,
url: currentUrl
}
];
}
}
// Extract search query from task
function extractSearchQuery(task) {
const match = task.match(/search for ["']?([^"']+)["']?/i) ||
task.match(/google ["']?([^"']+)["']?/i) ||
task.match(/find ["']?([^"']+)["']?/i);
return match ? match[1] : task.replace(/(search|google|find)\s*/i, '').trim();
}
// Extract element from task
function extractElementFromTask(task) {
// Simple extraction - in reality would be more sophisticated
const lowerTask = task.toLowerCase();
if (lowerTask.includes('search') || lowerTask.includes('google')) return 'textarea[name="q"], input[name="q"]';
if (lowerTask.includes('button')) return 'button';
if (lowerTask.includes('link')) return 'a';
return '*'; // generic selector
}
// Execute a single automation step
async function executeAutomationStep(step, tabId) {
try {
switch (step.action) {
case 'click':
return await chrome.scripting.executeScript({
target: { tabId: tabId },
func: clickElement,
args: [step.selector]
});
case 'fill':
return await chrome.scripting.executeScript({
target: { tabId: tabId },
func: fillElement,
args: [step.selector, step.value]
});
case 'press':
// For key press, we'll inject a script to simulate the key
return await chrome.scripting.executeScript({
target: { tabId: tabId },
func: pressKey,
args: [step.key]
});
default:
console.log('Unknown action:', step.action);
return { success: false, error: `Unknown action: ${step.action}` };
}
} catch (error) {
console.error('Step execution error:', error);
throw error;
}
}
// Helper functions to be injected into the page
function clickElement(selector) {
const element = document.querySelector(selector);
if (element) {
element.click();
return { success: true, message: `Clicked element: ${selector}` };
} else {
return { success: false, error: `Element not found: ${selector}` };
}
}
function fillElement(selector, value) {
const element = document.querySelector(selector);
if (element) {
element.value = value;
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
return { success: true, message: `Filled element: ${selector} with value: ${value}` };
} else {
return { success: false, error: `Element not found: ${selector}` };
}
}
function pressKey(key) {
const event = new KeyboardEvent('keydown', {
key: key,
code: key.toUpperCase(),
bubbles: true
});
document.activeElement.dispatchEvent(event);
return { success: true, message: `Pressed key: ${key}` };
}
// Listen for tab updates to manage state
chrome.tabs.onUpdated.addListener((tabId, changeInfo, tab) => {
if (changeInfo.status === 'complete' && tab.active) {
// Tab loaded completely, extension is ready for new tasks
console.log(`Tab ${tabId} loaded: ${tab.url}`);
}
});

View File

@@ -0,0 +1,201 @@
// Content script for Qwen AI Automation Extension
console.log('Qwen AI Automation content script loaded');
// Store extension state
let extensionState = {
isActive: false,
currentTask: null,
qwenToken: null
};
// Listen for messages from background script
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
switch (message.action) {
case 'getPageContent':
sendResponse(getPageContent());
break;
case 'getElementInfo':
sendResponse(getElementInfo(message.selector));
break;
case 'executeAction':
sendResponse(executeAction(message.action, message.params));
break;
default:
console.log('Unknown message action:', message.action);
}
return true; // Keep message channel open for async response
});
// Get page content for AI analysis
function getPageContent() {
return {
url: window.location.href,
title: document.title,
content: document.body.innerText.substring(0, 2000), // First 2000 chars
elements: Array.from(document.querySelectorAll('input, button, a, textarea, select'))
.map(el => ({
tag: el.tagName.toLowerCase(),
id: el.id || null,
className: el.className || null,
text: el.textContent?.substring(0, 100) || el.value || null,
name: el.name || null,
placeholder: el.placeholder || null,
role: el.getAttribute('role') || null
}))
};
}
// Get specific element information
function getElementInfo(selector) {
const element = document.querySelector(selector);
if (element) {
return {
exists: true,
tag: element.tagName.toLowerCase(),
id: element.id || null,
className: element.className || null,
text: element.textContent?.substring(0, 100) || element.value || null,
name: element.name || null,
placeholder: element.placeholder || null,
role: element.getAttribute('role') || null,
rect: element.getBoundingClientRect(),
isVisible: !!(element.offsetWidth || element.offsetHeight || element.getClientRects().length)
};
} else {
return { exists: false };
}
}
// Execute an action on the page
function executeAction(action, params) {
try {
switch (action) {
case 'click':
return clickElement(params.selector);
case 'fill':
return fillElement(params.selector, params.value);
case 'clickText':
return clickElementByText(params.text);
case 'waitForElement':
return waitForElement(params.selector, params.timeout || 5000);
case 'scrollToElement':
return scrollToElement(params.selector);
case 'extractText':
return extractTextFromElement(params.selector);
default:
return { success: false, error: `Unknown action: ${action}` };
}
} catch (error) {
return { success: false, error: error.message };
}
}
// Helper functions
function clickElement(selector) {
const element = document.querySelector(selector);
if (element) {
element.click();
return { success: true, message: `Clicked element: ${selector}` };
} else {
return { success: false, error: `Element not found: ${selector}` };
}
}
function fillElement(selector, value) {
const element = document.querySelector(selector);
if (element) {
element.value = value;
element.dispatchEvent(new Event('input', { bubbles: true }));
element.dispatchEvent(new Event('change', { bubbles: true }));
element.dispatchEvent(new Event('blur', { bubbles: true })); // Trigger any blur events
return { success: true, message: `Filled element: ${selector} with value: ${value}` };
} else {
return { success: false, error: `Element not found: ${selector}` };
}
}
function clickElementByText(text) {
const elements = Array.from(document.querySelectorAll('button, a, input, textarea, span, div'));
const element = elements.find(el =>
el.textContent?.trim().toLowerCase().includes(text.toLowerCase()) ||
el.value?.toLowerCase().includes(text.toLowerCase()) ||
el.placeholder?.toLowerCase().includes(text.toLowerCase())
);
if (element) {
element.click();
return { success: true, message: `Clicked element with text: ${text}` };
} else {
return { success: false, error: `Element with text not found: ${text}` };
}
}
function waitForElement(selector, timeout) {
return new Promise((resolve) => {
const element = document.querySelector(selector);
if (element) {
resolve({ success: true, message: `Element found immediately: ${selector}` });
return;
}
const observer = new MutationObserver(() => {
const element = document.querySelector(selector);
if (element) {
observer.disconnect();
resolve({ success: true, message: `Element found after waiting: ${selector}` });
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
setTimeout(() => {
observer.disconnect();
resolve({ success: false, error: `Element not found within timeout: ${selector}` });
}, timeout);
});
}
function scrollToElement(selector) {
const element = document.querySelector(selector);
if (element) {
element.scrollIntoView({ behavior: 'smooth', block: 'center' });
return { success: true, message: `Scrolled to element: ${selector}` };
} else {
return { success: false, error: `Element not found: ${selector}` };
}
}
function extractTextFromElement(selector) {
const element = document.querySelector(selector);
if (element) {
return {
success: true,
text: element.textContent || element.value || '',
message: `Extracted text from element: ${selector}`
};
} else {
return { success: false, error: `Element not found: ${selector}` };
}
}
// Expose functions to window for advanced usage if needed
window.qwenAutomation = {
getPageContent,
getElementInfo,
executeAction,
clickElement,
fillElement
};

View File

@@ -0,0 +1 @@
This is a placeholder for the 128x128 icon file. In a real extension, this would be an actual PNG image file.

View File

@@ -0,0 +1 @@
This is a placeholder for the 48x48 icon file. In a real extension, this would be an actual PNG image file.

View File

@@ -0,0 +1,32 @@
{
"manifest_version": 3,
"name": "Qwen AI Automation Suite",
"version": "1.0.0",
"description": "AI-powered browser automation with Qwen integration",
"permissions": [
"activeTab",
"scripting",
"storage"
],
"host_permissions": [
"<all_urls>"
],
"action": {
"default_popup": "popup.html",
"default_title": "Qwen AI Automation"
},
"background": {
"service_worker": "background.js"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
}
],
"icons": {
"16": "icon16.png",
"48": "icon48.png",
"128": "icon128.png"
}
}

View File

@@ -0,0 +1,121 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
body {
width: 350px;
padding: 15px;
font-family: Arial, sans-serif;
background-color: #f5f5f5;
}
.header {
text-align: center;
margin-bottom: 15px;
}
.header h1 {
font-size: 18px;
margin: 0;
color: #1a73e8;
}
.auth-section {
margin-bottom: 15px;
}
.auth-status {
padding: 8px;
border-radius: 4px;
margin-bottom: 10px;
text-align: center;
}
.authenticated {
background-color: #e6f4ea;
color: #137333;
}
.not-authenticated {
background-color: #fce8e6;
color: #c5221f;
}
.task-input {
width: 100%;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
box-sizing: border-box;
margin-bottom: 10px;
}
.execute-btn {
width: 100%;
padding: 10px;
background-color: #1a73e8;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
}
.execute-btn:hover {
background-color: #0d62c9;
}
.execute-btn:disabled {
background-color: #ccc;
cursor: not-allowed;
}
.history {
margin-top: 15px;
max-height: 200px;
overflow-y: auto;
}
.history-item {
padding: 5px;
border-bottom: 1px solid #eee;
font-size: 12px;
}
.loading {
text-align: center;
padding: 10px;
}
.spinner {
border: 2px solid #f3f3f3;
border-top: 2px solid #1a73e8;
border-radius: 50%;
width: 20px;
height: 20px;
animation: spin 1s linear infinite;
margin: 0 auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
</style>
</head>
<body>
<div class="header">
<h1>🤖 Qwen AI Automation</h1>
</div>
<div class="auth-section">
<div id="authStatus" class="auth-status not-authenticated">
Not authenticated with Qwen
</div>
<button id="authBtn" class="execute-btn">Authenticate with Qwen</button>
</div>
<div id="taskSection" style="display: none;">
<textarea id="taskInput" class="task-input" rows="3" placeholder="Describe your automation task..."></textarea>
<button id="executeBtn" class="execute-btn">Execute Task</button>
</div>
<div id="loading" class="loading" style="display: none;">
<div class="spinner"></div>
<div>Processing with AI...</div>
</div>
<div class="history" id="history">
<h3>Recent Tasks</h3>
<div id="historyList"></div>
</div>
<script src="popup.js"></script>
</body>
</html>

View File

@@ -0,0 +1,91 @@
// Popup UI Logic
document.addEventListener('DOMContentLoaded', function() {
const authStatus = document.getElementById('authStatus');
const authBtn = document.getElementById('authBtn');
const taskSection = document.getElementById('taskSection');
const taskInput = document.getElementById('taskInput');
const executeBtn = document.getElementById('executeBtn');
const loading = document.getElementById('loading');
const historyList = document.getElementById('historyList');
// Check authentication status
checkAuthStatus();
// Auth button click handler
authBtn.addEventListener('click', async function() {
try {
// Open authentication flow
await chrome.runtime.sendMessage({ action: 'openAuth' });
} catch (error) {
console.error('Auth error:', error);
}
});
// Execute button click handler
executeBtn.addEventListener('click', async function() {
const task = taskInput.value.trim();
if (!task) return;
// Show loading
executeBtn.disabled = true;
loading.style.display = 'block';
try {
// Send task to background script
const result = await chrome.runtime.sendMessage({
action: 'executeTask',
task: task
});
// Add to history
addToHistory(task, result);
taskInput.value = '';
} catch (error) {
console.error('Execution error:', error);
addToHistory(task, `Error: ${error.message}`);
} finally {
// Hide loading
executeBtn.disabled = false;
loading.style.display = 'none';
}
});
async function checkAuthStatus() {
try {
const response = await chrome.runtime.sendMessage({ action: 'checkAuth' });
if (response.authenticated) {
authStatus.textContent = '✅ Authenticated with Qwen';
authStatus.className = 'auth-status authenticated';
taskSection.style.display = 'block';
} else {
authStatus.textContent = '❌ Not authenticated with Qwen';
authStatus.className = 'auth-status not-authenticated';
taskSection.style.display = 'none';
}
} catch (error) {
console.error('Auth check error:', error);
}
}
function addToHistory(task, result) {
const historyItem = document.createElement('div');
historyItem.className = 'history-item';
historyItem.innerHTML = `
<strong>Task:</strong> ${task}<br>
<strong>Result:</strong> ${result}
`;
historyList.insertBefore(historyItem, historyList.firstChild);
// Limit to 5 items
if (historyList.children.length > 5) {
historyList.removeChild(historyList.lastChild);
}
}
// Listen for auth status updates
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.action === 'authStatusUpdated') {
checkAuthStatus();
}
});
});