🚀 WAIIDE Bootstrap - LLM Debugging Context

🚀 WAIIDE Bootstrap - LLM Debugging Context

Calliope Integration: This component is integrated into the Calliope AI platform. Some features and configurations may differ from the upstream project.

🚨 CRITICAL DEVELOPMENT CONSTRAINTS

⛔ NEVER Modify Files Inside WAIIDE/ Submodule

THE RULE: The WAIIDE/ directory is a Git submodule tracking upstream Microsoft WAIIDE. ANY changes to files inside WAIIDE/src/ or WAIIDE/extensions/ will be overwritten when syncing with upstream.

WHY THIS MATTERS:

  • WAIIDE/ tracks Microsoft’s WAIIDE repo
  • We keep it updated to get latest WAIIDE features & security patches
  • Direct modifications break on git submodule update

CORRECT APPROACH:

# ❌ WRONG - Will be overwritten on submodule update
vim WAIIDE/src/vs/platform/product/common/product.ts

# ✅ CORRECT - Outside submodule, injected at build time
vim WAIIDE/product.json

If You Need to Change WAIIDE Behavior:

  1. Branding: Edit WAIIDE/product.json (outside src/, safe to modify)
  2. Settings: Edit assets/server-settings.json or assets/default-settings.json
  3. Extensions: Modify calliope-WAIIDE-agent/ (our extension, not upstream)
  4. Core changes: Consider if it can be done via extension API first
  5. Last resort: Document the patch needed and apply at build time

Check if File is in Submodule:

cd WAIIDE && git ls-files --error-unmatch path/to/file
# Exit 0 = tracked by submodule = DON'T TOUCH
# Exit non-zero = safe to modify

⚠️ ALSO IMPORTANT: The documentation has port inconsistencies. This bootstrap file reflects the actual implementation from the WAIIDE. Default ports are 8070 (main) and 8071 (WAIIDE), NOT 8080 as some docs claim.

🎯 Quick Context

WAIIDE (Web AI IDE) is a containerized WAIIDE Server designed for JupyterHub integration. It runs WAIIDE in a web browser, allowing users to spawn multiple isolated development environments through JupyterHub.

🏗️ Architecture Overview

┌─────────────────────────────────────────────────────┐
│                 WAIIDE Container                    │
├─────────────────────────────────────────────────────┤
│                                                     │
│  Entrypoint: /usr/local/bin/entrypoint-jupyterhub.sh │
│  ├─ Detects JupyterHub environment variables      │
│  ├─ Fixes permissions (root → UID 1000)           │
│  └─ Starts appropriate services                   │
│                                                     │
│  ┌───────────────────┬────────────────────────┐    │
│  │ JupyterHub Mode   │ Standalone Mode        │    │
│  ├───────────────────┼────────────────────────┤    │
│  │ jupyterhub-       │ api_server.py          │    │
│  │ singleuser        │ (Custom proxy)         │    │
│  │ Port: 8070*       │ Port: 8070*            │    │
│  │                   │                        │    │
│  │ jupyter-server-   │ Proxies all requests   │    │
│  │ proxy             │ to WAIIDE             │    │
│  └─────────┬─────────┴──────────┬─────────────┘    │
│            │                    │                   │
│            └────────┬───────────┘                   │
│                     ↓                               │
│         ┌─────────────────────┐                     │
│         │   WAIIDE Server    │                     │
│         │   Port: 8071*       │                     │
│         │   (localhost only)  │                     │
│         └─────────────────────┘                     │
└─────────────────────────────────────────────────────┘
* Default ports - can be overridden with env vars

🔧 Port Configuration (ACTUAL from WAIIDE)

ServiceDefault PortEnvironment VariableNotes
Main Service8070PORT or JUPYTERHUB_PORTExternal facing (JupyterHub/API)
WAIIDE Server8071VSCODE_PORTInternal only (localhost)

⚠️ Documentation Inconsistencies

  • Some docs incorrectly claim port 8080 as default
  • Various docs show different ports - trust the WAIIDE, not the docs
  • Actual defaults: 8070 (main service) and 8071 (WAIIDE)

🚀 Operating Modes

1. JupyterHub Mode (Production)

Activated when these env vars are present:

  • JUPYTERHUB_SERVICE_PREFIX (e.g., /user/alice/alice-waiide-abc123/)
  • JUPYTERHUB_USER (e.g., alice)
  • JUPYTERHUB_API_TOKEN

What happens:

  1. Starts jupyterhub-singleuser on port 8070
  2. Includes jupyter-server-proxy for routing
  3. WAIIDE accessible at root URL via proxy

2. Standalone Mode (Development)

Activated when NO JupyterHub env vars detected.

What happens:

  1. Starts api_server.py on port 8070
  2. Provides JupyterHub-compatible API endpoints
  3. Proxies all non-API requests to WAIIDE

📁 Key Files & Their Purpose

