diff --git a/docs/cypress-cloud.md b/docs/cypress-cloud.md index 0d2838a9f1..64465000b3 100644 --- a/docs/cypress-cloud.md +++ b/docs/cypress-cloud.md @@ -1,2 +1,4 @@ -Could you navigate to this URL in interactive mode (isInteractive = true), and keep it open while I chat with you, DO NOT DO ANYTHING UNTIL I TELL YOU TO CONTINUE. -https://cloud.cypress.io/projects/hkawvm/runs/22055/overview/b890f243-ce56-4047-9252-c99469dfbbea?roarHideRunsWithDiffGroupsAndTags=1&interactive=true \ No newline at end of file +Could you navigate to this URL in interactive mode (browserPort = 7333) +https://cloud.cypress.io/projects/hkawvm/runs/22055/overview/b890f243-ce56-4047-9252-c99469dfbbea?roarHideRunsWithDiffGroupsAndTags=1&interactive=true + +Please analyze the failure and let me know if you can fix it. \ No newline at end of file diff --git a/src/core/Cline.ts b/src/core/Cline.ts index 5dfd0e20b5..321fe34545 100644 --- a/src/core/Cline.ts +++ b/src/core/Cline.ts @@ -74,6 +74,7 @@ export class Cline { private browserSession: BrowserSession private didEditFile: boolean = false private isInteractiveMode: boolean = false + private browserPort: string = '7333' customInstructions?: string alwaysAllowReadOnly: boolean alwaysAllowWrite: boolean @@ -669,16 +670,22 @@ export class Cline { newUserContent.push(...formatResponse.imageBlocks(responseImages)) } const wasInteractiveBrowser = (lastRelevantMessageIndex > 0) ? modifiedClineMessages[lastRelevantMessageIndex - 1].text?.includes("interactive mode") : false + let hadBrowserPort = ''; + if ( wasInteractiveBrowser ) { + const match = modifiedClineMessages[lastRelevantMessageIndex - 1].text?.match(/\(browserPort\s*=\s*(\d+)\)/) + if (match) { + hadBrowserPort = match[1] ?? this.browserPort; + this.providerRef.deref()?.outputChannel.appendLine(`resumeTaskFromHistory :: browserPort :: ${hadBrowserPort}`) + } + } await this.overwriteApiConversationHistory(modifiedApiConversationHistory) - await this.initiateTaskLoop(newUserContent, wasInteractiveBrowser) + await this.initiateTaskLoop(newUserContent, wasInteractiveBrowser, hadBrowserPort) } - private async initiateTaskLoop(userContent: UserContent, wasInteractiveBrowser: boolean = false): Promise { + private async initiateTaskLoop(userContent: UserContent, wasInteractiveBrowser: boolean = false, hadBrowserPort: string = ''): Promise { // Check if any text block contains "interactive mode" const hasInteractiveMode = userContent.some((block) => { if (block.type === "text" && typeof block.text === "string") { - this.providerRef.deref()?.outputChannel.appendLine(`initiateTaskLoop :: block.text :: ${block.text.toLowerCase()}`) - return (block.type === "text" && typeof block.text === "string" && block.text.toLowerCase().includes("interactive mode")) @@ -693,6 +700,17 @@ export class Cline { // Set interactive mode flag if found in text if (hasInteractiveMode) { this.isInteractiveMode = true; + + // Parse browserPort if specified in text blocks + userContent.forEach((block) => { + if (block.type === "text" && typeof block.text === "string") { + const match = block.text.match(/\(browserPort\s*=\s*(\d+)\)/) + if (match) { + this.browserPort = match[1] ?? hadBrowserPort; + this.providerRef.deref()?.outputChannel.appendLine(`initiateTaskLoop :: browserPort :: ${this.browserPort}`) + } + } + }) } let nextUserContent = userContent; @@ -701,7 +719,8 @@ export class Cline { const didEndLoop = await this.recursivelyMakeClineRequests( nextUserContent, includeFileDetails, - this.isInteractiveMode // Pass the flag to recursivelyMakeClineRequests + this.isInteractiveMode, // Pass the flag to recursivelyMakeClineRequests + this.browserPort // Pass the browserPort to recursivelyMakeClineRequests ) includeFileDetails = false @@ -1493,7 +1512,7 @@ export class Cline { // NOTE: it's okay that we call this message since the partial inspect_site is finished streaming. The only scenario we have to avoid is sending messages WHILE a partial message exists at the end of the messages array. For example the api_req_finished message would interfere with the partial message, so we needed to remove that. // await this.say("inspect_site_result", "") // no result, starts the loading spinner waiting for result await this.say("browser_action_result", "") // starts loading spinner - await this.browserSession.launchBrowser(this.isInteractiveMode) + await this.browserSession.launchBrowser(this.isInteractiveMode, this.browserPort) browserActionResult = await this.browserSession.navigateToUrl(url) } else { if (action === "click") { @@ -1817,7 +1836,8 @@ export class Cline { async recursivelyMakeClineRequests( userContent: UserContent, includeFileDetails: boolean = false, - isInteractiveMode: boolean = false + isInteractiveMode: boolean = false, + browserPort: string = '7333' ): Promise { if (this.abort) { throw new Error("Cline instance aborted") @@ -2074,7 +2094,7 @@ export class Cline { this.consecutiveMistakeCount++ } - const recDidEndLoop = await this.recursivelyMakeClineRequests(this.userMessageContent, false, this.isInteractiveMode) + const recDidEndLoop = await this.recursivelyMakeClineRequests(this.userMessageContent, false, this.isInteractiveMode, browserPort) didEndLoop = recDidEndLoop } else { // if there's no assistant_responses, that means we got no text or tool_use content blocks from API which we should assume is an error diff --git a/src/services/browser/BrowserSession.ts b/src/services/browser/BrowserSession.ts index 9007848f38..a08c8be427 100644 --- a/src/services/browser/BrowserSession.ts +++ b/src/services/browser/BrowserSession.ts @@ -13,15 +13,21 @@ export class BrowserSession { private page?: Page private currentMousePosition?: string private isInteractive: boolean = false + private browserPort: string = '7333' constructor(context: vscode.ExtensionContext) { this.context = context } - async launchBrowser(interactive: boolean = false) { + async launchBrowser(interactive: boolean = false, port?: string) { console.log("launch browser called") this.isInteractive = interactive + // Set browserPort if provided, otherwise use default + if (port) { + this.browserPort = port + } + if (this.browser) { await this.closeBrowser() // this may happen when the model launches a browser again after having used it already before } @@ -29,20 +35,20 @@ export class BrowserSession { if (this.isInteractive) { try { // Fetch the WebSocket endpoint from Chrome's debugging API - const response = await fetch('http://127.0.0.1:7333/json/version') + const response = await fetch(`http://127.0.0.1:${this.browserPort}/json/version`) const data = await response.json() const browserWSEndpoint = data.webSocketDebuggerUrl if (!browserWSEndpoint) { - throw new Error('Could not get WebSocket endpoint from Chrome debugging API') + throw new Error(`BrowserSession.ts :: launchBrowser :: Could not get webSocketDebuggerUrl from Chrome debugging API, port: ${this.browserPort}`) } this.browser = await connect({ browserWSEndpoint, }) } catch (error) { - console.error("Failed to connect to browser:", error) - throw new Error(`Failed to connect to browser: ${error.message}`) + console.error("BrowserSession.ts :: launchBrowser :: Failed to connect to browser, make sure you have a running browser with --remote-debugging-port=7333", error) + throw new Error(`BrowserSession.ts :: launchBrowser :: Failed to connect to browser: ${error.message}, make sure you have a running browser with --remote-debugging-port=7333`) } } else { this.browser = await launch({ @@ -63,7 +69,7 @@ export class BrowserSession { return { screenshot: "", logs: this.isInteractive ? - "Browser launched in interactive mode. Please confirm when you're done using the browser." : + "Connected to browser in remote debugging mode." : "Browser launched successfully.", currentUrl: this.page?.url(), currentMousePosition: this.currentMousePosition,