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.
Files changed (2) hide show
  1. package/dist/ai.js +61 -9
  2. 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
- const jsonMatch = content.match(/\[[\s\S]*\]/);
127
+ let jsonMatch = content.match(/\[[\s\S]*\]/);
128
128
  if (!jsonMatch)
129
129
  throw new Error('Ollama returned no valid JSON array');
130
- return JSON.parse(jsonMatch[0]);
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 from response
178
- const jsonMatch = content.match(/\[[\s\S]*\]/);
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
- return JSON.parse(jsonMatch[0]);
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
- await el.click({ timeout: 5000 });
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) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "veil-browser",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "Stealth browser CLI for AI agents — bypass bot detection, persist sessions, local CAPTCHA solving, MCP server",
5
5
  "keywords": [
6
6
  "browser",