/usr/local/bin/
├── entrypoint-jupyterhub.sh  # Main entry - detects mode, fixes perms
└── api_server.py             # Standalone proxy server

/opt/calliope/
├── WAIIDE-server/            # WAIIDE Server binaries (system-wide)
└── scripts/                  # System scripts (jupyter_server_config.py, etc.)

/home/{username}/             # Dynamically created per user
├── workspace/                # User's WAIIDE and projects
└── .WAIIDE-server/          # User's WAIIDE data & extensions

🔑 Critical Environment Variables

JupyterHub Sets These:

JUPYTERHUB_SERVICE_PREFIX=/user/alice/alice-waiide-abc123/
JUPYTERHUB_USER=alice
JUPYTERHUB_SERVER_NAME=alice-waiide-abc123  # Full instance name
JUPYTERHUB_API_TOKEN=<oauth-token>
JUPYTERHUB_API_URL=http://hub:8081/hub/api

Container Configuration:

PORT=8070                    # Override main service port
VSCODE_PORT=8071            # Override WAIIDE port  
USE_ALL_COMPONENTS=true     # Enable all services
LOG_LEVEL=INFO              # Logging verbosity

🌐 URL Routing & Multi-Instance

Modern Architecture (Current)

  • Instance URLs: https://hub.example.com/user/alice/alice-waiide-abc123/
  • WAIIDE URL: https://hub.example.com/user/alice/alice-waiide-abc123/ide/
  • Unique naming: username-service-hash format
  • WAIIDE at /ide: Configured via jupyter-server-proxy
  • Multiple instances: Each user can have unlimited instances

Request Flow

Browser → JupyterHub Proxy → Container:8070 → jupyterhub-singleuser
    → jupyter-server-proxy → WAIIDE:8071 → Response

🔍 Common Debugging Commands

# Check running mode
docker exec <container> env | grep JUPYTER

# Verify services running
docker exec <container> ps aux | grep -E "(node|jupyter)"

# Check ports
docker exec <container> netstat -tlnp

# View logs
docker logs <container>

# Test API endpoints
curl http://localhost:8070/api/status
curl http://localhost:8070/api/services

# Access container
docker exec -it <container> /bin/bash

🐛 Troubleshooting Checklist

Container Issues

  • Check if port 8070 is already in use
  • Verify Docker network exists (for JupyterHub)
  • Ensure image is latest: docker pull calliopeai/waiide:latest
  • Check container has sufficient memory (4GB recommended)

JupyterHub Integration

  • Verify spawner sets correct environment variables
  • Check JUPYTERHUB_SERVER_NAME matches container name
  • Ensure OAuth scopes include server name for named instances
  • Confirm network connectivity between Hub and container

WAIIDE Not Loading

  • Wait 15-20 seconds for WAIIDE initialization
  • Check WAIIDE process: ps aux | grep node
  • Verify internal port 8071 is listening
  • Check for JavaScript console errors in browser

🚀 ECS-Specific Issues

Common ECS Boot Errors

1. WAIIDE Server Missing

Error: Cannot find module '/opt/calliope/WAIIDE-server/out/server-main.js'

Cause: Incomplete Docker image build or wrong architecture Fix: Rebuild with --platform linux/amd64 and verify files exist

2. Scripts Missing

cp: cannot stat '/opt/calliope/scripts/jupyter_server_config.py'

Cause: Scripts not properly copied during build Fix: Verify /opt/calliope/scripts/ contains all files

3. Port Conflicts

Common Spawner Misconfiguration:

JUPYTERHUB_PORT=8070
JUPYTERHUB_SERVICE_URL=http://0.0.0.0:8071  # Wrong! Should match PORT

Symptom: jupyterhub-singleuser fails with “port already in use” Fix: Ensure spawner sets JUPYTERHUB_SERVICE_URL to use port 8070 (or whatever PORT is)

Correct Configuration:

# All should use the same main port (8070)
PORT=8070
JUPYTERHUB_PORT=8070  
JUPYTERHUB_SERVICE_URL=http://0.0.0.0:8070

# WAIIDE uses different internal port
VSCODE_PORT=8071

4. File Collision Issues (v2.0)

Symptom:

cp: cannot create directory '/home/lmata/.WAIIDE-server/extensions/log': File exists

Cause: Extension copying without checking for existing files (v2.0 issue)

Fix in v2.1+:

  • Pre-built template at /opt/calliope/user-template/
  • Uses rsync with --ignore-existing or cp with -n (no-clobber)
  • Checks for existing directories before copying

Temporary Workaround for v2.0:

