Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.pnpm-store
dist
out
out-*
Expand Down
38 changes: 20 additions & 18 deletions locales/ca/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/de/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/es/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/fr/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/hi/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/it/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/ja/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/ko/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/pl/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/pt-BR/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/tr/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/vi/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/zh-CN/README.md

Large diffs are not rendered by default.

38 changes: 20 additions & 18 deletions locales/zh-TW/README.md

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion scripts/generate-types.mts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ import fs from "fs/promises"
import { zodToTs, createTypeAlias, printNode } from "zod-to-ts"
import { $ } from "execa"

import { typeDefinitions } from "../src/schemas"
import schemas from "../src/schemas"
const { typeDefinitions } = schemas

async function main() {
const types: string[] = [
Expand Down
7 changes: 4 additions & 3 deletions src/core/Cline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2466,7 +2466,8 @@ export class Cline extends EventEmitter<ClineEvents> {
}
break
} else {
let browserActionResult: BrowserActionResult
// Initialize with empty object to avoid "used before assigned" errors
let browserActionResult: BrowserActionResult = {}
if (action === "launch") {
if (!url) {
this.consecutiveMistakeCount++
Expand Down Expand Up @@ -2552,9 +2553,9 @@ export class Cline extends EventEmitter<ClineEvents> {
pushToolResult(
formatResponse.toolResult(
`The browser action has been executed. The console logs and screenshot have been captured for your analysis.\n\nConsole logs:\n${
browserActionResult.logs || "(No new logs)"
browserActionResult?.logs || "(No new logs)"
}\n\n(REMEMBER: if you need to proceed to using non-\`browser_action\` tools or launch a new browser, you MUST first close this browser. For example, if after analyzing the logs and screenshot you need to edit a file, you must first close the browser before you can use the write_to_file tool.)`,
browserActionResult.screenshot ? [browserActionResult.screenshot] : [],
browserActionResult?.screenshot ? [browserActionResult.screenshot] : [],
),
)
break
Expand Down
5 changes: 1 addition & 4 deletions src/core/mentions/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,7 @@ export async function openMention(mention?: string, osInfo?: string): Promise<vo
return
}

if (
(osInfo !== "win32" && mention.startsWith("/")) ||
(osInfo === "win32" && mention.startsWith("\\"))
) {
if ((osInfo !== "win32" && mention.startsWith("/")) || (osInfo === "win32" && mention.startsWith("\\"))) {
const relPath = mention.slice(1)
let absPath = path.resolve(cwd, relPath)
if (absPath.includes(" ")) {
Expand Down
91 changes: 22 additions & 69 deletions src/core/webview/ClineProvider.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ import { McpHub } from "../../services/mcp/McpHub"
import { McpServerManager } from "../../services/mcp/McpServerManager"
import { ShadowCheckpointService } from "../../services/checkpoints/ShadowCheckpointService"
import { BrowserSession } from "../../services/browser/BrowserSession"
import { discoverChromeInstances } from "../../services/browser/browserDiscovery"
import { discoverChromeHostUrl, tryChromeHostUrl } from "../../services/browser/browserDiscovery"
import { searchWorkspaceFiles } from "../../services/search/file-search"
import { fileExistsAtPath } from "../../utils/fs"
import { playSound, setSoundEnabled, setSoundVolume } from "../../utils/sound"
Expand Down Expand Up @@ -1420,74 +1420,17 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
await this.postStateToWebview()
break
case "testBrowserConnection":
try {
const browserSession = new BrowserSession(this.context)
// If no text is provided, try auto-discovery
if (!message.text) {
try {
const discoveredHost = await discoverChromeInstances()
if (discoveredHost) {
// Test the connection to the discovered host
const result = await browserSession.testConnection(discoveredHost)
// Send the result back to the webview
await this.postMessageToWebview({
type: "browserConnectionResult",
success: result.success,
text: `Auto-discovered and tested connection to Chrome at ${discoveredHost}: ${result.message}`,
values: { endpoint: result.endpoint },
})
} else {
await this.postMessageToWebview({
type: "browserConnectionResult",
success: false,
text: "No Chrome instances found on the network. Make sure Chrome is running with remote debugging enabled (--remote-debugging-port=9222).",
})
}
} catch (error) {
await this.postMessageToWebview({
type: "browserConnectionResult",
success: false,
text: `Error during auto-discovery: ${error instanceof Error ? error.message : String(error)}`,
})
}
} else {
// Test the provided URL
const result = await browserSession.testConnection(message.text)

// Send the result back to the webview
await this.postMessageToWebview({
type: "browserConnectionResult",
success: result.success,
text: result.message,
values: { endpoint: result.endpoint },
})
}
} catch (error) {
await this.postMessageToWebview({
type: "browserConnectionResult",
success: false,
text: `Error testing connection: ${error instanceof Error ? error.message : String(error)}`,
})
}
break
case "discoverBrowser":
try {
const discoveredHost = await discoverChromeInstances()

if (discoveredHost) {
// Don't update the remoteBrowserHost state when auto-discovering
// This way we don't override the user's preference

// Test the connection to get the endpoint
const browserSession = new BrowserSession(this.context)
const result = await browserSession.testConnection(discoveredHost)

// If no text is provided, try auto-discovery
if (!message.text) {
// Use testBrowserConnection for auto-discovery
const chromeHostUrl = await discoverChromeHostUrl()
if (chromeHostUrl) {
// Send the result back to the webview
await this.postMessageToWebview({
type: "browserConnectionResult",
success: true,
text: `Successfully discovered and connected to Chrome at ${discoveredHost}`,
values: { endpoint: result.endpoint },
success: !!chromeHostUrl,
text: `Auto-discovered and tested connection to Chrome: ${chromeHostUrl}`,
values: { endpoint: chromeHostUrl },
})
} else {
await this.postMessageToWebview({
Expand All @@ -1496,11 +1439,17 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
text: "No Chrome instances found on the network. Make sure Chrome is running with remote debugging enabled (--remote-debugging-port=9222).",
})
}
} catch (error) {
} else {
// Test the provided URL
const customHostUrl = message.text
const hostIsValid = await tryChromeHostUrl(message.text)
// Send the result back to the webview
await this.postMessageToWebview({
type: "browserConnectionResult",
success: false,
text: `Error discovering browser: ${error instanceof Error ? error.message : String(error)}`,
success: hostIsValid,
text: hostIsValid
? `Successfully connected to Chrome: ${customHostUrl}`
: "Failed to connect to Chrome",
})
}
break
Expand Down Expand Up @@ -2602,6 +2551,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
screenshotQuality,
remoteBrowserHost,
remoteBrowserEnabled,
cachedChromeHostUrl,
writeDelayMs,
terminalOutputLineLimit,
terminalShellIntegrationTimeout,
Expand Down Expand Up @@ -2670,6 +2620,8 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
screenshotQuality: screenshotQuality ?? 75,
remoteBrowserHost,
remoteBrowserEnabled: remoteBrowserEnabled ?? false,
cachedChromeHostUrl: cachedChromeHostUrl,
preferredLanguage: language ?? "English",
writeDelayMs: writeDelayMs ?? 1000,
terminalOutputLineLimit: terminalOutputLineLimit ?? 500,
terminalShellIntegrationTimeout: terminalShellIntegrationTimeout ?? TERMINAL_SHELL_INTEGRATION_TIMEOUT,
Expand Down Expand Up @@ -2755,6 +2707,7 @@ export class ClineProvider extends EventEmitter<ClineProviderEvents> implements
screenshotQuality: stateValues.screenshotQuality ?? 75,
remoteBrowserHost: stateValues.remoteBrowserHost,
remoteBrowserEnabled: stateValues.remoteBrowserEnabled ?? false,
cachedChromeHostUrl: stateValues.cachedChromeHostUrl as string | undefined,
fuzzyMatchThreshold: stateValues.fuzzyMatchThreshold ?? 1.0,
writeDelayMs: stateValues.writeDelayMs ?? 1000,
terminalOutputLineLimit: stateValues.terminalOutputLineLimit ?? 500,
Expand Down
82 changes: 7 additions & 75 deletions src/core/webview/__tests__/ClineProvider.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,12 @@ jest.mock("../../../services/browser/BrowserSession", () => ({

// Mock browserDiscovery
jest.mock("../../../services/browser/browserDiscovery", () => ({
discoverChromeInstances: jest.fn().mockImplementation(async () => {
discoverChromeHostUrl: jest.fn().mockImplementation(async () => {
return "http://localhost:9222"
}),
tryChromeHostUrl: jest.fn().mockImplementation(async (url) => {
return url === "http://localhost:9222"
}),
}))

jest.mock(
Expand Down Expand Up @@ -1916,9 +1919,9 @@ describe("ClineProvider", () => {
type: "testBrowserConnection",
})

// Verify discoverChromeInstances was called
const { discoverChromeInstances } = require("../../../services/browser/browserDiscovery")
expect(discoverChromeInstances).toHaveBeenCalled()
// Verify discoverChromeHostUrl was called
const { discoverChromeHostUrl } = require("../../../services/browser/browserDiscovery")
expect(discoverChromeHostUrl).toHaveBeenCalled()

// Verify postMessage was called with success result
expect(mockPostMessage).toHaveBeenCalledWith(
Expand All @@ -1929,77 +1932,6 @@ describe("ClineProvider", () => {
}),
)
})

test("handles discoverBrowser message", async () => {
// Get the message handler
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]

// Test browser discovery
await messageHandler({
type: "discoverBrowser",
})

// Verify discoverChromeInstances was called
const { discoverChromeInstances } = require("../../../services/browser/browserDiscovery")
expect(discoverChromeInstances).toHaveBeenCalled()

// Verify postMessage was called with success result
expect(mockPostMessage).toHaveBeenCalledWith(
expect.objectContaining({
type: "browserConnectionResult",
success: true,
text: expect.stringContaining("Successfully discovered and connected to Chrome"),
}),
)
})

test("handles errors during browser discovery", async () => {
// Mock discoverChromeInstances to throw an error
const { discoverChromeInstances } = require("../../../services/browser/browserDiscovery")
discoverChromeInstances.mockImplementationOnce(() => {
throw new Error("Discovery error")
})

// Get the message handler
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]

// Test browser discovery with error
await messageHandler({
type: "discoverBrowser",
})

// Verify postMessage was called with error result
expect(mockPostMessage).toHaveBeenCalledWith(
expect.objectContaining({
type: "browserConnectionResult",
success: false,
text: expect.stringContaining("Error discovering browser"),
}),
)
})

test("handles case when no browsers are discovered", async () => {
// Mock discoverChromeInstances to return null (no browsers found)
const { discoverChromeInstances } = require("../../../services/browser/browserDiscovery")
discoverChromeInstances.mockImplementationOnce(() => null)

// Get the message handler
const messageHandler = (mockWebviewView.webview.onDidReceiveMessage as jest.Mock).mock.calls[0][0]

// Test browser discovery with no browsers found
await messageHandler({
type: "discoverBrowser",
})

// Verify postMessage was called with failure result
expect(mockPostMessage).toHaveBeenCalledWith(
expect.objectContaining({
type: "browserConnectionResult",
success: false,
text: expect.stringContaining("No Chrome instances found"),
}),
)
})
})
})

Expand Down
1 change: 1 addition & 0 deletions src/exports/roo-code.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ type GlobalSettings = {
screenshotQuality?: number | undefined
remoteBrowserEnabled?: boolean | undefined
remoteBrowserHost?: string | undefined
cachedChromeHostUrl?: string | undefined
enableCheckpoints?: boolean | undefined
checkpointStorage?: ("task" | "workspace") | undefined
ttsEnabled?: boolean | undefined
Expand Down
1 change: 1 addition & 0 deletions src/exports/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -237,6 +237,7 @@ type GlobalSettings = {
screenshotQuality?: number | undefined
remoteBrowserEnabled?: boolean | undefined
remoteBrowserHost?: string | undefined
cachedChromeHostUrl?: string | undefined
enableCheckpoints?: boolean | undefined
checkpointStorage?: ("task" | "workspace") | undefined
ttsEnabled?: boolean | undefined
Expand Down
7 changes: 6 additions & 1 deletion src/schemas/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -513,6 +513,7 @@ export const globalSettingsSchema = z.object({
screenshotQuality: z.number().optional(),
remoteBrowserEnabled: z.boolean().optional(),
remoteBrowserHost: z.string().optional(),
cachedChromeHostUrl: z.string().optional(),

enableCheckpoints: z.boolean().optional(),
checkpointStorage: checkpointStoragesSchema.optional(),
Expand Down Expand Up @@ -618,6 +619,7 @@ const globalSettingsRecord: GlobalSettingsRecord = {
customModePrompts: undefined,
customSupportPrompts: undefined,
enhancementApiConfigId: undefined,
cachedChromeHostUrl: undefined,
}

export const GLOBAL_SETTINGS_KEYS = Object.keys(globalSettingsRecord) as Keys<GlobalSettings>[]
Expand Down Expand Up @@ -791,7 +793,7 @@ export type TokenUsage = z.infer<typeof tokenUsageSchema>
* TypeDefinition
*/

type TypeDefinition = {
export type TypeDefinition = {
schema: z.ZodTypeAny
identifier: string
}
Expand All @@ -802,3 +804,6 @@ export const typeDefinitions: TypeDefinition[] = [
{ schema: clineMessageSchema, identifier: "ClineMessage" },
{ schema: tokenUsageSchema, identifier: "TokenUsage" },
]

// Also export as default for ESM compatibility
export default { typeDefinitions }
Loading