veil-browser 0.1.2 → 0.1.4
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/dist/ai.js +61 -9
- package/package.json +1 -1
package/dist/ai.js
CHANGED
|
@@ -124,10 +124,31 @@ Return JSON array of action steps:`;
|
|
|
124
124
|
throw new Error(`Ollama error: ${response.status} ${await response.text()}`);
|
|
125
125
|
const data = await response.json();
|
|
126
126
|
const content = data.message?.content ?? '';
|
|
127
|
-
|
|
127
|
+
let jsonMatch = content.match(/\[[\s\S]*\]/);
|
|
128
128
|
if (!jsonMatch)
|
|
129
129
|
throw new Error('Ollama returned no valid JSON array');
|
|
130
|
-
|
|
130
|
+
let jsonStr = jsonMatch[0];
|
|
131
|
+
try {
|
|
132
|
+
return JSON.parse(jsonStr);
|
|
133
|
+
}
|
|
134
|
+
catch {
|
|
135
|
+
jsonStr = jsonStr.replace(/("description":\s*)"([^"]*)"([^"]*?)"([^"]*)"/g, '$1"$2\\\"$3\\\"$4"');
|
|
136
|
+
try {
|
|
137
|
+
return JSON.parse(jsonStr);
|
|
138
|
+
}
|
|
139
|
+
catch {
|
|
140
|
+
const actions = [];
|
|
141
|
+
const blocks = jsonStr.match(/\{\s*"?action"?\s*:\s*"([^"]+)"/g) ?? [];
|
|
142
|
+
blocks.forEach((block) => {
|
|
143
|
+
const actionMatch = block.match(/"action"\s*:\s*"([^"]+)"/);
|
|
144
|
+
if (actionMatch)
|
|
145
|
+
actions.push({ action: actionMatch[1] });
|
|
146
|
+
});
|
|
147
|
+
if (actions.length === 0)
|
|
148
|
+
throw new Error('Could not extract actions from Ollama response');
|
|
149
|
+
return actions;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
131
152
|
}
|
|
132
153
|
if (llm.provider === 'openai' || llm.provider === 'openrouter') {
|
|
133
154
|
const baseUrl = llm.provider === 'openrouter'
|
|
@@ -174,22 +195,53 @@ Return JSON array of action steps:`;
|
|
|
174
195
|
const content = llm.provider === 'anthropic'
|
|
175
196
|
? data.content[0].text
|
|
176
197
|
: data.choices[0].message.content;
|
|
177
|
-
// Extract JSON
|
|
178
|
-
|
|
198
|
+
// Extract JSON more robustly — handles malformed JSON
|
|
199
|
+
let jsonMatch = content.match(/\[[\s\S]*\]/);
|
|
179
200
|
if (!jsonMatch)
|
|
180
201
|
throw new Error('LLM returned no valid JSON array');
|
|
181
|
-
|
|
202
|
+
let jsonStr = jsonMatch[0];
|
|
203
|
+
// Try to parse as-is first
|
|
204
|
+
try {
|
|
205
|
+
return JSON.parse(jsonStr);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
// If that fails, try to fix common issues
|
|
209
|
+
// Remove unescaped quotes in description fields
|
|
210
|
+
jsonStr = jsonStr.replace(/("description":\s*)"([^"]*)"([^"]*?)"([^"]*)"/g, '$1"$2\\\"$3\\\"$4"');
|
|
211
|
+
try {
|
|
212
|
+
return JSON.parse(jsonStr);
|
|
213
|
+
}
|
|
214
|
+
catch {
|
|
215
|
+
// Last resort: extract actions manually
|
|
216
|
+
const actions = [];
|
|
217
|
+
const blocks = jsonStr.match(/\{\s*"?action"?\s*:\s*"([^"]+)"/g) ?? [];
|
|
218
|
+
blocks.forEach((block) => {
|
|
219
|
+
const actionMatch = block.match(/"action"\s*:\s*"([^"]+)"/);
|
|
220
|
+
if (actionMatch)
|
|
221
|
+
actions.push({ action: actionMatch[1] });
|
|
222
|
+
});
|
|
223
|
+
if (actions.length === 0)
|
|
224
|
+
throw new Error('Could not extract actions from LLM response');
|
|
225
|
+
return actions;
|
|
226
|
+
}
|
|
227
|
+
}
|
|
182
228
|
}
|
|
183
|
-
// Execute a single action step
|
|
229
|
+
// Execute a single action step with X-specific handling
|
|
184
230
|
async function executeStep(page, step) {
|
|
185
231
|
switch (step.action) {
|
|
186
232
|
case 'click': {
|
|
187
233
|
if (!step.selector)
|
|
188
234
|
throw new Error('click requires selector');
|
|
189
|
-
// Try multiple selector strategies
|
|
190
235
|
const el = page.locator(step.selector).first();
|
|
191
236
|
await el.waitFor({ timeout: 5000 }).catch(() => { });
|
|
192
|
-
|
|
237
|
+
// For X, use force click to bypass overlay interceptors
|
|
238
|
+
const isX = page.url().includes('x.com') || page.url().includes('twitter.com');
|
|
239
|
+
if (isX) {
|
|
240
|
+
await el.click({ force: true, timeout: 5000 });
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
await el.click({ timeout: 5000 });
|
|
244
|
+
}
|
|
193
245
|
await humanDelay(300, 700);
|
|
194
246
|
break;
|
|
195
247
|
}
|
|
@@ -198,7 +250,7 @@ async function executeStep(page, step) {
|
|
|
198
250
|
throw new Error('type requires selector and text');
|
|
199
251
|
const el = page.locator(step.selector).first();
|
|
200
252
|
await el.waitFor({ timeout: 5000 }).catch(() => { });
|
|
201
|
-
await el.click();
|
|
253
|
+
await el.click({ force: true });
|
|
202
254
|
await humanDelay(200, 400);
|
|
203
255
|
// Type with human-like delays
|
|
204
256
|
for (const char of step.text) {
|