# Clear extensions and restart container
rm -rf /home/$USER/.WAIIDE-server/extensions/*

5. Missing Built-in Extensions

Symptom:

[Error]: Unable to read file '/opt/calliope/.WAIIDE-server/extensions/clojure/package.json'
[Error]: Unable to read file '/opt/calliope/.WAIIDE-server/extensions/coffeescript/package.json'
... (many similar errors for built-in extensions)

Cause: Template copy using --ignore-existing or -n flag prevents extensions from being copied when directory already exists

Fix in v2.2+:

  • Extensions are copied separately without no-clobber flag
  • Added fallback mechanism to verify and copy missing built-in extensions
  • Enhanced extension verification in post-setup debugging

Temporary Workaround:

# Copy extensions manually from system location
cp -rf /opt/calliope/WAIIDE-server/extensions/* /opt/calliope/.WAIIDE-server/extensions/

Note about CalliopeAI Agent Extension: If the calliope-WAIIDE-agent extension error persists, it may not have been built during the Docker image creation. This can happen if:

  • The calliope-WAIIDE-agent submodule wasn’t checked out
  • The build script failed silently
  • The extension wasn’t copied to the runtime image

To verify if the extension exists in the image:

ls -la /opt/calliope/WAIIDE-server/extensions/ | grep calliope

6. CI Build Extension Issues

Symptom: Extensions missing in CI-built Docker images, showing errors like:

[Error]: Unable to read file '/opt/calliope/.WAIIDE-server/extensions/markdown-language-features/package.json'
[Error]: Unable to read file '/opt/calliope/.WAIIDE-server/extensions/calliope-WAIIDE-agent/package.json'

Cause:

  • Extensions not properly built or copied during CI Docker build
  • Submodules not fully initialized in CI environment
  • Build failures not properly reported

Fix in v2.3+:

  • Added verification steps in Dockerfile to check extension copying
  • Improved error reporting during build process
  • Enhanced template copy mechanism to handle missing extensions

Immediate Workaround:

# Run the extension fix script inside the container
docker exec <container> fix-WAIIDE-extensions

# Or manually copy extensions
docker exec <container> bash -c "cp -rf /opt/calliope/WAIIDE-server/extensions/* /opt/calliope/.WAIIDE-server/extensions/"

CI Build Verification: To ensure extensions are included in CI builds:

  1. Check CI logs for “Copying WAIIDE extensions to build output”
  2. Verify extension count is > 90
  3. Look for key extension verification (✓ marks)

ECS Build & Deploy Commands

# Build for ECS (amd64)
docker buildx build --platform linux/amd64 --no-cache -t waiide:ecs .

# Test locally with ECS-like environment
docker run -it --rm \
  -e JUPYTERHUB_USER=testuser \
  -e JUPYTERHUB_SERVICE_PREFIX=/user/testuser/waiide/ \
  -e JUPYTERHUB_SERVICE_URL=http://0.0.0.0:8080/user/testuser/waiide/ \
  -e PORT=8080 \
  -p 8080:8080 \
  waiide:ecs

# Verify critical files exist
docker run -it --rm waiide:ecs find /home -name "server-main.js" -o -name "jupyter_server_config.py"

🏃 Quick Start Commands

Standalone Development

# Basic run (uses default ports 8070/8071)
docker run -p 8070:8070 calliopeai/waiide:latest

# With compose.yml (now uses default port 8070)
docker compose up

# Override to use port 8080 if needed
docker run -p 8080:8080 -e PORT=8080 calliopeai/waiide:latest

JupyterHub Simulation

docker run -p 8070:8070 \
  -e JUPYTERHUB_USER=testuser \
  -e JUPYTERHUB_SERVICE_PREFIX=/user/testuser/testuser-waiide-abc123/ \
  -e JUPYTERHUB_SERVER_NAME=testuser-waiide-abc123 \
  calliopeai/waiide:latest

📊 Health Check Endpoints

  • GET /api - Comprehensive service info
  • GET /api/status - Basic health check
  • GET /api/services - Service discovery with ports

🎯 Key Insights for Debugging

  1. Permission Dance: Container starts as root, fixes ownership, then drops to UID 1000
  2. Dynamic Home: Home directory set based on JUPYTERHUB_USER or defaults
  3. Port Flexibility: Main port can be 8070 (default), 8080, or any via PORT env
  4. OAuth Complexity: Named servers require proper scope configuration
  5. URL Rewriting: API server handles prefix stripping/adding in standalone mode

🚨 Known Issues

  1. Documentation Inconsistency: Some docs incorrectly show port 8080 - WAIIDE defaults to 8070
  2. OAuth with Named Servers: May need manual scope fixes for multi-instance
  3. Slow Startup (v2.0): Containers stuck in “Created” state for 10+ minutes - fixed in v2.1 with template approach
  4. Extension Limitations: Many desktop extensions don’t work in web version
  5. ECS Build Issues: Multi-stage builds may fail silently - always verify image contents
  6. Port Conflicts: Spawner misconfiguration can cause port conflicts between services

Remember: When debugging, always check the actual running processes and environment variables inside the container rather than relying solely on documentation.