remove eval

This commit is contained in:
Arjun 2026-05-06 12:17:21 +05:30
parent b9a62f6662
commit 629df2d83a
4 changed files with 2 additions and 85 deletions

View file

@ -254,20 +254,6 @@ export class ElectronBrowserControlService implements IBrowserControlService {
const page = await browserViewManager.readPageSummary(signal, { waitForReady: false }) ?? undefined;
return buildSuccessResult('wait', `Waited ${duration}ms for the page to settle.`, page);
}
case 'eval': {
const code = input.code;
if (!code) {
return buildErrorResult('eval', 'code is required for eval.');
}
await browserViewManager.ensureActiveTabReady(signal);
const result = await browserViewManager.executeScript(code, signal);
if (!result.ok) {
return buildErrorResult('eval', result.error);
}
const success = buildSuccessResult('eval', 'Evaluated script in the active tab.');
return { ...success, result: result.result };
}
}
} catch (error) {
return buildErrorResult(

View file

@ -78,41 +78,6 @@ function abortIfNeeded(signal?: AbortSignal): void {
throw signal.reason instanceof Error ? signal.reason : new Error('Browser action aborted');
}
const EVAL_RESULT_MAX_BYTES = 200_000;
function safeSerialize(value: unknown): unknown {
const seen = new WeakSet<object>();
const coerce = (v: unknown): unknown => {
if (v === null || v === undefined) return v;
const t = typeof v;
if (t === 'string' || t === 'number' || t === 'boolean') return v;
if (t === 'bigint') return (v as bigint).toString();
if (t === 'function' || t === 'symbol') return `[${t}]`;
if (typeof v === 'object') {
if (seen.has(v as object)) return '[circular]';
seen.add(v as object);
if (Array.isArray(v)) return v.map(coerce);
const out: Record<string, unknown> = {};
for (const [k, val] of Object.entries(v as Record<string, unknown>)) {
out[k] = coerce(val);
}
return out;
}
return String(v);
};
const coerced = coerce(value);
try {
const json = JSON.stringify(coerced);
if (json && json.length > EVAL_RESULT_MAX_BYTES) {
return { truncated: true, preview: json.slice(0, EVAL_RESULT_MAX_BYTES) };
}
} catch {
return String(value);
}
return coerced;
}
async function sleep(ms: number, signal?: AbortSignal): Promise<void> {
if (ms <= 0) return;
abortIfNeeded(signal);
@ -813,17 +778,6 @@ export class BrowserViewManager extends EventEmitter {
await this.waitForWebContentsSettle(activeTab, signal);
}
async executeScript(code: string, signal?: AbortSignal): Promise<{ ok: true; result: unknown } | { ok: false; error: string }> {
try {
const wrapped = `(async () => { ${code} \n})()`;
const raw = await this.executeOnActiveTab<unknown>(wrapped, signal);
const serialized = safeSerialize(raw);
return { ok: true, result: serialized };
} catch (error) {
return { ok: false, error: error instanceof Error ? error.message : 'Script evaluation failed.' };
}
}
getState(): BrowserState {
return this.snapshotState();
}

View file

@ -94,17 +94,6 @@ Wait for the page to settle, useful after async UI changes.
Parameters:
- ` + "`ms`" + `: milliseconds to wait (optional)
### eval
Run arbitrary JavaScript in the active tab and return its value. Use this as an escape hatch when the structured actions above are insufficient for example, submitting a form (` + "`form.submit()`" + `), reading DOM state (` + "`document.querySelector(...).textContent`" + `), or computing something that requires page-scoped APIs.
Parameters:
- ` + "`code`" + `: JavaScript source. The code runs inside an ` + "`async`" + ` IIFE, so you can ` + "`await`" + ` freely. The final expression's value (or a ` + "`return`" + `ed value) is serialized back. Non-serializable values (DOM nodes, functions) are coerced to placeholder strings. Large results are truncated.
Example:
- ` + "`{ action: \"eval\", code: \"return document.querySelector('meta[name=user-login]')?.content ?? null\" }`" + `
Security: ` + "`eval`" + ` runs in the active tab's origin with the user's cookies. Do not exfiltrate credentials, cookies, or localStorage contents to third-party origins.
## Companion Tools
### load-browser-skill
@ -112,7 +101,7 @@ Rowboat caches a library of browser skills (from ` + "`browser-use/browser-harne
You can also proactively call ` + "`load-browser-skill({ action: \"list\", site: \"<site>\" })`" + ` when you know you're about to work on a site, to see what skills exist even if ` + "`suggestedSkills`" + ` is empty (e.g. before navigating).
These skills are written against a Python harness, so treat them as **reference knowledge** adapt the recipes into the actions above, especially structured browser actions plus ` + "`eval`" + ` for the ` + "`js(...)`" + ` and ` + "`http_get(...)`" + ` style calls they use. **Do not look for or call ` + "`http-fetch`" + ` it is not available.** If a harness recipe suggests hitting a public API directly, either use ` + "`eval`" + ` with ` + "`fetch()`" + ` inside the page context when that is truly necessary, or fall back to reading and interacting with the page itself. The selectors, DOM gotchas, and sequencing are the durable part; the exact function names are not.
These skills are written against a Python harness, so treat them as **reference knowledge**. Reuse the selectors, timing, and sequencing, but adapt them to Rowboat's structured browser actions. **Do not look for or call ` + "`http-fetch`" + `.** If a browser-harness recipe suggests ` + "`js(...)`" + ` or ` + "`http_get(...)`" + ` style shortcuts, treat those as non-portable and fall back to reading and interacting with the page itself.
## Important Rules
@ -121,8 +110,7 @@ These skills are written against a Python harness, so treat them as **reference
- If the tool says the snapshot is stale, call ` + "`read-page`" + ` again.
- After navigation, clicking, typing, pressing, or scrolling, use the returned page snapshot instead of assuming the page state.
- **Always check ` + "`suggestedSkills`" + ` after ` + "`navigate`" + `, ` + "`new-tab`" + `, or ` + "`read-page`" + `, and load the matching domain or interaction skill before acting.** Skipping this step is the single most common way to waste a dozen failed clicks on a site whose quirks are already documented. If the array is empty, proceed normally but don't skip the check.
- Prefer structured actions (click/type/press) over ` + "`eval`" + ` when both work. Reach for ` + "`eval`" + ` when the site fights synthetic events, when you need to submit a form directly, or when you need to read DOM state the structured actions don't surface.
- Do not try to use ` + "`http-fetch`" + `. If a browser-harness recipe mentions ` + "`http_get(...)`" + ` or a public API shortcut, adapt it to ` + "`eval`" + ` or DOM-based browsing instead.
- Do not try to use ` + "`http-fetch`" + `. If a browser-harness recipe mentions ` + "`http_get(...)`" + ` or a public API shortcut, adapt it to DOM-based browsing instead.
- Use Rowboat's browser for live interaction. Use web search tools for research where a live session is unnecessary.
- Do not wrap browser URLs or browser pages in ` + "```filepath" + ` blocks. Filepath cards are only for real files on disk, not web pages or browser tabs.
- If you mention a page the browser opened, use plain text for the URL/title instead of trying to create a clickable file card.

View file

@ -51,7 +51,6 @@ export const BrowserControlActionSchema = z.enum([
'press',
'scroll',
'wait',
'eval',
]);
const BrowserElementTargetFields = {
@ -71,7 +70,6 @@ export const BrowserControlInputSchema = z.object({
ms: z.number().int().positive().max(30000).optional(),
maxElements: z.number().int().positive().max(100).optional(),
maxTextLength: z.number().int().positive().max(20000).optional(),
code: z.string().min(1).max(50000).optional(),
...BrowserElementTargetFields,
}).strict().superRefine((value, ctx) => {
const needsElementTarget = value.action === 'click' || value.action === 'type';
@ -116,14 +114,6 @@ export const BrowserControlInputSchema = z.object({
message: 'Provide an element index or selector.',
});
}
if (value.action === 'eval' && !value.code) {
ctx.addIssue({
code: z.ZodIssueCode.custom,
path: ['code'],
message: 'code is required for eval.',
});
}
});
export const SuggestedBrowserSkillSchema = z.object({
@ -139,7 +129,6 @@ export const BrowserControlResultSchema = z.object({
error: z.string().optional(),
browser: BrowserStateSchema,
page: BrowserPageSnapshotSchema.optional(),
result: z.unknown().optional(),
suggestedSkills: z.array(SuggestedBrowserSkillSchema).optional(),
});