Skip to content
Closed
Show file tree
Hide file tree
Changes from all 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
36 changes: 36 additions & 0 deletions .devcontainer/devcontainer.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"name": "Roo Code Development",
"image": "mcr.microsoft.com/devcontainers/typescript-node:1-20-bullseye",

// Features to add to the dev container
"features": {
"ghcr.io/devcontainers/features/github-cli:1": {}
},

// Install Chromium and its dependencies automatically
"postCreateCommand": "sudo apt-get update && sudo apt --fix-broken install -y && sudo apt-get install -y chromium-browser chromium-codecs-ffmpeg chromium-codecs-ffmpeg-extra libatk-bridge2.0-0 libatk1.0-0 libatspi2.0-0 libcups2 libdbus-1-3 libdrm2 libgbm1 libgtk-3-0 libnspr4 libnss3 libx11-xcb1 libxcomposite1 libxdamage1 libxfixes3 libxkbcommon0 libxrandr2 xdg-utils && npm install",

// Configure VS Code settings
"customizations": {
"vscode": {
"extensions": ["dbaeumer.vscode-eslint", "esbenp.prettier-vscode", "ms-vscode.vscode-typescript-next"],
"settings": {
"terminal.integrated.defaultProfile.linux": "bash",
"typescript.tsdk": "node_modules/typescript/lib"
}
}
},

// Forward ports for development servers
"forwardPorts": [3000, 5173, 8080],

// Environment variables
"remoteEnv": {
"PUPPETEER_SKIP_CHROMIUM_DOWNLOAD": "true",
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we add a comment explaining why these environment variables are needed? It would help future maintainers understand that:

  • PUPPETEER_SKIP_CHROMIUM_DOWNLOAD: Prevents downloading Chromium since we're using the system-installed version
  • PUPPETEER_EXECUTABLE_PATH: Points to the system Chromium installation

"PUPPETEER_EXECUTABLE_PATH": "/usr/bin/chromium-browser",
"CODESPACES": "true"
},

// Run as non-root user
"remoteUser": "node"
}
97 changes: 82 additions & 15 deletions src/services/browser/BrowserSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import delay from "delay"
import { fileExistsAtPath } from "../../utils/fs"
import { BrowserActionResult } from "../../shared/ExtensionMessage"
import { discoverChromeHostUrl, tryChromeHostUrl } from "./browserDiscovery"
import { isCodespacesEnvironment, fixCodespaceDependencies, isMissingDependencyError } from "./codespaceUtils"

// Timeout constants
const BROWSER_NAVIGATION_TIMEOUT = 15_000 // 15 seconds
Expand Down Expand Up @@ -42,13 +43,35 @@ export class BrowserSession {
await fs.mkdir(puppeteerDir, { recursive: true })
}

// if chromium doesn't exist, this will download it to path.join(puppeteerDir, ".chromium-browser-snapshots")
// if it does exist it will return the path to existing chromium
const stats: PCRStats = await PCR({
downloadPath: puppeteerDir,
})
try {
// if chromium doesn't exist, this will download it to path.join(puppeteerDir, ".chromium-browser-snapshots")
// if it does exist it will return the path to existing chromium
const stats: PCRStats = await PCR({
downloadPath: puppeteerDir,
})

return stats
return stats
} catch (error) {
// Check if this is a missing dependency error in Codespaces
if (isCodespacesEnvironment() && isMissingDependencyError(error)) {
console.log("Detected missing browser dependencies in Codespaces, attempting to fix...")

// Try to fix the dependencies
const fixed = await fixCodespaceDependencies()

if (fixed) {
// Retry PCR after fixing dependencies
console.log("Dependencies fixed, retrying browser initialization...")
const stats: PCRStats = await PCR({
downloadPath: puppeteerDir,
})
return stats
}
}

// If we couldn't fix it or it's not a Codespaces issue, throw the original error
throw error
}
}

/**
Expand All @@ -65,16 +88,60 @@ export class BrowserSession {
*/
private async launchLocalBrowser(): Promise<void> {
console.log("Launching local browser")
const stats = await this.ensureChromiumExists()
this.browser = await stats.puppeteer.launch({
args: [

try {
const stats = await this.ensureChromiumExists()

const args = [
"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
],
executablePath: stats.executablePath,
defaultViewport: this.getViewport(),
// headless: false,
})
this.isUsingRemoteBrowser = false
]

// Add additional args for Linux/Codespaces environments
if (process.platform === "linux" || isCodespacesEnvironment()) {
args.push("--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage")
}

this.browser = await stats.puppeteer.launch({
args,
executablePath: stats.executablePath,
defaultViewport: this.getViewport(),
// headless: false,
})
this.isUsingRemoteBrowser = false
} catch (error) {
// Check if this is a missing dependency error in Codespaces
if (isCodespacesEnvironment() && isMissingDependencyError(error)) {
console.log("Browser launch failed due to missing dependencies, attempting to fix...")

// Try to fix the dependencies
const fixed = await fixCodespaceDependencies()

if (fixed) {
// Retry launching after fixing dependencies
console.log("Dependencies fixed, retrying browser launch...")
const stats = await this.ensureChromiumExists()

const args = [
"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
"--no-sandbox",
"--disable-setuid-sandbox",
"--disable-dev-shm-usage",
]

this.browser = await stats.puppeteer.launch({
args,
executablePath: stats.executablePath,
defaultViewport: this.getViewport(),
// headless: false,
})
this.isUsingRemoteBrowser = false
return
}
}

// If we couldn't fix it or it's not a Codespaces issue, throw the original error
throw error
}
}

