Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a37ec31
Added local docker deployment
olesho Aug 27, 2025
a21844b
Neko + Devtools run but separately
olesho Aug 31, 2025
50a4a21
Local setup running with devtools
olesho Aug 31, 2025
0ad4e72
Custom Devtools working on CloudRun
olesho Sep 2, 2025
95b40da
Local setup
olesho Sep 4, 2025
2552285
Using Twilio + CloudRun to deploy devtools frontend with Chromium
olesho Sep 8, 2025
b6b6be1
Merge branch 'main' into feat/twilio-devtools
olesho Sep 8, 2025
432a863
Removed obsolete cloudrun script
olesho Sep 8, 2025
5998959
Fix Makefile
olesho Sep 8, 2025
f908215
Fix the build
olesho Sep 8, 2025
6ff623d
Fixed conlict in the run-local.sh
olesho Sep 9, 2025
f7a49cb
Remove test script
olesho Sep 9, 2025
457f882
Remove test file
olesho Sep 9, 2025
fbad484
Remove test files
olesho Sep 9, 2025
202b50b
Remove hardcoded credentials
olesho Sep 9, 2025
40b5590
Fixed cloubuild.yaml
olesho Sep 9, 2025
5f9e3a0
Switching to TCP instead UDP for WebRTC
olesho Sep 9, 2025
0a3ab42
fix the deployment issue
tysonthomas9 Sep 9, 2025
ca565e4
Fixed hardcoded project name; removed google as a start page
olesho Sep 9, 2025
7c6253d
Modified example env file
olesho Sep 9, 2025
c541c53
A follow up changes to improve CloudRun deployment
olesho Sep 10, 2025
6de5332
Merge branch 'main' into feat/twilio-followup
olesho Sep 10, 2025
aa674a3
Add eval-server integration with Node.js and nginx routes
olesho Oct 5, 2025
e6ed228
Enable AUTOMATED_MODE in DevTools build for eval-server auto-connect
olesho Oct 5, 2025
612f5aa
Add --auto-open-devtools-for-tabs to auto-open DevTools panel
olesho Oct 5, 2025
d7aff66
Add python3 to final image for devtools-frontend HTTP server
olesho Oct 6, 2025
3e81cf8
Add --auto-open-devtools-for-tabs to supervisor chromium.conf
olesho Oct 6, 2025
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
42 changes: 38 additions & 4 deletions Dockerfile.cloudrun
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,19 @@ RUN git remote add upstream https://github.com/BrowserOperator/browser-operator-
RUN git fetch upstream
RUN git checkout upstream/main

# Build Browser Operator version
# Enable AUTOMATED_MODE for Docker deployment
RUN sed -i 's/AUTOMATED_MODE: false/AUTOMATED_MODE: true/' front_end/panels/ai_chat/core/BuildConfig.ts

# Build Browser Operator version with AUTOMATED_MODE enabled
RUN npm run build

# Eval-Server build stage
FROM node:22-bullseye-slim AS eval-server-builder
WORKDIR /eval-server
COPY eval-server/nodejs/package*.json ./
RUN npm install --production
COPY eval-server/nodejs/ ./

