Skip to content
Open
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
62 changes: 50 additions & 12 deletions packages/browseros-agent/apps/server/src/api/routes/agents.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
type PodmanRuntime,
} from '../services/podman-runtime'

const OPENCLAW_IMAGE = 'ghcr.io/openclaw/openclaw:latest'
const OPENCLAW_IMAGE = 'ghcr.io/browseros/openclaw-runtime:latest'
const MAX_LOG_LINES = 1000

// Maps BrowserOS provider types to OpenClaw environment variable names
Expand Down Expand Up @@ -181,18 +181,20 @@ function generateComposeFile(config: {
image: ${config.image}
ports:
- "127.0.0.1:${config.gatewayPort}:18789"
- "127.0.0.1:${config.gatewayPort + 1000}-${config.gatewayPort + 1099}:4000-4099"
environment:
- OPENCLAW_GATEWAY_TOKEN=${config.token}
- TZ=${tz}
volumes:
- ${config.configDir}:/home/node/.openclaw
- ${config.workspaceDir}:/home/node/.openclaw/workspace
command: node dist/index.js gateway --bind lan --port 18789
shm_size: "256mb"
healthcheck:
test: ["CMD", "curl", "-sf", "http://127.0.0.1:18789/healthz"]
interval: 30s
timeout: 10s
retries: 3
start_period: 60s
restart: unless-stopped
`
}
Expand Down Expand Up @@ -220,6 +222,11 @@ function generateOpenClawConfig(config: {
},
},
},
// Disable exec approvals — the container is already sandboxed
approvals: {
exec: { enabled: false },
plugin: { enabled: false },
},
}

if (!config.apiKey || !config.providerType) {
Expand Down Expand Up @@ -556,19 +563,50 @@ export function createAgentsRoutes() {
pushLog(instance, 'Wrote openclaw.json configuration')

pushLog(instance, 'Checking container runtime...')
await getPodmanRuntime().ensureReady((msg) => pushLog(instance, msg))
const runtime = getPodmanRuntime()
await runtime.ensureReady((msg) => pushLog(instance, msg))
pushLog(instance, 'Container runtime ready')

pushLog(instance, `Pulling image ${OPENCLAW_IMAGE}...`)
const pullExit = await runCommandWithLogs(
instance,
['compose', 'pull', '--quiet'],
{ cwd: agentDir, env: composeEnv(name) },
// Check if the runtime image exists locally; build if not
const imageCheck = Bun.spawn(
[runtime.getPodmanPath(), 'image', 'exists', OPENCLAW_IMAGE],
{ stdout: 'ignore', stderr: 'ignore' },
)
if (pullExit !== 0) {
throw new Error('Failed to pull OpenClaw image')
const imageExists = (await imageCheck.exited) === 0

if (!imageExists) {
// Try pulling from registry first
pushLog(instance, `Pulling image ${OPENCLAW_IMAGE}...`)
const pullExit = await runCommandWithLogs(instance, [
'pull',
OPENCLAW_IMAGE,
])
// If pull fails, build locally from Dockerfile
if (pullExit !== 0) {
pushLog(
instance,
'Image not found in registry, building locally (first time, may take a few minutes)...',
)
const dockerfileDir = path.join(
import.meta.dir,
'../services/openclaw-container',
)
const buildExit = await runCommandWithLogs(instance, [
'build',
'-t',
OPENCLAW_IMAGE,
dockerfileDir,
])
if (buildExit !== 0) {
throw new Error('Failed to build runtime image')
}
pushLog(instance, 'Runtime image built successfully')
} else {
pushLog(instance, 'Image pulled successfully')
}
} else {
pushLog(instance, 'Using existing runtime image')
}
pushLog(instance, 'Image pulled successfully')

pushLog(instance, 'Starting OpenClaw gateway...')
const upExit = await runCommandWithLogs(
Expand All @@ -582,7 +620,7 @@ export function createAgentsRoutes() {

pushLog(instance, 'Waiting for gateway to be ready...')
let healthy = false
for (let i = 0; i < 30; i++) {
for (let i = 0; i < 90; i++) {
try {
const res = await fetch(`http://127.0.0.1:${port}/healthz`)
if (res.ok) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
browseros_server
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
FROM ghcr.io/openclaw/openclaw:latest

USER root

# Install BrowserOS browser (.deb)
ARG BROWSEROS_DEB_URL=http://cdn.browseros.com/releases/0.44.0.2/linux/BrowserOS_v0.44.0.2_arm64.deb
RUN apt-get update -qq && \
curl -fsSL -o /tmp/BrowserOS.deb "$BROWSEROS_DEB_URL" && \
apt-get install -y -qq --fix-broken /tmp/BrowserOS.deb && \
Comment on lines +6 to +9
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Hardcoded ARM64 .deb — local builds on AMD64 will install the wrong binary

The PR adds an auto-local-build fallback when the registry image is missing, so this Dockerfile will run on user machines of any architecture. The default BROWSEROS_DEB_URL points to the arm64 package, causing dpkg to fail or install an unusable binary on AMD64 hosts. Detect arch at build time instead:

Suggested change
ARG BROWSEROS_DEB_URL=http://cdn.browseros.com/releases/0.44.0.2/linux/BrowserOS_v0.44.0.2_arm64.deb
RUN apt-get update -qq && \
curl -fsSL -o /tmp/BrowserOS.deb "$BROWSEROS_DEB_URL" && \
apt-get install -y -qq --fix-broken /tmp/BrowserOS.deb && \
ARG BROWSEROS_VERSION=0.44.0.2
RUN ARCH=$(dpkg --print-architecture) && \
curl -fsSL -o /tmp/BrowserOS.deb \
"http://cdn.browseros.com/releases/${BROWSEROS_VERSION}/linux/BrowserOS_v${BROWSEROS_VERSION}_${ARCH}.deb" && \
apt-get update -qq && \
apt-get install -y -qq --fix-broken /tmp/BrowserOS.deb && \
rm /tmp/BrowserOS.deb && \
npm install -g browseros-cli && \
rm -rf /var/lib/apt/lists/*
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/api/services/openclaw-container/Dockerfile
Line: 6-9

Comment:
**Hardcoded ARM64 `.deb` — local builds on AMD64 will install the wrong binary**

The PR adds an auto-local-build fallback when the registry image is missing, so this Dockerfile will run on user machines of any architecture. The default `BROWSEROS_DEB_URL` points to the `arm64` package, causing `dpkg` to fail or install an unusable binary on AMD64 hosts. Detect arch at build time instead:

```suggestion
ARG BROWSEROS_VERSION=0.44.0.2
RUN ARCH=$(dpkg --print-architecture) && \
    curl -fsSL -o /tmp/BrowserOS.deb \
      "http://cdn.browseros.com/releases/${BROWSEROS_VERSION}/linux/BrowserOS_v${BROWSEROS_VERSION}_${ARCH}.deb" && \
    apt-get update -qq && \
    apt-get install -y -qq --fix-broken /tmp/BrowserOS.deb && \
    rm /tmp/BrowserOS.deb && \
    npm install -g browseros-cli && \
    rm -rf /var/lib/apt/lists/*
```

How can I resolve this? If you propose a fix, please make it concise.

rm /tmp/BrowserOS.deb && \
npm install -g browseros-cli && \
rm -rf /var/lib/apt/lists/*

# Copy BrowserOS server binary (runs separately from the browser)
COPY browseros_server /usr/local/bin/browseros_server
RUN chmod +x /usr/local/bin/browseros_server

# Install browseros-agent skill
RUN su node -c "openclaw skills install browseros-agent" || true

COPY entrypoint.sh /entrypoint.sh
RUN chmod +x /entrypoint.sh

USER node

ENTRYPOINT ["/entrypoint.sh"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
#!/bin/bash
set -e

# Ports — mirrors BrowserOSAppManager pattern
CDP_PORT=9000
SERVER_PORT=9100
EXTENSION_PORT=9300

# 1. Launch BrowserOS (browser only, server disabled — matches --manual mode)
browseros \
--headless=new \
--no-sandbox \
--disable-gpu \
--disable-software-rasterizer \
--disable-dev-shm-usage \
--no-zygote \
--single-process \
--no-first-run \
--no-default-browser-check \
--use-mock-keychain \
--disable-browseros-server \
--disable-browseros-extensions \
--remote-debugging-port=$CDP_PORT \
--browseros-mcp-port=$SERVER_PORT \
--browseros-extension-port=$EXTENSION_PORT \
--window-size=1440,900 \
--user-data-dir=/tmp/browseros-data \
about:blank \
&

echo "[entrypoint] Waiting for BrowserOS CDP on port $CDP_PORT..."
for i in $(seq 1 30); do
if curl -sf http://localhost:$CDP_PORT/json/version > /dev/null 2>&1; then
echo "[entrypoint] CDP ready"
break
fi
sleep 1
done
Comment on lines +31 to +38
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Silent failure if CDP never becomes ready

The wait loop exits after 30 iterations without signaling failure. If BrowserOS never starts, browseros_server still launches and the container reports healthy with broken browser automation.

Add exit 1 after the loop:

Suggested change
echo "[entrypoint] Waiting for BrowserOS CDP on port $CDP_PORT..."
for i in $(seq 1 30); do
if curl -sf http://localhost:$CDP_PORT/json/version > /dev/null 2>&1; then
echo "[entrypoint] CDP ready"
break
fi
sleep 1
done
echo "[entrypoint] Waiting for BrowserOS CDP on port $CDP_PORT..."
READY=0
for i in $(seq 1 30); do
if curl -sf http://localhost:$CDP_PORT/json/version > /dev/null 2>&1; then
echo "[entrypoint] CDP ready"
READY=1
break
fi
sleep 1
done
if [ "$READY" -eq 0 ]; then
echo "[entrypoint] ERROR: BrowserOS CDP did not become ready in time" >&2
exit 1
fi
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/api/services/openclaw-container/entrypoint.sh
Line: 31-38

Comment:
**Silent failure if CDP never becomes ready**

The wait loop exits after 30 iterations without signaling failure. If BrowserOS never starts, `browseros_server` still launches and the container reports healthy with broken browser automation.

Add `exit 1` after the loop:
```suggestion
echo "[entrypoint] Waiting for BrowserOS CDP on port $CDP_PORT..."
READY=0
for i in $(seq 1 30); do
  if curl -sf http://localhost:$CDP_PORT/json/version > /dev/null 2>&1; then
    echo "[entrypoint] CDP ready"
    READY=1
    break
  fi
  sleep 1
done
if [ "$READY" -eq 0 ]; then
  echo "[entrypoint] ERROR: BrowserOS CDP did not become ready in time" >&2
  exit 1
fi
```

How can I resolve this? If you propose a fix, please make it concise.


# 2. Launch BrowserOS server (connects to browser via CDP)
BROWSEROS_CDP_PORT=$CDP_PORT \
BROWSEROS_SERVER_PORT=$SERVER_PORT \
BROWSEROS_EXTENSION_PORT=$EXTENSION_PORT \
NODE_ENV=production \
browseros_server \
--cdp-port $CDP_PORT \
--server-port $SERVER_PORT \
&

echo "[entrypoint] Waiting for BrowserOS server on port $SERVER_PORT..."
for i in $(seq 1 30); do
if curl -sf http://localhost:$SERVER_PORT/health > /dev/null 2>&1; then
echo "[entrypoint] Server ready"
break
fi
sleep 1
done
Comment on lines +50 to +57
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Server wait loop also swallows failure silently

Same issue as the CDP loop: if browseros_server never becomes healthy, the script silently continues and starts the OpenClaw gateway, producing a healthy-looking container with broken automation.

Suggested change
echo "[entrypoint] Waiting for BrowserOS server on port $SERVER_PORT..."
for i in $(seq 1 30); do
if curl -sf http://localhost:$SERVER_PORT/health > /dev/null 2>&1; then
echo "[entrypoint] Server ready"
break
fi
sleep 1
done
echo "[entrypoint] Waiting for BrowserOS server on port $SERVER_PORT..."
READY=0
for i in $(seq 1 30); do
if curl -sf http://localhost:$SERVER_PORT/health > /dev/null 2>&1; then
echo "[entrypoint] Server ready"
READY=1
break
fi
sleep 1
done
if [ "$READY" -eq 0 ]; then
echo "[entrypoint] ERROR: BrowserOS server did not become ready in time" >&2
exit 1
fi
Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/browseros-agent/apps/server/src/api/services/openclaw-container/entrypoint.sh
Line: 50-57

Comment:
**Server wait loop also swallows failure silently**

Same issue as the CDP loop: if `browseros_server` never becomes healthy, the script silently continues and starts the OpenClaw gateway, producing a healthy-looking container with broken automation.

```suggestion
echo "[entrypoint] Waiting for BrowserOS server on port $SERVER_PORT..."
READY=0
for i in $(seq 1 30); do
  if curl -sf http://localhost:$SERVER_PORT/health > /dev/null 2>&1; then
    echo "[entrypoint] Server ready"
    READY=1
    break
  fi
  sleep 1
done
if [ "$READY" -eq 0 ]; then
  echo "[entrypoint] ERROR: BrowserOS server did not become ready in time" >&2
  exit 1
fi
```

How can I resolve this? If you propose a fix, please make it concise.


# 3. Configure browseros-cli
browseros-cli init $SERVER_PORT 2>/dev/null || true

# 4. Start OpenClaw gateway
exec node dist/index.js gateway --bind lan --port 18789
Loading