/**
Expand Down
134 changes: 103 additions & 31 deletions src/services/browser/UrlContentFetcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import TurndownService from "turndown"
import PCR from "puppeteer-chromium-resolver"
import { fileExistsAtPath } from "../../utils/fs"
import { serializeError } from "serialize-error"
import { isCodespacesEnvironment, fixCodespaceDependencies, isMissingDependencyError } from "./codespaceUtils"

// Timeout constants
const URL_FETCH_TIMEOUT = 30_000 // 30 seconds
Expand Down Expand Up @@ -37,44 +38,115 @@ export class UrlContentFetcher {
if (!dirExists) {
await fs.mkdir(puppeteerDir, { recursive: true })
}
// if chromium doesn't exist, this will download it to path.join(puppeteerDir, ".chromium-browser-snapshots")
// if it does exist it will return the path to existing chromium
const stats: PCRStats = await PCR({
downloadPath: puppeteerDir,
})
return stats

try {
// if chromium doesn't exist, this will download it to path.join(puppeteerDir, ".chromium-browser-snapshots")
// if it does exist it will return the path to existing chromium
const stats: PCRStats = await PCR({
downloadPath: puppeteerDir,
})
return stats
} catch (error) {
// Check if this is a missing dependency error in Codespaces
if (isCodespacesEnvironment() && isMissingDependencyError(error)) {
console.log("Detected missing browser dependencies in Codespaces, attempting to fix...")

// Try to fix the dependencies
const fixed = await fixCodespaceDependencies()

if (fixed) {
// Retry PCR after fixing dependencies
console.log("Dependencies fixed, retrying browser initialization...")
const stats: PCRStats = await PCR({
downloadPath: puppeteerDir,
})
return stats
}
}

// If we couldn't fix it or it's not a Codespaces issue, throw the original error
throw error
}
}

async launchBrowser(): Promise<void> {
if (this.browser) {
return
}
const stats = await this.ensureChromiumExists()
const args = [
"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
"--disable-dev-shm-usage",
"--disable-accelerated-2d-canvas",
"--no-first-run",
"--disable-gpu",
"--disable-features=VizDisplayCompositor",
]
if (process.platform === "linux") {
// Fixes network errors on Linux hosts (see https://github.com/puppeteer/puppeteer/issues/8246)
args.push("--no-sandbox")
}
this.browser = await stats.puppeteer.launch({
args,
executablePath: stats.executablePath,
})
// (latest version of puppeteer does not add headless to user agent)
this.page = await this.browser?.newPage()

// Set additional page configurations to improve loading success
if (this.page) {
await this.page.setViewport({ width: 1280, height: 720 })
await this.page.setExtraHTTPHeaders({
"Accept-Language": "en-US,en;q=0.9",

try {
const stats = await this.ensureChromiumExists()
const args = [
"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
"--disable-dev-shm-usage",
"--disable-accelerated-2d-canvas",
"--no-first-run",
"--disable-gpu",
"--disable-features=VizDisplayCompositor",
]

// Add additional args for Linux/Codespaces environments
if (process.platform === "linux" || isCodespacesEnvironment()) {
args.push("--no-sandbox", "--disable-setuid-sandbox")
}

this.browser = await stats.puppeteer.launch({
args,
executablePath: stats.executablePath,
})
// (latest version of puppeteer does not add headless to user agent)
this.page = await this.browser?.newPage()

// Set additional page configurations to improve loading success
if (this.page) {
await this.page.setViewport({ width: 1280, height: 720 })
await this.page.setExtraHTTPHeaders({
"Accept-Language": "en-US,en;q=0.9",
})
}
} catch (error) {
// Check if this is a missing dependency error in Codespaces
if (isCodespacesEnvironment() && isMissingDependencyError(error)) {
console.log("Browser launch failed due to missing dependencies, attempting to fix...")

// Try to fix the dependencies
const fixed = await fixCodespaceDependencies()

if (fixed) {
// Retry launching after fixing dependencies
console.log("Dependencies fixed, retrying browser launch...")
const stats = await this.ensureChromiumExists()
const args = [
"--user-agent=Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36",
"--disable-dev-shm-usage",
"--disable-accelerated-2d-canvas",
"--no-first-run",
"--disable-gpu",
"--disable-features=VizDisplayCompositor",
"--no-sandbox",
"--disable-setuid-sandbox",
]

this.browser = await stats.puppeteer.launch({
args,
executablePath: stats.executablePath,
})
// (latest version of puppeteer does not add headless to user agent)
this.page = await this.browser?.newPage()

// Set additional page configurations to improve loading success
if (this.page) {
await this.page.setViewport({ width: 1280, height: 720 })
await this.page.setExtraHTTPHeaders({
"Accept-Language": "en-US,en;q=0.9",
})
}
return
}
}

// If we couldn't fix it or it's not a Codespaces issue, throw the original error
throw error
}
}

Expand Down
1 change: 1 addition & 0 deletions src/services/browser/__tests__/UrlContentFetcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,7 @@ describe("UrlContentFetcher", () => {
"--disable-gpu",
"--disable-features=VizDisplayCompositor",
"--no-sandbox", // Linux-specific argument
"--disable-setuid-sandbox", // Additional Linux/Codespaces argument
],
executablePath: "/path/to/chromium",
})
Expand Down
Loading
Loading