# Multi-stage build using kernel-images as base
FROM docker.io/golang:1.25.0 AS server-builder
WORKDIR /workspace/server
Expand Down Expand Up @@ -143,7 +153,16 @@ RUN apt-get update && \
netcat \
nginx \
# PPA req
software-properties-common && \
software-properties-common \
# Node.js for eval-server
ca-certificates \
gnupg && \
# Install Node.js 22.x for eval-server
mkdir -p /etc/apt/keyrings && \
curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key | gpg --dearmor -o /etc/apt/keyrings/nodesource.gpg && \
echo "deb [signed-by=/etc/apt/keyrings/nodesource.gpg] https://deb.nodesource.com/node_22.x nodistro main" | tee /etc/apt/sources.list.d/nodesource.list && \
apt-get update && \
apt-get install -y nodejs && \
# Disable nginx auto-start to prevent conflicts with custom config
systemctl disable nginx || true && \
systemctl mask nginx || true && \
Expand Down Expand Up @@ -187,7 +206,7 @@ ENV USERNAME=kernel
RUN set -eux; \
apt-get update; \
apt-get install -y --no-install-recommends \
wget ca-certificates python2 supervisor xclip xdotool \
wget ca-certificates python2 python3 python-is-python3 supervisor xclip xdotool \
pulseaudio dbus-x11 xserver-xorg-video-dummy \
libcairo2 libxcb1 libxrandr2 libxv1 libopus0 libvpx7 \
gstreamer1.0-plugins-base gstreamer1.0-plugins-good \
Expand Down Expand Up @@ -256,6 +275,20 @@ COPY --from=devtools-builder /workspace/devtools/devtools-frontend/out/Default/g
# Set permissions for DevTools files
RUN chown -R kernel:kernel /usr/share/nginx/devtools

# ============================================================================
# Eval-Server Integration
# ============================================================================

# Copy eval-server from builder
COPY --from=eval-server-builder /eval-server /opt/eval-server

# Copy custom eval-server startup script INTO eval-server directory
COPY eval-server-start.js /opt/eval-server/start-cloudrun.js
RUN chmod +x /opt/eval-server/start-cloudrun.js

# Set permissions for eval-server
RUN chown -R kernel:kernel /opt/eval-server

# Cloud Run specific: wrapper scripts (nginx config is inline)
# DO NOT copy nginx.conf to avoid auto-start conflicts
COPY cloudrun-wrapper.sh /cloudrun-wrapper.sh
Expand All @@ -268,6 +301,7 @@ COPY supervisor/services-cloudrun/xorg.conf /etc/supervisor/conf.d/services-clou
COPY supervisor/services-cloudrun/neko.conf /etc/supervisor/conf.d/services-cloudrun/neko.conf
COPY supervisor/services-cloudrun/chromium.conf /etc/supervisor/conf.d/services-cloudrun/chromium.conf
COPY supervisor/services-cloudrun/devtools-frontend.conf /etc/supervisor/conf.d/services-cloudrun/devtools-frontend.conf
COPY supervisor/services-cloudrun/eval-server.conf /etc/supervisor/conf.d/services-cloudrun/eval-server.conf

# Create nginx temp directories for non-root execution
RUN mkdir -p /tmp/nginx_client_temp /tmp/nginx_proxy_temp /tmp/nginx_fastcgi_temp \
Expand All @@ -279,7 +313,7 @@ RUN mkdir -p /tmp/nginx_client_temp /tmp/nginx_proxy_temp /tmp/nginx_fastcgi_tem
# Create supervisor log directories
RUN mkdir -p /var/log/supervisord/chromium /var/log/supervisord/neko /var/log/supervisord/xorg \
/var/log/supervisord/dbus /var/log/supervisord/kernel-images-api /var/log/supervisord/mutter \
/var/log/supervisord/nginx /var/log/supervisord/devtools-frontend && \
/var/log/supervisord/nginx /var/log/supervisord/devtools-frontend /var/log/supervisord/eval-server && \
chown -R kernel:kernel /var/log/supervisord

# Create health check endpoint
Expand Down
23 changes: 21 additions & 2 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -227,16 +227,35 @@ Example: 1 hour session ≈ $0.50-1.00

The `cloudbuild.yaml` provides:
1. Submodule initialization
2. Docker image build
2. Docker image build with caching
3. Container Registry push
4. Cloud Run deployment
5. Traffic routing

Trigger builds via:
### Build Commands

```bash
# Normal build (with cache) - recommended for development
gcloud builds submit --config cloudbuild.yaml

# Force rebuild without cache - use when dependencies change
gcloud builds submit --config cloudbuild.yaml --substitutions=_NO_CACHE=true

# Automated deployment with Twilio TURN server setup
./deploy.sh
```

