diff --git a/AGENTS.md b/AGENTS.md index 3d0139d8..fd3d371a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,14 +4,12 @@ Guidelines for AI agents working on this codebase. ## Project Overview -This is a Cloudflare Worker that runs [Moltbot](https://molt.bot/) in a Cloudflare Sandbox container. It provides: -- Proxying to the Moltbot gateway (web UI + WebSocket) +This is a Cloudflare Worker that runs [OpenClaw](https://openclaw.ai/) in a Cloudflare Sandbox container. It provides: +- Proxying to the OpenClaw gateway (web UI + WebSocket) - Admin UI at `/_admin/` for device management - API endpoints at `/api/*` for device pairing - Debug endpoints at `/debug/*` for troubleshooting -**Note:** The CLI tool is still named `clawdbot` (upstream hasn't renamed yet), so CLI commands and internal config paths still use that name. - ## Project Structure ``` @@ -23,7 +21,7 @@ src/ │ ├── jwt.ts # JWT verification │ ├── jwks.ts # JWKS fetching and caching │ └── middleware.ts # Hono middleware for auth -├── gateway/ # Moltbot gateway management +├── gateway/ # OpenClaw gateway management │ ├── process.ts # Process lifecycle (find, start) │ ├── env.ts # Environment variable building │ ├── r2.ts # R2 bucket mounting @@ -43,16 +41,15 @@ src/ ### Environment Variables -- `DEV_MODE` - Skips CF Access auth AND bypasses device pairing (maps to `CLAWDBOT_DEV_MODE` for container) +- `DEV_MODE` - Skips CF Access auth AND bypasses device pairing (maps to `OPENCLAW_DEV_MODE` for container) - `DEBUG_ROUTES` - Enables `/debug/*` routes (disabled by default) - See `src/types.ts` for full `MoltbotEnv` interface ### CLI Commands -When calling the moltbot CLI from the worker, always include `--url ws://localhost:18789`. -Note: The CLI is still named `clawdbot` until upstream renames it: +When calling the openclaw CLI from the worker, always include `--url ws://localhost:18789`. ```typescript -sandbox.startProcess('clawdbot devices list --json --url ws://localhost:18789') +sandbox.startProcess('openclaw devices list --json --url ws://localhost:18789') ``` CLI commands take 10-15 seconds due to WebSocket connection overhead. Use `waitForProcess()` helper in `src/routes/api.ts`. @@ -114,7 +111,7 @@ Browser ▼ ┌─────────────────────────────────────┐ │ Cloudflare Worker (index.ts) │ -│ - Starts Moltbot in sandbox │ +│ - Starts OpenClaw in sandbox │ │ - Proxies HTTP/WebSocket requests │ │ - Passes secrets as env vars │ └──────────────┬──────────────────────┘ @@ -123,7 +120,7 @@ Browser ┌─────────────────────────────────────┐ │ Cloudflare Sandbox Container │ │ ┌───────────────────────────────┐ │ -│ │ Moltbot Gateway │ │ +│ │ OpenClaw Gateway │ │ │ │ - Control UI on port 18789 │ │ │ │ - WebSocket RPC protocol │ │ │ │ - Agent runtime │ │ @@ -136,9 +133,9 @@ Browser | File | Purpose | |------|---------| | `src/index.ts` | Worker that manages sandbox lifecycle and proxies requests | -| `Dockerfile` | Container image based on `cloudflare/sandbox` with Node 22 + Moltbot | -| `start-moltbot.sh` | Startup script that configures moltbot from env vars and launches gateway | -| `moltbot.json.template` | Default Moltbot configuration template | +| `Dockerfile` | Container image based on `cloudflare/sandbox` with Node 22 + OpenClaw | +| `start-moltbot.sh` | Startup script that configures openclaw from env vars and launches gateway | +| `moltbot.json.template` | Default OpenClaw configuration template | | `wrangler.jsonc` | Cloudflare Worker + Container configuration | ## Local Development @@ -169,14 +166,14 @@ Local development with `wrangler dev` has issues proxying WebSocket connections The Dockerfile includes a cache bust comment. When changing `moltbot.json.template` or `start-moltbot.sh`, bump the version: ```dockerfile -# Build cache bust: 2026-01-26-v10 +# Build cache bust: 2026-02-05-v28-workspace-persistence ``` ## Gateway Configuration -Moltbot configuration is built at container startup: +OpenClaw configuration is built at container startup: -1. `moltbot.json.template` is copied to `~/.clawdbot/clawdbot.json` (internal path unchanged) +1. `moltbot.json.template` is copied to `~/.openclaw/openclaw.json` 2. `start-moltbot.sh` updates the config with values from environment variables 3. Gateway starts with `--allow-unconfigured` flag (skips onboarding wizard) @@ -186,24 +183,24 @@ These are the env vars passed TO the container (internal names): | Variable | Config Path | Notes | |----------|-------------|-------| -| `ANTHROPIC_API_KEY` | (env var) | Moltbot reads directly from env | -| `CLAWDBOT_GATEWAY_TOKEN` | `--token` flag | Mapped from `MOLTBOT_GATEWAY_TOKEN` | -| `CLAWDBOT_DEV_MODE` | `controlUi.allowInsecureAuth` | Mapped from `DEV_MODE` | +| `ANTHROPIC_API_KEY` | (env var) | OpenClaw reads directly from env | +| `OPENCLAW_GATEWAY_TOKEN` | `--token` flag | Mapped from `MOLTBOT_GATEWAY_TOKEN` | +| `OPENCLAW_DEV_MODE` | `controlUi.allowInsecureAuth` | Mapped from `DEV_MODE` | | `TELEGRAM_BOT_TOKEN` | `channels.telegram.botToken` | | | `DISCORD_BOT_TOKEN` | `channels.discord.token` | | | `SLACK_BOT_TOKEN` | `channels.slack.botToken` | | | `SLACK_APP_TOKEN` | `channels.slack.appToken` | | -## Moltbot Config Schema +## OpenClaw Config Schema -Moltbot has strict config validation. Common gotchas: +OpenClaw has strict config validation. Common gotchas: - `agents.defaults.model` must be `{ "primary": "model/name" }` not a string - `gateway.mode` must be `"local"` for headless operation - No `webchat` channel - the Control UI is served automatically - `gateway.bind` is not a config option - use `--bind` CLI flag -See [Moltbot docs](https://docs.molt.bot/gateway/configuration) for full schema. +See [OpenClaw docs](https://docs.openclaw.ai/gateway/configuration) for full schema. ## Common Tasks @@ -235,7 +232,12 @@ Enable debug routes with `DEBUG_ROUTES=true` and check `/debug/processes`. ## R2 Storage Notes -R2 is mounted via s3fs at `/data/moltbot`. Important gotchas: +R2 is mounted via s3fs at `/data/moltbot`. The following directories are synced to R2: +- `/root/.openclaw/` → `openclaw/` (config, sessions, paired devices) +- `/root/clawd/skills/` → `skills/` (custom skills) +- `/root/clawd/` → `workspace/` (MEMORY.md, IDENTITY.md, memory/, user files) + +Important gotchas: - **rsync compatibility**: Use `rsync -r --no-times` instead of `rsync -a`. s3fs doesn't support setting timestamps, which causes rsync to fail with "Input/output error". diff --git a/Dockerfile b/Dockerfile index d7fd5d31..e66e07f1 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM docker.io/cloudflare/sandbox:0.7.0 -# Install Node.js 22 (required by clawdbot) and rsync (for R2 backup sync) +# Install Node.js 22 (required by openclaw) and rsync (for R2 backup sync) # The base image has Node 20, we need to replace it with Node 22 # Using direct binary download for reliability ENV NODE_VERSION=22.13.1 @@ -20,25 +20,32 @@ RUN ARCH="$(dpkg --print-architecture)" \ # Install pnpm globally RUN npm install -g pnpm -# Install moltbot (CLI is still named clawdbot until upstream renames) +# Install openclaw # Pin to specific version for reproducible builds -RUN npm install -g clawdbot@2026.1.24-3 \ - && clawdbot --version - -# Create moltbot directories (paths still use clawdbot until upstream renames) -# Templates are stored in /root/.clawdbot-templates for initialization -RUN mkdir -p /root/.clawdbot \ - && mkdir -p /root/.clawdbot-templates \ +# Skip optional deps (like Puppeteer/Chromium) to reduce size +# Clean up caches to reduce layer size +ENV PUPPETEER_SKIP_DOWNLOAD=true +ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=true +RUN npm install -g openclaw@2026.2.1 --omit=optional \ + && openclaw --version \ + && npm cache clean --force \ + && rm -rf /root/.npm /tmp/* /var/cache/apt/archives/* \ + && apt-get clean + +# Create openclaw directories +# Templates are stored in /root/.openclaw-templates for initialization +RUN mkdir -p /root/.openclaw \ + && mkdir -p /root/.openclaw-templates \ && mkdir -p /root/clawd \ && mkdir -p /root/clawd/skills # Copy startup script -# Build cache bust: 2026-01-28-v26-browser-skill +# Build cache bust: 2026-02-05-v28-workspace-persistence COPY start-moltbot.sh /usr/local/bin/start-moltbot.sh RUN chmod +x /usr/local/bin/start-moltbot.sh # Copy default configuration template -COPY moltbot.json.template /root/.clawdbot-templates/moltbot.json.template +COPY moltbot.json.template /root/.openclaw-templates/moltbot.json.template # Copy custom skills COPY skills/ /root/clawd/skills/ diff --git a/README.md b/README.md index 90bf7b72..a672bf60 100644 --- a/README.md +++ b/README.md @@ -87,7 +87,7 @@ To use the admin UI at `/_admin/` for device management, you need to: The easiest way to protect your worker is using the built-in Cloudflare Access integration for workers.dev: 1. Go to the [Workers & Pages dashboard](https://dash.cloudflare.com/?to=/:account/workers-and-pages) -2. Select your Worker (e.g., `moltbot-sandbox`) +2. Select your Worker (e.g., `openclaw-sandbox`) 3. In **Settings**, under **Domains & Routes**, in the `workers.dev` row, click the meatballs menu (`...`) 4. Click **Enable Cloudflare Access** 5. Click **Manage Cloudflare Access** to configure who can access: @@ -124,7 +124,7 @@ If you prefer more control, you can manually create an Access application: 1. Go to [Cloudflare Zero Trust Dashboard](https://one.dash.cloudflare.com/) 2. Navigate to **Access** > **Applications** 3. Create a new **Self-hosted** application -4. Set the application domain to your Worker URL (e.g., `moltbot-sandbox.your-subdomain.workers.dev`) +4. Set the application domain to your Worker URL (e.g., `openclaw-sandbox.your-subdomain.workers.dev`) 5. Add paths to protect: `/_admin/*`, `/api/*`, `/debug/*` 6. Configure your desired identity providers (e.g., email OTP, Google, GitHub) 7. Copy the **Application Audience (AUD)** tag and set the secrets as shown above @@ -140,7 +140,7 @@ DEBUG_ROUTES=true # Enable /debug/* routes (optional) ## Authentication -By default, moltbot uses **device pairing** for authentication. When a new device (browser, CLI, etc.) connects, it must be approved via the admin UI at `/_admin/`. +By default, OpenClaw uses **device pairing** for authentication. When a new device (browser, CLI, etc.) connects, it must be approved via the admin UI at `/_admin/`. ### Device Pairing @@ -166,7 +166,7 @@ For local development only, set `DEV_MODE=true` in `.dev.vars` to skip Cloudflar ## Persistent Storage (R2) -By default, moltbot data (configs, paired devices, conversation history) is lost when the container restarts. To enable persistent storage across sessions, configure R2: +By default, OpenClaw data (configs, paired devices, conversation history) is lost when the container restarts. To enable persistent storage across sessions, configure R2: ### 1. Create R2 API Token @@ -196,18 +196,18 @@ To find your Account ID: Go to the [Cloudflare Dashboard](https://dash.cloudflar R2 storage uses a backup/restore approach for simplicity: **On container startup:** -- If R2 is mounted and contains backup data, it's restored to the moltbot config directory +- If R2 is mounted and contains backup data, it's restored to the OpenClaw config directory - OpenClaw uses its default paths (no special configuration needed) **During operation:** -- A cron job runs every 5 minutes to sync the moltbot config to R2 +- A cron job runs every 5 minutes to sync the OpenClaw config to R2 - You can also trigger a manual backup from the admin UI at `/_admin/` **In the admin UI:** - When R2 is configured, you'll see "Last backup: [timestamp]" - Click "Backup Now" to trigger an immediate sync -Without R2 credentials, moltbot still works but uses ephemeral storage (data lost on container restart). +Without R2 credentials, OpenClaw still works but uses ephemeral storage (data lost on container restart). ## Container Lifecycle @@ -228,7 +228,7 @@ When the container sleeps, the next request will trigger a cold start. If you ha Access the admin UI at `/_admin/` to: - **R2 Storage Status** - Shows if R2 is configured, last backup time, and a "Backup Now" button -- **Restart Gateway** - Kill and restart the moltbot gateway process +- **Restart Gateway** - Kill and restart the OpenClaw gateway process - **Device Pairing** - View pending requests, approve devices individually or all at once, view paired devices The admin UI requires Cloudflare Access authentication (or `DEV_MODE=true` for local development). @@ -239,7 +239,7 @@ Debug endpoints are available at `/debug/*` when enabled (requires `DEBUG_ROUTES - `GET /debug/processes` - List all container processes - `GET /debug/logs?id=` - Get logs for a specific process -- `GET /debug/version` - Get container and moltbot version info +- `GET /debug/version` - Get container and OpenClaw version info ## Optional: Chat Channels diff --git a/package-lock.json b/package-lock.json index 170a6f26..e39562a4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "": { "name": "moltbot-sandbox", "version": "1.0.0", - "license": "MIT", + "license": "Apache-2.0", "dependencies": { "@cloudflare/puppeteer": "^1.0.5", "hono": "^4.11.6", @@ -432,6 +432,23 @@ "wrangler": "^4.60.0" } }, + "node_modules/@cloudflare/workerd-darwin-64": { + "version": "1.20260120.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-darwin-64/-/workerd-darwin-64-1.20260120.0.tgz", + "integrity": "sha512-JLHx3p5dpwz4wjVSis45YNReftttnI3ndhdMh5BUbbpdreN/g0jgxNt5Qp9tDFqEKl++N63qv+hxJiIIvSLR+Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=16" + } + }, "node_modules/@cloudflare/workerd-darwin-arm64": { "version": "1.20260120.0", "cpu": [ @@ -447,6 +464,57 @@ "node": ">=16" } }, + "node_modules/@cloudflare/workerd-linux-64": { + "version": "1.20260120.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-64/-/workerd-linux-64-1.20260120.0.tgz", + "integrity": "sha512-O0mIfJfvU7F8N5siCoRDaVDuI12wkz2xlG4zK6/Ct7U9c9FiE0ViXNFWXFQm5PPj+qbkNRyhjUwhP+GCKTk5EQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-linux-arm64": { + "version": "1.20260120.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-linux-arm64/-/workerd-linux-arm64-1.20260120.0.tgz", + "integrity": "sha512-aRHO/7bjxVpjZEmVVcpmhbzpN6ITbFCxuLLZSW0H9O0C0w40cDCClWSi19T87Ax/PQcYjFNT22pTewKsupkckA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=16" + } + }, + "node_modules/@cloudflare/workerd-windows-64": { + "version": "1.20260120.0", + "resolved": "https://registry.npmjs.org/@cloudflare/workerd-windows-64/-/workerd-windows-64-1.20260120.0.tgz", + "integrity": "sha512-ASZIz1E8sqZQqQCgcfY1PJbBpUDrxPt8NZ+lqNil0qxnO4qX38hbCsdDF2/TDAuq0Txh7nu8ztgTelfNDlb4EA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=16" + } + }, "node_modules/@cloudflare/workers-types": { "version": "4.20260124.0", "dev": true, @@ -463,6 +531,17 @@ "node": ">=12" } }, + "node_modules/@emnapi/runtime": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.8.1.tgz", + "integrity": "sha512-mehfKSMWjjNol8659Z8KxEMrdSJDDot5SXMq00dM8BN4o+CLNXQ0xH2V7EchNHV4RmbZLmmPdEaXZc5H2FXmDg==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.25.12", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", @@ -932,6 +1011,29 @@ "@img/sharp-libvips-darwin-arm64": "1.2.4" } }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.5.tgz", + "integrity": "sha512-YNEFAF/4KQ/PeW0N+r+aVVsoIY0/qxxikF2SWdp+NRkmMB7y9LBZAVqQ4yhGCm/H3H270OSykqmQMKLBhBJDEw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.4" + } + }, "node_modules/@img/sharp-libvips-darwin-arm64": { "version": "1.2.4", "cpu": [ @@ -947,6 +1049,423 @@ "url": "https://opencollective.com/libvips" } }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.4.tgz", + "integrity": "sha512-1IOd5xfVhlGwX+zXv2N93k0yMONvUlANylbJw1eTah8K/Jtpi15KC+WSiaX/nBmbm2HxRM1gZ0nSdjSsrZbGKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.4.tgz", + "integrity": "sha512-bFI7xcKFELdiNCVov8e44Ia4u2byA+l3XtsAj+Q8tfCwO6BQ8iDojYdvoPMqsKDkuoOo+X6HZA0s0q11ANMQ8A==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.4.tgz", + "integrity": "sha512-excjX8DfsIcJ10x1Kzr4RcWe1edC9PquDRRPx3YVCvQv+U5p7Yin2s32ftzikXojb1PIFc/9Mt28/y+iRklkrw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.4.tgz", + "integrity": "sha512-FMuvGijLDYG6lW+b/UvyilUWu5Ayu+3r2d1S8notiGCIyYU/76eig1UfMmkZ7vwgOrzKzlQbFSuQfgm7GYUPpA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-riscv64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-riscv64/-/sharp-libvips-linux-riscv64-1.2.4.tgz", + "integrity": "sha512-oVDbcR4zUC0ce82teubSm+x6ETixtKZBh/qbREIOcI3cULzDyb18Sr/Wcyx7NRQeQzOiHTNbZFF1UwPS2scyGA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.4.tgz", + "integrity": "sha512-qmp9VrzgPgMoGZyPvrQHqk02uyjA0/QrTO26Tqk6l4ZV0MPWIW6LTkqOIov+J1yEu7MbFQaDpwdwJKhbJvuRxQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.4.tgz", + "integrity": "sha512-tJxiiLsmHc9Ax1bz3oaOYBURTXGIRDODBqhveVHonrHJ9/+k89qbLl0bcJns+e4t4rvaNBxaEZsFtSfAdquPrw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.4.tgz", + "integrity": "sha512-FVQHuwx1IIuNow9QAbYUzJ+En8KcVm9Lk5+uGUQJHaZmMECZmOlix9HnH7n1TRkXMS0pGxIJokIVB9SuqZGGXw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.4.tgz", + "integrity": "sha512-+LpyBk7L44ZIXwz/VYfglaX/okxezESc6UxDSoyo2Ks6Jxc4Y7sGjpgU9s4PMgqgjj1gZCylTieNamqA1MF7Dg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.5.tgz", + "integrity": "sha512-9dLqsvwtg1uuXBGZKsxem9595+ujv0sJ6Vi8wcTANSFpwV/GONat5eCkzQo/1O6zRIkh0m/8+5BjrRr7jDUSZw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.5.tgz", + "integrity": "sha512-bKQzaJRY/bkPOXyKx5EVup7qkaojECG6NLYswgktOZjaXecSAeCWiZwwiFf3/Y+O1HrauiE3FVsGxFg8c24rZg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.5.tgz", + "integrity": "sha512-7zznwNaqW6YtsfrGGDA6BRkISKAAE1Jo0QdpNYXNMHu2+0dTrPflTLNkpc8l7MUP5M16ZJcUvysVWWrMefZquA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-riscv64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-riscv64/-/sharp-linux-riscv64-0.34.5.tgz", + "integrity": "sha512-51gJuLPTKa7piYPaVs8GmByo7/U7/7TZOq+cnXJIHZKavIRHAP77e3N2HEl3dgiqdD/w0yUfiJnII77PuDDFdw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-riscv64": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.5.tgz", + "integrity": "sha512-nQtCk0PdKfho3eC5MrbQoigJ2gd1CgddUMkabUj+rBevs8tZ2cULOx46E7oyX+04WGfABgIwmMC0VqieTiR4jg==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.4" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.5.tgz", + "integrity": "sha512-MEzd8HPKxVxVenwAa+JRPwEC7QFjoPWuS5NZnBt6B3pu7EG2Ge0id1oLHZpPJdn3OQK+BQDiw9zStiHBTJQQQQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.5.tgz", + "integrity": "sha512-fprJR6GtRsMt6Kyfq44IsChVZeGN97gTD331weR1ex1c1rypDEABN6Tm2xa1wE6lYb5DdEnk03NZPqA7Id21yg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.4" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.5.tgz", + "integrity": "sha512-Jg8wNT1MUzIvhBFxViqrEhWDGzqymo3sV7z7ZsaWbZNDLXRJZoRGrjulp60YYtV4wfY8VIKcWidjojlLcWrd8Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.4" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.5.tgz", + "integrity": "sha512-OdWTEiVkY2PHwqkbBI8frFxQQFekHaSSkUIJkwzclWZe64O1X4UlUjqqqLaPbUpMOQk6FBu/HtlGXNblIs0huw==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.7.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.5.tgz", + "integrity": "sha512-WQ3AgWCWYSb2yt+IG8mnC6Jdk9Whs7O0gxphblsLvdhSpSTtmu69ZG1Gkb6NuvxsNACwiPV6cNSZNzt0KPsw7g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.5.tgz", + "integrity": "sha512-FV9m/7NmeCmSHDD5j4+4pNI8Cp3aW+JvLoXcTUo0IqyjSfAZJ8dIUmijx1qaJsIiU+Hosw6xM5KijAWRJCSgNg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.5", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.5.tgz", + "integrity": "sha512-+29YMsqY2/9eFEiW93eqWnuLcWcufowXewwSNIT6UwZdUUCrM3oFjMWH/Z6/TMmb4hlFenmfAVbpWeup2jryCw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.13", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", diff --git a/skills/cloudflare-browser/SKILL.md b/skills/cloudflare-browser/SKILL.md index 93053a7b..0c89c4b3 100644 --- a/skills/cloudflare-browser/SKILL.md +++ b/skills/cloudflare-browser/SKILL.md @@ -10,7 +10,7 @@ Control headless browsers via Cloudflare's Browser Rendering service using CDP ( ## Prerequisites - `CDP_SECRET` environment variable set -- Browser profile configured in clawdbot.json with `cdpUrl` pointing to the worker endpoint: +- Browser profile configured in openclaw.json with `cdpUrl` pointing to the worker endpoint: ```json "browser": { "profiles": { diff --git a/src/gateway/env.test.ts b/src/gateway/env.test.ts index 29f033db..a7616a1e 100644 --- a/src/gateway/env.test.ts +++ b/src/gateway/env.test.ts @@ -85,10 +85,10 @@ describe('buildEnvVars', () => { expect(result.OPENAI_API_KEY).toBe('sk-openai-key'); }); - it('maps MOLTBOT_GATEWAY_TOKEN to CLAWDBOT_GATEWAY_TOKEN for container', () => { + it('maps MOLTBOT_GATEWAY_TOKEN to OPENCLAW_GATEWAY_TOKEN for container', () => { const env = createMockEnv({ MOLTBOT_GATEWAY_TOKEN: 'my-token' }); const result = buildEnvVars(env); - expect(result.CLAWDBOT_GATEWAY_TOKEN).toBe('my-token'); + expect(result.OPENCLAW_GATEWAY_TOKEN).toBe('my-token'); }); it('includes all channel tokens when set', () => { @@ -110,15 +110,15 @@ describe('buildEnvVars', () => { expect(result.SLACK_APP_TOKEN).toBe('slack-app'); }); - it('maps DEV_MODE to CLAWDBOT_DEV_MODE for container', () => { + it('maps DEV_MODE to OPENCLAW_DEV_MODE for container', () => { const env = createMockEnv({ DEV_MODE: 'true', - CLAWDBOT_BIND_MODE: 'lan', + OPENCLAW_BIND_MODE: 'lan', }); const result = buildEnvVars(env); - expect(result.CLAWDBOT_DEV_MODE).toBe('true'); - expect(result.CLAWDBOT_BIND_MODE).toBe('lan'); + expect(result.OPENCLAW_DEV_MODE).toBe('true'); + expect(result.OPENCLAW_BIND_MODE).toBe('lan'); }); it('combines all env vars correctly', () => { @@ -131,7 +131,7 @@ describe('buildEnvVars', () => { expect(result).toEqual({ ANTHROPIC_API_KEY: 'sk-key', - CLAWDBOT_GATEWAY_TOKEN: 'token', + OPENCLAW_GATEWAY_TOKEN: 'token', TELEGRAM_BOT_TOKEN: 'tg', }); }); diff --git a/src/gateway/env.ts b/src/gateway/env.ts index a57e781b..d90049b4 100644 --- a/src/gateway/env.ts +++ b/src/gateway/env.ts @@ -43,10 +43,10 @@ export function buildEnvVars(env: MoltbotEnv): Record { } else if (env.ANTHROPIC_BASE_URL) { envVars.ANTHROPIC_BASE_URL = env.ANTHROPIC_BASE_URL; } - // Map MOLTBOT_GATEWAY_TOKEN to CLAWDBOT_GATEWAY_TOKEN (container expects this name) - if (env.MOLTBOT_GATEWAY_TOKEN) envVars.CLAWDBOT_GATEWAY_TOKEN = env.MOLTBOT_GATEWAY_TOKEN; - if (env.DEV_MODE) envVars.CLAWDBOT_DEV_MODE = env.DEV_MODE; // Pass DEV_MODE as CLAWDBOT_DEV_MODE to container - if (env.CLAWDBOT_BIND_MODE) envVars.CLAWDBOT_BIND_MODE = env.CLAWDBOT_BIND_MODE; + // Map MOLTBOT_GATEWAY_TOKEN to OPENCLAW_GATEWAY_TOKEN (container expects this name) + if (env.MOLTBOT_GATEWAY_TOKEN) envVars.OPENCLAW_GATEWAY_TOKEN = env.MOLTBOT_GATEWAY_TOKEN; + if (env.DEV_MODE) envVars.OPENCLAW_DEV_MODE = env.DEV_MODE; // Pass DEV_MODE as OPENCLAW_DEV_MODE to container + if (env.OPENCLAW_BIND_MODE) envVars.OPENCLAW_BIND_MODE = env.OPENCLAW_BIND_MODE; if (env.TELEGRAM_BOT_TOKEN) envVars.TELEGRAM_BOT_TOKEN = env.TELEGRAM_BOT_TOKEN; if (env.TELEGRAM_DM_POLICY) envVars.TELEGRAM_DM_POLICY = env.TELEGRAM_DM_POLICY; if (env.DISCORD_BOT_TOKEN) envVars.DISCORD_BOT_TOKEN = env.DISCORD_BOT_TOKEN; diff --git a/src/gateway/process.test.ts b/src/gateway/process.test.ts index 4243658d..bdedd14b 100644 --- a/src/gateway/process.test.ts +++ b/src/gateway/process.test.ts @@ -7,7 +7,7 @@ import { createMockSandbox } from '../test-utils'; function createFullMockProcess(overrides: Partial = {}): Process { return { id: 'test-id', - command: 'clawdbot gateway', + command: 'openclaw gateway', status: 'running', startTime: new Date(), endTime: undefined, @@ -28,8 +28,8 @@ describe('findExistingMoltbotProcess', () => { it('returns null when only CLI commands are running', async () => { const processes = [ - createFullMockProcess({ command: 'clawdbot devices list --json', status: 'running' }), - createFullMockProcess({ command: 'clawdbot --version', status: 'completed' }), + createFullMockProcess({ command: 'openclaw devices list --json', status: 'running' }), + createFullMockProcess({ command: 'openclaw --version', status: 'completed' }), ]; const { sandbox, listProcessesMock } = createMockSandbox(); listProcessesMock.mockResolvedValue(processes); @@ -41,11 +41,11 @@ describe('findExistingMoltbotProcess', () => { it('returns gateway process when running', async () => { const gatewayProcess = createFullMockProcess({ id: 'gateway-1', - command: 'clawdbot gateway --port 18789', + command: 'openclaw gateway --port 18789', status: 'running' }); const processes = [ - createFullMockProcess({ command: 'clawdbot devices list', status: 'completed' }), + createFullMockProcess({ command: 'openclaw devices list', status: 'completed' }), gatewayProcess, ]; const { sandbox, listProcessesMock } = createMockSandbox(); @@ -70,7 +70,7 @@ describe('findExistingMoltbotProcess', () => { it('ignores completed gateway processes', async () => { const processes = [ - createFullMockProcess({ command: 'clawdbot gateway', status: 'completed' }), + createFullMockProcess({ command: 'openclaw gateway', status: 'completed' }), createFullMockProcess({ command: 'start-moltbot.sh', status: 'failed' }), ]; const { sandbox, listProcessesMock } = createMockSandbox(); @@ -105,7 +105,7 @@ describe('findExistingMoltbotProcess', () => { it('returns first matching gateway process', async () => { const firstGateway = createFullMockProcess({ id: 'gateway-1', - command: 'clawdbot gateway', + command: 'openclaw gateway', status: 'running' }); const secondGateway = createFullMockProcess({ diff --git a/src/gateway/process.ts b/src/gateway/process.ts index aa35e069..ef17a460 100644 --- a/src/gateway/process.ts +++ b/src/gateway/process.ts @@ -14,14 +14,13 @@ export async function findExistingMoltbotProcess(sandbox: Sandbox): Promise { }); describe('sanity checks', () => { - it('returns error when source is missing clawdbot.json', async () => { + it('returns error when source is missing openclaw.json', async () => { const { sandbox, startProcessMock } = createMockSandbox(); startProcessMock .mockResolvedValueOnce(createMockProcess('s3fs on /data/moltbot type fuse.s3fs\n')) @@ -49,9 +49,8 @@ describe('syncToR2', () => { const result = await syncToR2(sandbox, env); - // Error message still references clawdbot.json since that's the actual file name expect(result.success).toBe(false); - expect(result.error).toBe('Sync aborted: source missing clawdbot.json'); + expect(result.error).toBe('Sync aborted: source missing openclaw.json'); expect(result.details).toContain('missing critical files'); }); }); @@ -108,13 +107,17 @@ describe('syncToR2', () => { await syncToR2(sandbox, env); - // Third call should be rsync (paths still use clawdbot internally) + // Third call should be rsync const rsyncCall = startProcessMock.mock.calls[2][0]; expect(rsyncCall).toContain('rsync'); expect(rsyncCall).toContain('--no-times'); expect(rsyncCall).toContain('--delete'); - expect(rsyncCall).toContain('/root/.clawdbot/'); - expect(rsyncCall).toContain('/data/moltbot/'); + expect(rsyncCall).toContain('/root/.openclaw/'); + expect(rsyncCall).toContain('/data/moltbot/openclaw/'); + expect(rsyncCall).toContain('/root/clawd/skills/'); + expect(rsyncCall).toContain('/data/moltbot/skills/'); + expect(rsyncCall).toContain('/root/clawd/'); + expect(rsyncCall).toContain('/data/moltbot/workspace/'); }); }); }); diff --git a/src/gateway/sync.ts b/src/gateway/sync.ts index a10c711a..0de8d923 100644 --- a/src/gateway/sync.ts +++ b/src/gateway/sync.ts @@ -39,13 +39,13 @@ export async function syncToR2(sandbox: Sandbox, env: MoltbotEnv): Promise ${R2_MOUNT_PATH}/.last-sync`; + // Sync: config, skills, and workspace (MEMORY.md, IDENTITY.md, etc.) + const syncCmd = `rsync -r --no-times --delete --exclude='*.lock' --exclude='*.log' --exclude='*.tmp' /root/.openclaw/ ${R2_MOUNT_PATH}/openclaw/ && rsync -r --no-times --delete /root/clawd/skills/ ${R2_MOUNT_PATH}/skills/ && rsync -r --no-times --delete --exclude='skills/' /root/clawd/ ${R2_MOUNT_PATH}/workspace/ && date -Iseconds > ${R2_MOUNT_PATH}/.last-sync`; try { const proc = await sandbox.startProcess(syncCmd); @@ -67,7 +68,7 @@ export async function syncToR2(sandbox: Sandbox, env: MoltbotEnv): Promise { // Ensure moltbot is running first await ensureMoltbotGateway(sandbox, c.env); - // Run moltbot CLI to list devices (CLI is still named clawdbot until upstream renames) + // Run openclaw CLI to list devices // Must specify --url to connect to the gateway running in the same container - const proc = await sandbox.startProcess('clawdbot devices list --json --url ws://localhost:18789'); + const proc = await sandbox.startProcess('openclaw devices list --json --url ws://localhost:18789'); await waitForProcess(proc, CLI_TIMEOUT_MS); const logs = await proc.getLogs(); @@ -84,8 +84,8 @@ adminApi.post('/devices/:requestId/approve', async (c) => { // Ensure moltbot is running first await ensureMoltbotGateway(sandbox, c.env); - // Run moltbot CLI to approve the device (CLI is still named clawdbot) - const proc = await sandbox.startProcess(`clawdbot devices approve ${requestId} --url ws://localhost:18789`); + // Run openclaw CLI to approve the device + const proc = await sandbox.startProcess(`openclaw devices approve ${requestId} --url ws://localhost:18789`); await waitForProcess(proc, CLI_TIMEOUT_MS); const logs = await proc.getLogs(); @@ -116,8 +116,8 @@ adminApi.post('/devices/approve-all', async (c) => { // Ensure moltbot is running first await ensureMoltbotGateway(sandbox, c.env); - // First, get the list of pending devices (CLI is still named clawdbot) - const listProc = await sandbox.startProcess('clawdbot devices list --json --url ws://localhost:18789'); + // First, get the list of pending devices + const listProc = await sandbox.startProcess('openclaw devices list --json --url ws://localhost:18789'); await waitForProcess(listProc, CLI_TIMEOUT_MS); const listLogs = await listProc.getLogs(); @@ -144,7 +144,7 @@ adminApi.post('/devices/approve-all', async (c) => { for (const device of pending) { try { - const approveProc = await sandbox.startProcess(`clawdbot devices approve ${device.requestId} --url ws://localhost:18789`); + const approveProc = await sandbox.startProcess(`openclaw devices approve ${device.requestId} --url ws://localhost:18789`); await waitForProcess(approveProc, CLI_TIMEOUT_MS); const approveLogs = await approveProc.getLogs(); diff --git a/src/routes/debug.ts b/src/routes/debug.ts index 612eb6f5..0850cff5 100644 --- a/src/routes/debug.ts +++ b/src/routes/debug.ts @@ -13,8 +13,8 @@ const debug = new Hono(); debug.get('/version', async (c) => { const sandbox = c.get('sandbox'); try { - // Get moltbot version (CLI is still named clawdbot until upstream renames) - const versionProcess = await sandbox.startProcess('clawdbot --version'); + // Get openclaw version + const versionProcess = await sandbox.startProcess('openclaw --version'); await new Promise(resolve => setTimeout(resolve, 500)); const versionLogs = await versionProcess.getLogs(); const moltbotVersion = (versionLogs.stdout || versionLogs.stderr || '').trim(); @@ -123,10 +123,10 @@ debug.get('/gateway-api', async (c) => { } }); -// GET /debug/cli - Test moltbot CLI commands (CLI is still named clawdbot) +// GET /debug/cli - Test openclaw CLI commands debug.get('/cli', async (c) => { const sandbox = c.get('sandbox'); - const cmd = c.req.query('cmd') || 'clawdbot --help'; + const cmd = c.req.query('cmd') || 'openclaw --help'; try { const proc = await sandbox.startProcess(cmd); @@ -347,7 +347,7 @@ debug.get('/env', async (c) => { has_cf_account_id: !!c.env.CF_ACCOUNT_ID, dev_mode: c.env.DEV_MODE, debug_routes: c.env.DEBUG_ROUTES, - bind_mode: c.env.CLAWDBOT_BIND_MODE, + bind_mode: c.env.OPENCLAW_BIND_MODE, cf_access_team_domain: c.env.CF_ACCESS_TEAM_DOMAIN, has_cf_access_aud: !!c.env.CF_ACCESS_AUD, }); @@ -358,7 +358,7 @@ debug.get('/container-config', async (c) => { const sandbox = c.get('sandbox'); try { - const proc = await sandbox.startProcess('cat /root/.clawdbot/clawdbot.json'); + const proc = await sandbox.startProcess('cat /root/.openclaw/openclaw.json'); let attempts = 0; while (attempts < 10) { diff --git a/src/types.ts b/src/types.ts index d0fe5450..da45bf0a 100644 --- a/src/types.ts +++ b/src/types.ts @@ -14,9 +14,9 @@ export interface MoltbotEnv { ANTHROPIC_API_KEY?: string; ANTHROPIC_BASE_URL?: string; OPENAI_API_KEY?: string; - MOLTBOT_GATEWAY_TOKEN?: string; // Gateway token (mapped to CLAWDBOT_GATEWAY_TOKEN for container) + MOLTBOT_GATEWAY_TOKEN?: string; // Gateway token (mapped to OPENCLAW_GATEWAY_TOKEN for container) - CLAWDBOT_BIND_MODE?: string; + OPENCLAW_BIND_MODE?: string; DEV_MODE?: string; // Set to 'true' for local dev (skips CF Access auth + moltbot device pairing) E2E_TEST_MODE?: string; // Set to 'true' for E2E tests (skips CF Access auth but keeps device pairing) DEBUG_ROUTES?: string; // Set to 'true' to enable /debug/* routes diff --git a/start-moltbot.sh b/start-moltbot.sh index 286a4d67..56c0361c 100644 --- a/start-moltbot.sh +++ b/start-moltbot.sh @@ -1,39 +1,81 @@ #!/bin/bash -# Startup script for Moltbot in Cloudflare Sandbox +# Startup script for OpenClaw in Cloudflare Sandbox # This script: -# 1. Restores config from R2 backup if available -# 2. Configures moltbot from environment variables -# 3. Starts a background sync to backup config to R2 -# 4. Starts the gateway +# 1. Migrates config from old clawdbot paths if needed (one-time) +# 2. Restores config from R2 backup if available +# 3. Configures openclaw from environment variables +# 4. Starts a background sync to backup config to R2 +# 5. Starts the gateway set -e -# Check if clawdbot gateway is already running - bail early if so -# Note: CLI is still named "clawdbot" until upstream renames it -if pgrep -f "clawdbot gateway" > /dev/null 2>&1; then - echo "Moltbot gateway is already running, exiting." +# Check if openclaw gateway is already running - bail early if so +if pgrep -f "openclaw gateway" > /dev/null 2>&1; then + echo "OpenClaw gateway is already running, exiting." exit 0 fi -# Paths (clawdbot paths are used internally - upstream hasn't renamed yet) -CONFIG_DIR="/root/.clawdbot" -CONFIG_FILE="$CONFIG_DIR/clawdbot.json" -TEMPLATE_DIR="/root/.clawdbot-templates" +# Paths +CONFIG_DIR="/root/.openclaw" +CONFIG_FILE="$CONFIG_DIR/openclaw.json" +TEMPLATE_DIR="/root/.openclaw-templates" TEMPLATE_FILE="$TEMPLATE_DIR/moltbot.json.template" BACKUP_DIR="/data/moltbot" +# Legacy paths (for migration) +OLD_CONFIG_DIR="/root/.clawdbot" +OLD_BACKUP_SUBDIR="$BACKUP_DIR/clawdbot" +NEW_BACKUP_SUBDIR="$BACKUP_DIR/openclaw" + echo "Config directory: $CONFIG_DIR" echo "Backup directory: $BACKUP_DIR" # Create config directory mkdir -p "$CONFIG_DIR" +# ============================================================ +# MIGRATE FROM CLAWDBOT TO OPENCLAW (ONE-TIME) +# ============================================================ +# Check if we need to migrate R2 backup from old clawdbot paths +if [ -f "$OLD_BACKUP_SUBDIR/clawdbot.json" ] && [ ! -f "$NEW_BACKUP_SUBDIR/openclaw.json" ]; then + echo "=== MIGRATION: Migrating R2 backup from clawdbot to openclaw ===" + mkdir -p "$NEW_BACKUP_SUBDIR" + + # Copy all files from old backup to new backup + cp -a "$OLD_BACKUP_SUBDIR/." "$NEW_BACKUP_SUBDIR/" + + # Rename the config file + if [ -f "$NEW_BACKUP_SUBDIR/clawdbot.json" ]; then + mv "$NEW_BACKUP_SUBDIR/clawdbot.json" "$NEW_BACKUP_SUBDIR/openclaw.json" + echo "Renamed clawdbot.json to openclaw.json in R2 backup" + fi + + echo "=== MIGRATION: R2 backup migration complete ===" + echo "Note: Old backup preserved at $OLD_BACKUP_SUBDIR (can be deleted manually)" +fi + +# Check if we need to migrate local config from old clawdbot paths +if [ -f "$OLD_CONFIG_DIR/clawdbot.json" ] && [ ! -f "$CONFIG_FILE" ]; then + echo "=== MIGRATION: Migrating local config from clawdbot to openclaw ===" + + # Copy all files from old config dir to new config dir + cp -a "$OLD_CONFIG_DIR/." "$CONFIG_DIR/" + + # Rename the config file + if [ -f "$CONFIG_DIR/clawdbot.json" ]; then + mv "$CONFIG_DIR/clawdbot.json" "$CONFIG_FILE" + echo "Renamed clawdbot.json to openclaw.json in local config" + fi + + echo "=== MIGRATION: Local config migration complete ===" +fi + # ============================================================ # RESTORE FROM R2 BACKUP # ============================================================ -# Check if R2 backup exists by looking for clawdbot.json +# Check if R2 backup exists by looking for openclaw.json # The BACKUP_DIR may exist but be empty if R2 was just mounted -# Note: backup structure is $BACKUP_DIR/clawdbot/ and $BACKUP_DIR/skills/ +# Note: backup structure is $BACKUP_DIR/openclaw/ and $BACKUP_DIR/skills/ # Helper function to check if R2 backup is newer than local should_restore_from_r2() { @@ -72,15 +114,15 @@ should_restore_from_r2() { fi } -if [ -f "$BACKUP_DIR/clawdbot/clawdbot.json" ]; then +if [ -f "$NEW_BACKUP_SUBDIR/openclaw.json" ]; then if should_restore_from_r2; then - echo "Restoring from R2 backup at $BACKUP_DIR/clawdbot..." - cp -a "$BACKUP_DIR/clawdbot/." "$CONFIG_DIR/" + echo "Restoring from R2 backup at $NEW_BACKUP_SUBDIR..." + cp -a "$NEW_BACKUP_SUBDIR/." "$CONFIG_DIR/" # Copy the sync timestamp to local so we know what version we have cp -f "$BACKUP_DIR/.last-sync" "$CONFIG_DIR/.last-sync" 2>/dev/null || true echo "Restored config from R2 backup" fi -elif [ -f "$BACKUP_DIR/clawdbot.json" ]; then +elif [ -f "$BACKUP_DIR/openclaw.json" ]; then # Legacy backup format (flat structure) if should_restore_from_r2; then echo "Restoring from legacy R2 backup at $BACKUP_DIR..." @@ -105,6 +147,19 @@ if [ -d "$BACKUP_DIR/skills" ] && [ "$(ls -A $BACKUP_DIR/skills 2>/dev/null)" ]; fi fi +# Restore workspace files from R2 backup if available (only if R2 is newer) +# This includes MEMORY.md, IDENTITY.md, memory/, and other user files +WORKSPACE_DIR="/root/clawd" +if [ -d "$BACKUP_DIR/workspace" ] && [ "$(ls -A $BACKUP_DIR/workspace 2>/dev/null)" ]; then + if should_restore_from_r2; then + echo "Restoring workspace from $BACKUP_DIR/workspace..." + mkdir -p "$WORKSPACE_DIR" + # Restore workspace but don't overwrite skills (handled separately) + rsync -r --exclude='skills/' "$BACKUP_DIR/workspace/" "$WORKSPACE_DIR/" + echo "Restored workspace from R2 backup" + fi +fi + # If config file still doesn't exist, create from template if [ ! -f "$CONFIG_FILE" ]; then echo "No existing config found, initializing from template..." @@ -136,7 +191,7 @@ fi node << EOFNODE const fs = require('fs'); -const configPath = '/root/.clawdbot/clawdbot.json'; +const configPath = '/root/.openclaw/openclaw.json'; console.log('Updating config at:', configPath); let config = {}; @@ -171,13 +226,13 @@ config.gateway.mode = 'local'; config.gateway.trustedProxies = ['10.1.0.0']; // Set gateway token if provided -if (process.env.CLAWDBOT_GATEWAY_TOKEN) { +if (process.env.OPENCLAW_GATEWAY_TOKEN) { config.gateway.auth = config.gateway.auth || {}; - config.gateway.auth.token = process.env.CLAWDBOT_GATEWAY_TOKEN; + config.gateway.auth.token = process.env.OPENCLAW_GATEWAY_TOKEN; } // Allow insecure auth for dev mode -if (process.env.CLAWDBOT_DEV_MODE === 'true') { +if (process.env.OPENCLAW_DEV_MODE === 'true') { config.gateway.controlUi = config.gateway.controlUi || {}; config.gateway.controlUi.allowInsecureAuth = true; } @@ -200,7 +255,7 @@ if (process.env.TELEGRAM_BOT_TOKEN) { // Discord configuration // Note: Discord uses nested dm.policy, not flat dmPolicy like Telegram -// See: https://github.com/moltbot/moltbot/blob/v2026.1.24-1/src/config/zod-schema.providers-core.ts#L147-L155 +// See: https://github.com/openclaw/openclaw docs for schema if (process.env.DISCORD_BOT_TOKEN) { config.channels.discord = config.channels.discord || {}; config.channels.discord.token = process.env.DISCORD_BOT_TOKEN; @@ -231,7 +286,7 @@ const isOpenAI = baseUrl.endsWith('/openai'); if (isOpenAI) { // Create custom openai provider config with baseUrl override - // Omit apiKey so moltbot falls back to OPENAI_API_KEY env var + // Omit apiKey so openclaw falls back to OPENAI_API_KEY env var console.log('Configuring OpenAI provider with base URL:', baseUrl); config.models = config.models || {}; config.models.providers = config.models.providers || {}; @@ -289,20 +344,20 @@ EOFNODE # START GATEWAY # ============================================================ # Note: R2 backup sync is handled by the Worker's cron trigger -echo "Starting Moltbot Gateway..." +echo "Starting OpenClaw Gateway..." echo "Gateway will be available on port 18789" # Clean up stale lock files -rm -f /tmp/clawdbot-gateway.lock 2>/dev/null || true +rm -f /tmp/openclaw-gateway.lock 2>/dev/null || true rm -f "$CONFIG_DIR/gateway.lock" 2>/dev/null || true BIND_MODE="lan" -echo "Dev mode: ${CLAWDBOT_DEV_MODE:-false}, Bind mode: $BIND_MODE" +echo "Dev mode: ${OPENCLAW_DEV_MODE:-false}, Bind mode: $BIND_MODE" -if [ -n "$CLAWDBOT_GATEWAY_TOKEN" ]; then +if [ -n "$OPENCLAW_GATEWAY_TOKEN" ]; then echo "Starting gateway with token auth..." - exec clawdbot gateway --port 18789 --verbose --allow-unconfigured --bind "$BIND_MODE" --token "$CLAWDBOT_GATEWAY_TOKEN" + exec openclaw gateway --port 18789 --verbose --allow-unconfigured --bind "$BIND_MODE" --token "$OPENCLAW_GATEWAY_TOKEN" else echo "Starting gateway with device pairing (no token)..." - exec clawdbot gateway --port 18789 --verbose --allow-unconfigured --bind "$BIND_MODE" + exec openclaw gateway --port 18789 --verbose --allow-unconfigured --bind "$BIND_MODE" fi