mona-agent / apps /desktop /src /tools /browser.js
mo
feat: enterprise capability expansion β€” app control, browser control, background execution
fce80d1
Raw History Blame Contribute Delete
3.79 kB
// Browser control β€” open URLs and run web searches in the default browser.
// Multi-OS: macOS (open + osascript), Linux (xdg-open), Windows (start).
import { exec } from 'node:child_process';
import { promisify } from 'node:util';
import os from 'node:os';
const pexec = promisify(exec);
const PLATFORM = os.platform();
/** Build a search URL for the requested site. Exported for tests. */
export function searchUrl(query, site = 'web') {
const q = encodeURIComponent(String(query || '').slice(0, 300));
switch (String(site).toLowerCase()) {
case 'youtube':
return `https://www.youtube.com/results?search_query=${q}`;
case 'google':
return `https://www.google.com/search?q=${q}`;
case 'web':
default:
return `https://www.bing.com/search?q=${q}`;
}
}
/** Validate a URL for the open action. Exported for tests. */
export function validateUrl(url) {
try {
const u = new URL(String(url));
if (!['http:', 'https:'].includes(u.protocol)) return null;
return u.toString();
} catch {
return null;
}
}
async function openWith(cmd, timeoutMs = 8000) {
try {
await pexec(cmd, { timeout: timeoutMs, maxBuffer: 1 << 20 });
return { exitCode: 0, opened: true };
} catch (err) {
return { exitCode: err.code ?? 1, error: err.message, opened: false };
}
}
export const browser = {
name: 'browser',
description: `Open URLs or run web searches (YouTube, Google, web) in the default browser (${PLATFORM}).`,
args: {
action: 'string β€” open | search | watch',
url: 'string β€” full URL (for open)',
query: 'string β€” search terms (for search/watch)',
site: 'string β€” youtube | google | web (default web)',
},
platform: PLATFORM,
async run(args) {
const action = String(args.action || 'open').toLowerCase();
let url = null;
if (action === 'open') {
url = validateUrl(args.url);
if (!url) return { error: 'A valid http(s) URL is required (url=...)' };
} else if (action === 'search' || action === 'watch') {
const site = args.site || (action === 'watch' ? 'youtube' : 'web');
url = searchUrl(args.query || '', site);
if (!String(args.query || '').trim()) return { error: 'query required' };
} else {
return { error: `Unknown action: ${action} (use open | search | watch)` };
}
if (PLATFORM === 'darwin') {
const res = await openWith(`open "${url}"`);
if (action === 'watch') {
// Best effort: ask Safari to play the first video result. Requires
// "Allow JavaScript from Apple Events" in Safari β€” falls back to the
// results page if the setting is off.
try {
await pexec(
`osascript -e 'tell application "Safari" to activate' -e 'delay 2' -e 'tell application "Safari" to do JavaScript "document.querySelector(\\"ytd-video-renderer a#video-title\\")?.click()" in front document'`,
{ timeout: 12000 }
);
res.autoplayAttempted = true;
} catch {
res.autoplayAttempted = false;
res.note = 'Opened the results page. Click the first result to play (Safari automation requires "Allow JavaScript from Apple Events").';
}
}
return { ...res, url };
}
if (PLATFORM === 'linux') {
const res = await openWith(`xdg-open "${url}"`);
return { ...res, url, note: action === 'watch' ? 'Opened the search results page β€” click the first result to play.' : undefined };
}
if (PLATFORM === 'win32') {
const res = await openWith(`start "" "${url}"`);
return { ...res, url, note: action === 'watch' ? 'Opened the search results page β€” click the first result to play.' : undefined };
}
return { error: `Unsupported platform: ${PLATFORM}` };
},
};