### Cache Control

The build system uses Docker layer caching by default to reduce build times and costs:
- **With cache**: ~5-10 minutes, lower cost
- **Without cache**: ~30+ minutes, higher cost (~$3-5 per build)

Use `_NO_CACHE=true` only when:
- Dependencies have changed significantly
- Base images need updating
- Debugging build issues

## 📚 Additional Resources

- [kernel-images Documentation](https://github.com/onkernel/kernel-images)
Expand Down
1 change: 1 addition & 0 deletions browser-operator-core
Submodule browser-operator-core added at 3aaef1
52 changes: 36 additions & 16 deletions cloudbuild.yaml
Original file line number Diff line number Diff line change
@@ -1,4 +1,8 @@
# Cloud Build configuration for kernel-browser
# Usage: gcloud builds submit --substitutions=_NO_CACHE=true (to disable cache)
substitutions:
_NO_CACHE: 'false'

steps:
# Step 1: Verify kernel-images directory exists
- name: 'gcr.io/cloud-builders/docker'
Expand All @@ -17,22 +21,36 @@ steps:
args:
- '-c'
- |
echo "Attempting to pull previous image for caching..."
docker pull us-docker.pkg.dev/$PROJECT_ID/gcr.io/kernel-browser:latest || echo "No previous image found for caching"
if [ "${_NO_CACHE}" = "true" ]; then
echo "⚠️ Cache disabled by _NO_CACHE=true flag"
else
echo "Attempting to pull previous image for caching..."
docker pull us-docker.pkg.dev/$PROJECT_ID/gcr.io/kernel-browser:latest || echo "No previous image found for caching"
fi

# Step 3: Build the Docker image with caching (using cloudrun Dockerfile)
- name: 'gcr.io/cloud-builders/docker'
entrypoint: 'bash'
args:
- 'build'
- '--file'
- 'Dockerfile.cloudrun'
- '--cache-from'
- 'us-docker.pkg.dev/$PROJECT_ID/gcr.io/kernel-browser:latest'
- '--build-arg'
- 'CACHE_BUST=$BUILD_ID'
- '--tag'
- 'us-docker.pkg.dev/$PROJECT_ID/gcr.io/kernel-browser:latest'
- '.'
- '-c'
- |
if [ "${_NO_CACHE}" = "true" ]; then
echo "🔨 Building without cache..."
docker build \
--file Dockerfile.cloudrun \
--no-cache \
--build-arg CACHE_BUST=$BUILD_ID \
--tag us-docker.pkg.dev/$PROJECT_ID/gcr.io/kernel-browser:latest \
.
else
echo "🚀 Building with cache from previous image..."
docker build \
--file Dockerfile.cloudrun \
--cache-from us-docker.pkg.dev/$PROJECT_ID/gcr.io/kernel-browser:latest \
--build-arg CACHE_BUST=$BUILD_ID \
--tag us-docker.pkg.dev/$PROJECT_ID/gcr.io/kernel-browser:latest \
.
fi
timeout: '3600s' # Allow 1 hour for build (it's a large image)

# Step 4: Push the image to Artifact Registry
Expand All @@ -48,12 +66,14 @@ steps:
- '-c'
- |
# Check if Twilio secrets exist and choose appropriate service file
if gcloud secrets describe twilio-account-sid --project=$PROJECT_ID >/dev/null 2>&1 && \
gcloud secrets describe twilio-auth-token --project=$PROJECT_ID >/dev/null 2>&1; then
echo "Using service-secrets.yaml with Secret Manager references"
echo "Checking for Twilio secrets..."
if gcloud secrets describe twilio-account-sid --project=$PROJECT_ID && \
gcloud secrets describe twilio-auth-token --project=$PROJECT_ID; then
echo "✅ Twilio secrets found! Using service-secrets.yaml with Secret Manager references"
cp service-secrets.yaml temp-service.yaml
else
echo "Using standard service.yaml (secrets not configured)"
echo "⚠️ Twilio secrets NOT found. Using standard service.yaml (secrets not configured)"
echo "To use Twilio TURN servers, run: ./deploy.sh to set up secrets"
cp service.yaml temp-service.yaml
fi

Expand Down
56 changes: 55 additions & 1 deletion cloudrun-wrapper.sh
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,12 @@ export HEIGHT=768
export WIDTH=1024
export NEKO_BIND=:8081

# WebRTC Cloud Run configuration - force relay-only mode
export NEKO_WEBRTC_ICE_LITE=true
export NEKO_WEBRTC_ICE_POLICY=relay
export NEKO_WEBRTC_MDNS=false
export NEKO_WEBRTC_ICE_INTERFACES=""

# Get fresh Twilio TURN credentials if available
if [ -f /twilio-credential-updater.sh ]; then
echo "[cloudrun-wrapper] Getting fresh Twilio TURN credentials..."
Expand All @@ -23,7 +29,7 @@ fi

# Port configuration for Cloud Run
export PORT=${PORT:-8080}
export CHROMIUM_FLAGS="${CHROMIUM_FLAGS:---user-data-dir=/home/kernel/user-data --disable-dev-shm-usage --disable-gpu --start-maximized --disable-software-rasterizer --remote-allow-origins=* --no-sandbox --disable-setuid-sandbox --disable-features=VizDisplayCompositor --custom-devtools-frontend=http://localhost:8001/ https://www.google.com}"
export CHROMIUM_FLAGS="${CHROMIUM_FLAGS:---user-data-dir=/home/kernel/user-data --disable-dev-shm-usage --disable-gpu --start-maximized --disable-software-rasterizer --remote-allow-origins=* --no-sandbox --disable-setuid-sandbox --disable-features=VizDisplayCompositor --custom-devtools-frontend=http://localhost:8001/ --auto-open-devtools-for-tabs https://www.google.com}"

# Setup directories with proper permissions
mkdir -p /tmp/nginx_client_temp /tmp/nginx_proxy_temp /tmp/nginx_fastcgi_temp \
Expand Down Expand Up @@ -51,6 +57,8 @@ http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

# Configure log files to use /tmp for non-root execution
access_log /tmp/cloudrun-nginx-access.log;
# Create temp directories for nginx (non-root execution)
client_body_temp_path /tmp/nginx_client_temp;
proxy_temp_path /tmp/nginx_proxy_temp;
Expand Down Expand Up @@ -120,6 +128,7 @@ http {
# Chrome DevTools Protocol HTTP endpoints
location /json {
proxy_pass http://127.0.0.1:9223/json;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
Expand All @@ -129,6 +138,7 @@ http {
# Chrome DevTools Protocol HTTP endpoints (with trailing slash)
location /json/ {
proxy_pass http://127.0.0.1:9223/json/;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
Expand Down Expand Up @@ -157,6 +167,50 @@ http {
proxy_send_timeout 86400;
}

# Eval-Server HTTP API endpoints
location /v1/responses {
proxy_pass http://127.0.0.1:8083/v1/responses;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_read_timeout 1800;
proxy_send_timeout 1800;
}

# Eval-Server status endpoint
location /eval/status {
proxy_pass http://127.0.0.1:8083/status;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}

# Eval-Server clients endpoint
location /eval/clients {
proxy_pass http://127.0.0.1:8083/clients;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
}

# Eval-Server evaluate endpoint
location /eval/evaluate {
proxy_pass http://127.0.0.1:8083/evaluate;
proxy_http_version 1.1;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_read_timeout 1800;
proxy_send_timeout 1800;
}

# Enhanced DevTools Frontend
location /devtools/ {
proxy_pass http://127.0.0.1:8001/;
Expand Down
23 changes: 23 additions & 0 deletions deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,29 @@ setup_secrets() {
--project="$PROJECT_ID" \
--quiet

# Grant Cloud Build service account permission to view secrets (needed for cloudbuild.yaml)
local project_number=$(gcloud projects describe "$PROJECT_ID" --format="value(projectNumber)")
local cb_sa_email="${project_number}@cloudbuild.gserviceaccount.com"

info "Granting Secret Manager viewer access to Cloud Build service account..."
gcloud projects add-iam-policy-binding "$PROJECT_ID" \
--member="serviceAccount:$cb_sa_email" \
--role="roles/secretmanager.viewer" \
--project="$PROJECT_ID" \
--quiet

gcloud secrets add-iam-policy-binding twilio-account-sid \
--member="serviceAccount:$cb_sa_email" \
--role="roles/secretmanager.secretAccessor" \
--project="$PROJECT_ID" \
--quiet

gcloud secrets add-iam-policy-binding twilio-auth-token \
--member="serviceAccount:$cb_sa_email" \
--role="roles/secretmanager.secretAccessor" \
--project="$PROJECT_ID" \
--quiet

# Set flag to use secrets-enabled service.yaml
export USE_SECRETS=true

Expand Down
45 changes: 45 additions & 0 deletions eval-server-start.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
#!/usr/bin/env node

// Custom eval-server startup script for Cloud Run
// Uses environment variables for port configuration

import { EvalServer } from './src/lib/EvalServer.js';
import { HTTPWrapper } from './src/lib/HTTPWrapper.js';

const WS_PORT = parseInt(process.env.EVAL_SERVER_WS_PORT || '8082');
const HTTP_PORT = parseInt(process.env.EVAL_SERVER_HTTP_PORT || '8083');
const HOST = process.env.EVAL_SERVER_HOST || '127.0.0.1';

console.log('🔧 Creating EvalServer...');
const evalServer = new EvalServer({
// No authKey - authentication disabled for automated mode
host: HOST,
port: WS_PORT
});

console.log('🔧 Creating HTTP wrapper...');
const httpWrapper = new HTTPWrapper(evalServer, {
port: HTTP_PORT,
host: HOST
});

console.log('🔧 Starting EvalServer...');
await evalServer.start();
console.log(`✅ EvalServer started on ws://${HOST}:${WS_PORT}`);

console.log('🔧 Starting HTTP wrapper...');
await httpWrapper.start();
console.log(`✅ HTTP API started on http://${HOST}:${HTTP_PORT}`);

console.log('⏳ Waiting for DevTools client to connect...');
console.log(` WebSocket URL: ws://${HOST}:${WS_PORT}`);
console.log(` HTTP API URL: http://${HOST}:${HTTP_PORT}`);
console.log(' Auth: Disabled (automated mode)');

// Add periodic status check
setInterval(() => {
const evalServerStatus = evalServer.getStatus();
const httpWrapperStatus = httpWrapper.getStatus();
console.log(`📊 EvalServer: ${evalServerStatus.connectedClients} clients, ${evalServerStatus.readyClients} ready`);
console.log(`📊 HTTP API: ${httpWrapperStatus.isRunning ? 'running' : 'stopped'} on ${httpWrapperStatus.url}`);
}, 30000);
16 changes: 16 additions & 0 deletions eval-server/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# WebSocket Server Configuration
PORT=8080
HOST=localhost

# LLM Judge Configuration
OPENAI_API_KEY=your-openai-api-key-here
JUDGE_MODEL=gpt-4
JUDGE_TEMPERATURE=0.1

# Logging Configuration
LOG_LEVEL=info
LOG_DIR=./logs

# RPC Configuration
RPC_TIMEOUT=30000
MAX_CONCURRENT_EVALUATIONS=10
3 changes: 3 additions & 0 deletions eval-server/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.env
node_modules
*.log
Loading