mirror of
https://github.com/browser-use/browser-use
synced 2026-05-06 17:52:15 +02:00
224 lines
7.3 KiB
HTML
224 lines
7.3 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Browser Agent Controller</title>
|
|
<style>
|
|
body {
|
|
font-family: Arial, sans-serif;
|
|
max-width: 800px;
|
|
margin: 0 auto;
|
|
padding: 20px;
|
|
background-color: #f5f5f5;
|
|
}
|
|
.container {
|
|
background-color: white;
|
|
padding: 20px;
|
|
border-radius: 8px;
|
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
|
}
|
|
.control-panel {
|
|
display: grid;
|
|
gap: 10px;
|
|
margin-top: 20px;
|
|
}
|
|
button {
|
|
padding: 10px;
|
|
border: none;
|
|
border-radius: 4px;
|
|
background-color: #007bff;
|
|
color: white;
|
|
cursor: pointer;
|
|
transition: background-color 0.3s;
|
|
}
|
|
button:disabled {
|
|
background-color: #cccccc;
|
|
cursor: not-allowed;
|
|
}
|
|
button:hover:not(:disabled) {
|
|
background-color: #0056b3;
|
|
}
|
|
#status {
|
|
margin-top: 20px;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
}
|
|
.status-running { background-color: #d4edda; color: #155724; }
|
|
.status-paused { background-color: #fff3cd; color: #856404; }
|
|
.status-stopped { background-color: #f8d7da; color: #721c24; }
|
|
.status-not-created { background-color: #e2e3e5; color: #383d41; }
|
|
.status-ready { background-color: #cce5ff; color: #004085; }
|
|
textarea {
|
|
width: 100%;
|
|
padding: 10px;
|
|
margin-bottom: 10px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 4px;
|
|
resize: vertical;
|
|
}
|
|
#logContainer {
|
|
margin-top: 20px;
|
|
padding: 10px;
|
|
background-color: #f8f9fa;
|
|
border-radius: 4px;
|
|
border: 1px solid #ddd;
|
|
height: 300px;
|
|
overflow-y: auto;
|
|
font-family: monospace;
|
|
font-size: 14px;
|
|
white-space: pre-wrap;
|
|
}
|
|
.log-entry {
|
|
margin: 2px 0;
|
|
padding: 2px 5px;
|
|
}
|
|
.button-group {
|
|
display: grid;
|
|
grid-template-columns: repeat(3, 1fr);
|
|
gap: 10px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>Browser Agent Controller</h1>
|
|
|
|
<div class="control-panel">
|
|
<textarea id="taskInput" rows="3" placeholder="Enter your task here..."></textarea>
|
|
<div class="button-group">
|
|
<button id="runBtn" onclick="runAgent()">Run Agent</button>
|
|
<button id="pauseBtn" onclick="pauseAgent()" disabled>Pause</button>
|
|
<button id="resumeBtn" onclick="resumeAgent()" disabled>Resume</button>
|
|
<button id="stopBtn" onclick="stopAgent()" disabled>Stop</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="status" class="status-not-created">
|
|
Status: Not Created
|
|
</div>
|
|
|
|
<div id="logContainer">
|
|
<div id="logs"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
let statusCheckInterval;
|
|
let eventSource;
|
|
|
|
function setupLogging() {
|
|
if (eventSource) {
|
|
eventSource.close();
|
|
}
|
|
|
|
eventSource = new EventSource('/logs');
|
|
const logsDiv = document.getElementById('logs');
|
|
|
|
eventSource.addEventListener('log', function(event) {
|
|
const logEntry = document.createElement('div');
|
|
logEntry.className = 'log-entry';
|
|
logEntry.textContent = event.data;
|
|
logsDiv.appendChild(logEntry);
|
|
logsDiv.scrollTop = logsDiv.scrollHeight;
|
|
});
|
|
|
|
eventSource.onerror = function(error) {
|
|
console.error('EventSource failed:', error);
|
|
eventSource.close();
|
|
setTimeout(setupLogging, 5000); // Try to reconnect after 5 seconds
|
|
};
|
|
}
|
|
|
|
async function runAgent() {
|
|
const task = document.getElementById('taskInput').value;
|
|
if (!task) {
|
|
alert('Please enter a task');
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const response = await fetch('/agent/run', {
|
|
method: 'POST',
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
},
|
|
body: JSON.stringify({ task }),
|
|
});
|
|
|
|
if (response.ok) {
|
|
startStatusCheck();
|
|
document.getElementById('taskInput').disabled = true;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
alert('Failed to run agent');
|
|
}
|
|
}
|
|
|
|
async function pauseAgent() {
|
|
try {
|
|
await fetch('/agent/pause', { method: 'POST' });
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
alert('Failed to pause agent');
|
|
}
|
|
}
|
|
|
|
async function resumeAgent() {
|
|
try {
|
|
await fetch('/agent/resume', { method: 'POST' });
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
alert('Failed to resume agent');
|
|
}
|
|
}
|
|
|
|
async function stopAgent() {
|
|
try {
|
|
await fetch('/agent/stop', { method: 'POST' });
|
|
document.getElementById('taskInput').disabled = false;
|
|
} catch (error) {
|
|
console.error('Error:', error);
|
|
alert('Failed to stop agent');
|
|
}
|
|
}
|
|
|
|
async function checkStatus() {
|
|
try {
|
|
const response = await fetch('/agent/status');
|
|
const data = await response.json();
|
|
|
|
const statusDiv = document.getElementById('status');
|
|
statusDiv.className = `status-${data.status}`;
|
|
statusDiv.textContent = `Status: ${data.status.charAt(0).toUpperCase() + data.status.slice(1)}`;
|
|
|
|
// Update buttons based on status
|
|
document.getElementById('runBtn').disabled = data.status === 'running';
|
|
document.getElementById('pauseBtn').disabled = data.status !== 'running';
|
|
document.getElementById('resumeBtn').disabled = data.status !== 'paused';
|
|
document.getElementById('stopBtn').disabled = data.status === 'stopped' || data.status === 'not_created';
|
|
|
|
if (data.task) {
|
|
statusDiv.textContent += `\nTask: ${data.task}`;
|
|
}
|
|
} catch (error) {
|
|
console.error('Error checking status:', error);
|
|
}
|
|
}
|
|
|
|
function startStatusCheck() {
|
|
// Check status immediately
|
|
checkStatus();
|
|
// Then check every 2 seconds
|
|
if (!statusCheckInterval) {
|
|
statusCheckInterval = setInterval(checkStatus, 2000);
|
|
}
|
|
}
|
|
|
|
// Initial setup
|
|
setupLogging();
|
|
checkStatus();
|
|
</script>
|
|
</body>
|
|
</html> |