vision-navigator 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +113 -0
- package/dist/cdp-driver.js +484 -0
- package/dist/cli.js +207 -0
- package/dist/injected-scripts.js +328 -0
- package/dist/navigator.js +897 -0
- package/dist/server.js +409 -0
- package/dist/storage.js +132 -0
- package/package.json +48 -0
- package/public/app.js +155 -0
- package/public/index.html +567 -0
package/public/app.js
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
2
|
+
const runBtn = document.getElementById('runBtn');
|
|
3
|
+
const yamlInput = document.getElementById('yamlInput');
|
|
4
|
+
const btnText = document.getElementById('btnText');
|
|
5
|
+
const loader = document.getElementById('loader');
|
|
6
|
+
const statusMessage = document.getElementById('statusMessage');
|
|
7
|
+
const resultsContainer = document.getElementById('resultsContainer');
|
|
8
|
+
const basePath = window.location.pathname.endsWith('/') ? window.location.pathname : window.location.pathname + '/';
|
|
9
|
+
const urlFor = (p) => basePath + String(p || '').replace(/^\//, '');
|
|
10
|
+
|
|
11
|
+
// Default placeholder
|
|
12
|
+
yamlInput.value = `steps:
|
|
13
|
+
- instruction: "Navigate to https://quotes.toscrape.com"
|
|
14
|
+
- instruction: "Click the 'Login' link"
|
|
15
|
+
- instruction: "Type 'admin' into the username field"
|
|
16
|
+
- instruction: "Type 'password' into the password field"
|
|
17
|
+
- instruction: "Click the 'Login' button"`;
|
|
18
|
+
|
|
19
|
+
runBtn.addEventListener('click', async () => {
|
|
20
|
+
const yamlContent = yamlInput.value.trim();
|
|
21
|
+
|
|
22
|
+
if (!yamlContent) {
|
|
23
|
+
alert('Please enter a YAML workflow.');
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Set Loading State
|
|
28
|
+
runBtn.disabled = true;
|
|
29
|
+
runBtn.classList.add('opacity-75', 'cursor-not-allowed');
|
|
30
|
+
btnText.innerText = 'Executing...';
|
|
31
|
+
loader.classList.remove('hidden');
|
|
32
|
+
|
|
33
|
+
statusMessage.innerText = 'Workflow is currently running. The AI is processing your instructions in a headless browser...';
|
|
34
|
+
statusMessage.classList.remove('hidden');
|
|
35
|
+
resultsContainer.innerHTML = '';
|
|
36
|
+
resultsContainer.classList.add('hidden');
|
|
37
|
+
|
|
38
|
+
try {
|
|
39
|
+
const response = await fetch(urlFor('workflow'), {
|
|
40
|
+
method: 'POST',
|
|
41
|
+
headers: {
|
|
42
|
+
'Content-Type': 'text/yaml'
|
|
43
|
+
},
|
|
44
|
+
body: yamlContent
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
const data = await response.json();
|
|
48
|
+
|
|
49
|
+
if (!response.ok) {
|
|
50
|
+
throw new Error(data.error || 'Unknown error occurred');
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
renderResults(data.run?.results || data.results);
|
|
54
|
+
|
|
55
|
+
} catch (error) {
|
|
56
|
+
statusMessage.innerHTML = `<span class="text-red-600 font-semibold">Error:</span> ${error.message}`;
|
|
57
|
+
} finally {
|
|
58
|
+
// Reset Loading State
|
|
59
|
+
runBtn.disabled = false;
|
|
60
|
+
runBtn.classList.remove('opacity-75', 'cursor-not-allowed');
|
|
61
|
+
btnText.innerText = 'Run Workflow';
|
|
62
|
+
loader.classList.add('hidden');
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
function renderResults(results) {
|
|
67
|
+
if (!results || results.length === 0) {
|
|
68
|
+
statusMessage.innerText = 'Workflow executed but returned no results.';
|
|
69
|
+
return;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
statusMessage.classList.add('hidden');
|
|
73
|
+
resultsContainer.classList.remove('hidden');
|
|
74
|
+
|
|
75
|
+
results.forEach((step, index) => {
|
|
76
|
+
const stepDiv = document.createElement('div');
|
|
77
|
+
stepDiv.className = 'border border-gray-200 rounded-lg p-4 bg-gray-50';
|
|
78
|
+
|
|
79
|
+
// Header
|
|
80
|
+
const header = document.createElement('h3');
|
|
81
|
+
header.className = 'text-lg font-bold text-blue-700 mb-2';
|
|
82
|
+
header.innerText = `Step ${index + 1}: ${step.step}`;
|
|
83
|
+
stepDiv.appendChild(header);
|
|
84
|
+
|
|
85
|
+
// Action taken
|
|
86
|
+
if (step.action) {
|
|
87
|
+
const actionDiv = document.createElement('div');
|
|
88
|
+
actionDiv.className = 'bg-gray-800 text-green-400 p-3 rounded text-sm font-mono mb-4 whitespace-pre-wrap';
|
|
89
|
+
|
|
90
|
+
// Try to parse action if it's JSON string
|
|
91
|
+
let displayAction = step.action;
|
|
92
|
+
try {
|
|
93
|
+
const parsed = JSON.parse(step.action);
|
|
94
|
+
displayAction = JSON.stringify(parsed, null, 2);
|
|
95
|
+
} catch(e) {}
|
|
96
|
+
|
|
97
|
+
actionDiv.innerText = displayAction;
|
|
98
|
+
stepDiv.appendChild(actionDiv);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Console Logs
|
|
102
|
+
if (step.logs && step.logs.length > 0) {
|
|
103
|
+
const logsDiv = document.createElement('div');
|
|
104
|
+
logsDiv.className = 'bg-black text-gray-300 p-3 rounded text-xs font-mono mb-4 h-32 overflow-y-auto';
|
|
105
|
+
|
|
106
|
+
const logsTitle = document.createElement('div');
|
|
107
|
+
logsTitle.className = 'text-gray-500 mb-1 border-b border-gray-700 pb-1';
|
|
108
|
+
logsTitle.innerText = 'Browser Console Logs:';
|
|
109
|
+
logsDiv.appendChild(logsTitle);
|
|
110
|
+
|
|
111
|
+
step.logs.forEach(log => {
|
|
112
|
+
const logLine = document.createElement('div');
|
|
113
|
+
logLine.innerText = log;
|
|
114
|
+
if (log.includes('ERROR')) logLine.className = 'text-red-400';
|
|
115
|
+
else if (log.includes('WARN')) logLine.className = 'text-yellow-400';
|
|
116
|
+
logsDiv.appendChild(logLine);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
stepDiv.appendChild(logsDiv);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Screenshots
|
|
123
|
+
if (step.screenshots) {
|
|
124
|
+
const imgGrid = document.createElement('div');
|
|
125
|
+
imgGrid.className = 'grid grid-cols-1 md:grid-cols-2 gap-4 mt-4';
|
|
126
|
+
|
|
127
|
+
if (step.screenshots.before) {
|
|
128
|
+
const beforeDiv = document.createElement('div');
|
|
129
|
+
beforeDiv.innerHTML = `
|
|
130
|
+
<p class="text-sm font-semibold text-gray-600 mb-1">Before Action</p>
|
|
131
|
+
<a href="${urlFor(step.screenshots.before)}" target="_blank">
|
|
132
|
+
<img src="${urlFor(step.screenshots.before)}" class="w-full border border-gray-300 rounded shadow-sm hover:opacity-90 transition cursor-pointer" alt="Before">
|
|
133
|
+
</a>
|
|
134
|
+
`;
|
|
135
|
+
imgGrid.appendChild(beforeDiv);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (step.screenshots.after) {
|
|
139
|
+
const afterDiv = document.createElement('div');
|
|
140
|
+
afterDiv.innerHTML = `
|
|
141
|
+
<p class="text-sm font-semibold text-gray-600 mb-1">After Action</p>
|
|
142
|
+
<a href="${urlFor(step.screenshots.after)}" target="_blank">
|
|
143
|
+
<img src="${urlFor(step.screenshots.after)}" class="w-full border border-gray-300 rounded shadow-sm hover:opacity-90 transition cursor-pointer" alt="After">
|
|
144
|
+
</a>
|
|
145
|
+
`;
|
|
146
|
+
imgGrid.appendChild(afterDiv);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
stepDiv.appendChild(imgGrid);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
resultsContainer.appendChild(stepDiv);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
});
|