Files
vibn-agent-runner/vibn-frontend/app/api/mcp/browser.ts

147 lines
4.1 KiB
TypeScript

import { NextResponse } from "next/server";
import { execInDevContainer } from "@/lib/dev-container";
/**
* Executes a simple Playwright script inside the dev container to extract browser console logs.
*/
export async function toolBrowserConsole(projectId: string, url: string) {
const script = `
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
const errors = [];
page.on('console', msg => {
if (msg.type() === 'error') {
errors.push(msg.text());
}
});
page.on('pageerror', err => {
errors.push(err.message);
});
try {
await page.goto('${url}', { waitUntil: 'networkidle', timeout: 15000 });
console.log(JSON.stringify({ ok: true, errors }));
} catch (err) {
console.log(JSON.stringify({ ok: true, load_error: err.message, errors }));
} finally {
await browser.close();
}
})();
`;
// Encode the script to base64 to bypass any shell quoting/escaping conflicts
const scriptB64 = Buffer.from(script, "utf8").toString("base64");
const tempFile = `.vibn-console-${projectId}.js`;
const setupCmd = `echo "${scriptB64}" | base64 -d > ${tempFile} && if [ ! -d "node_modules/playwright" ]; then npm install --no-save playwright && npx playwright install chromium; fi && node ${tempFile} && rm -f ${tempFile}`;
try {
const res = await execInDevContainer({
projectId,
command: setupCmd,
timeoutMs: 60000, // 60s since downloading chromium takes a moment on first run
});
if (res.code !== 0) {
return NextResponse.json(
{ error: "Failed to run browser test", details: res.stderr },
{ status: 500 },
);
}
const lines = res.stdout.trim().split("\n");
const jsonLine = lines[lines.length - 1];
let result;
try {
result = JSON.parse(jsonLine);
} catch {
result = {
ok: false,
error: "Could not parse browser output",
raw: res.stdout,
};
}
return NextResponse.json({ result });
} catch (e) {
return NextResponse.json(
{ error: e instanceof Error ? e.message : String(e) },
{ status: 500 },
);
}
}
/**
* Executes a simple Playwright script inside the dev container to extract the page title and status code.
*/
export async function toolBrowserNavigate(projectId: string, url: string) {
const script = `
const { chromium } = require('playwright');
(async () => {
const browser = await chromium.launch();
const context = await browser.newContext();
const page = await context.newPage();
try {
const response = await page.goto('${url}', { waitUntil: 'domcontentloaded', timeout: 15000 });
const title = await page.title();
console.log(JSON.stringify({ ok: true, status: response ? response.status() : null, title }));
} catch (err) {
console.log(JSON.stringify({ ok: true, load_error: err.message }));
} finally {
await browser.close();
}
})();
`;
// Encode the script to base64 to bypass any shell quoting/escaping conflicts
const scriptB64 = Buffer.from(script, "utf8").toString("base64");
const tempFile = `.vibn-navigate-${projectId}.js`;
const setupCmd = `echo "${scriptB64}" | base64 -d > ${tempFile} && if [ ! -d "node_modules/playwright" ]; then npm install --no-save playwright && npx playwright install chromium; fi && node ${tempFile} && rm -f ${tempFile}`;
try {
const res = await execInDevContainer({
projectId,
command: setupCmd,
timeoutMs: 60000,
});
if (res.code !== 0) {
return NextResponse.json(
{ error: "Failed to navigate", details: res.stderr },
{ status: 500 },
);
}
const lines = res.stdout.trim().split("\n");
const jsonLine = lines[lines.length - 1];
let result;
try {
result = JSON.parse(jsonLine);
} catch {
result = {
ok: false,
error: "Could not parse browser output",
raw: res.stdout,
};
}
return NextResponse.json({ result });
} catch (e) {
return NextResponse.json(
{ error: e instanceof Error ? e.message : String(e) },
{ status: 500 },
);
}
}