mycoder-agent
Version:
Agent module for mycoder - an AI-powered software development assistant
153 lines • 7.29 kB
JavaScript
import { z } from 'zod';
import { zodToJsonSchema } from 'zod-to-json-schema';
import { errorToString } from '../../utils/errorToString.js';
import { sleep } from '../../utils/sleep.js';
import { BrowserDetector } from './lib/BrowserDetector.js';
import { filterPageContent } from './lib/filterPageContent.js';
import { SessionManager } from './lib/SessionManager.js';
import { browserSessions } from './lib/types.js';
import { SessionStatus } from './SessionTracker.js';
const parameterSchema = z.object({
url: z.string().url().optional().describe('Initial URL to navigate to'),
timeout: z
.number()
.optional()
.describe('Default timeout in milliseconds (default: 30000)'),
description: z
.string()
.describe('The reason for starting this browser session (max 80 chars)'),
});
const returnSchema = z.object({
instanceId: z.string(),
status: z.string(),
content: z.string().optional(),
error: z.string().optional(),
});
export const sessionStartTool = {
name: 'sessionStart',
logPrefix: '🏄',
description: 'Starts a new browser session with optional initial URL',
parameters: parameterSchema,
parametersJsonSchema: zodToJsonSchema(parameterSchema),
returns: returnSchema,
returnsJsonSchema: zodToJsonSchema(returnSchema),
execute: async ({ url, timeout = 30000 }, { logger, headless, userSession, pageFilter, browserTracker, ...context // Other parameters
}) => {
// Get config from context if available
const config = context.config || {};
logger.debug(`Starting browser session${url ? ` at ${url}` : ''}`);
logger.debug(`User session mode: ${userSession ? 'enabled' : 'disabled'}`);
logger.debug(`Webpage processing mode: ${pageFilter}`);
try {
// Register this browser session with the tracker
const instanceId = browserTracker.registerBrowser(url);
// Get browser configuration from config
const browserConfig = config.browser || {};
// Create browser configuration
const sessionConfig = {
headless,
defaultTimeout: timeout,
useSystemBrowsers: browserConfig.useSystemBrowsers !== false,
preferredType: browserConfig.preferredType || 'chromium',
executablePath: browserConfig.executablePath,
};
// If userSession is true, use system Chrome
if (userSession) {
logger.debug('User session mode enabled, forcing system Chrome');
sessionConfig.useSystemBrowsers = true;
sessionConfig.preferredType = 'chromium';
// Try to detect Chrome browser
const browsers = await BrowserDetector.detectBrowsers();
const chrome = browsers.find((b) => b.name.toLowerCase().includes('chrome'));
if (chrome) {
logger.debug(`Found system Chrome at ${chrome.path}`);
sessionConfig.executablePath = chrome.path;
}
}
logger.debug(`Browser config: ${JSON.stringify(sessionConfig)}`);
// Create a session manager and launch browser
const sessionManager = new SessionManager();
const session = await sessionManager.createSession(sessionConfig);
// Set the default timeout
session.page.setDefaultTimeout(timeout);
// Get references to the browser and page
const browser = session.browser;
const page = session.page;
// Store the session in the browserSessions map for compatibility
browserSessions.set(instanceId, {
browser,
page,
id: instanceId,
});
// Setup cleanup handlers
browser.on('disconnected', () => {
browserSessions.delete(instanceId);
// Update browser tracker when browser disconnects
browserTracker.updateSessionStatus(instanceId, SessionStatus.TERMINATED);
});
// Navigate to URL if provided
let content = '';
if (url) {
try {
// Try with 'domcontentloaded' first which is more reliable than 'networkidle'
logger.debug(`Navigating to ${url} with 'domcontentloaded' waitUntil`);
await page.goto(url, { waitUntil: 'domcontentloaded', timeout });
await sleep(3000);
content = await filterPageContent(page, pageFilter);
logger.debug(`Content: ${content}`);
logger.debug('Navigation completed with domcontentloaded strategy');
}
catch (error) {
// If that fails, try with no waitUntil option at all (most basic)
logger.warn(`Failed with domcontentloaded strategy: ${errorToString(error)}`);
logger.debug(`Retrying navigation to ${url} with no waitUntil option`);
try {
await page.goto(url, { timeout });
await sleep(3000);
content = await filterPageContent(page, pageFilter);
logger.debug(`Content: ${content}`);
logger.debug('Navigation completed with basic strategy');
}
catch (innerError) {
logger.error(`Failed with basic navigation strategy: ${errorToString(innerError)}`);
throw innerError; // Re-throw to be caught by outer catch block
}
}
}
logger.debug('Browser session started successfully');
logger.debug(`Content length: ${content.length} characters`);
// Update browser tracker with running status
browserTracker.updateSessionStatus(instanceId, SessionStatus.RUNNING, {
url: url || 'about:blank',
contentLength: content.length,
});
return {
instanceId,
status: 'initialized',
content: content || undefined,
};
}
catch (error) {
logger.error(`Failed to start browser: ${errorToString(error)}`);
// No need to update browser tracker here as we don't have a valid instanceId
// when an error occurs before the browser is properly initialized
return {
instanceId: '',
status: 'error',
error: errorToString(error),
};
}
},
logParameters: ({ url, description }, { logger, pageFilter = 'simple' }) => {
logger.log(`Starting browser session${url ? ` at ${url}` : ''} with ${pageFilter} processing, ${description}`);
},
logReturns: (output, { logger }) => {
if (output.error) {
logger.error(`Browser start failed: ${output.error}`);
}
else {
logger.log(`Browser session started with ID: ${output.instanceId}`);
}
},
};
//# sourceMappingURL=sessionStart.js.map