From f41e7325a9cc819edb7912506a73cd98e510d00c Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 24 Feb 2026 17:31:02 -0800 Subject: [PATCH 01/20] openclaw gateway instructions --- docs/agents/openclaw-gateway.mdx | 159 +++++++++++++++++++++++++++++++ docs/agents/openclaw.mdx | 2 + 2 files changed, 161 insertions(+) create mode 100644 docs/agents/openclaw-gateway.mdx diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx new file mode 100644 index 00000000..999a8a7c --- /dev/null +++ b/docs/agents/openclaw-gateway.mdx @@ -0,0 +1,159 @@ +--- +title: "OpenClaw Gateway" +description: "Start the OpenClaw gateway in an E2B sandbox, connect your browser, and auto-approve device pairing." +icon: "/images/icons/openclaw.svg" +--- + +The OpenClaw [gateway](https://docs.openclaw.ai/gateway) serves a web UI for chatting with agents. When exposed over a network it requires **token auth** and **device pairing** — your browser must be approved as a trusted device before it can connect. + +This guide starts the gateway inside an E2B sandbox, prints the URL, and programmatically approves your browser. + +## Quick start + + +```typescript JavaScript & TypeScript +import { Sandbox } from 'e2b' + +const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-gateway-token' +const PORT = 18789 + +// 1. Create sandbox +const sandbox = await Sandbox.create('openclaw', { + envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY }, + timeoutMs: 3600_000, +}) + +// 2. Start the gateway with token auth +sandbox.commands.run( + `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}`, + { background: true } +) + +// 3. Wait for the gateway to start listening +for (let i = 0; i < 45; i++) { + const probe = await sandbox.commands.run( + `bash -lc 'ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting'` + ) + if (probe.stdout.trim() === 'ready') break + await new Promise((r) => setTimeout(r, 1000)) +} + +const url = `https://${sandbox.getHost(PORT)}/?token=${TOKEN}` +console.log(`Gateway: ${url}`) +``` +```python Python +import os, time +from e2b import Sandbox + +TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-gateway-token") +PORT = 18789 + +# 1. Create sandbox +sandbox = Sandbox.create("openclaw", envs={ + "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"], +}, timeout=3600) + +# 2. Start the gateway with token auth +sandbox.commands.run( + f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}", + background=True, +) + +# 3. Wait for the gateway to start listening +for _ in range(45): + probe = sandbox.commands.run( + f'bash -lc \'ss -ltn | grep -q ":{PORT} " && echo ready || echo waiting\'' + ) + if probe.stdout.strip() == "ready": + break + time.sleep(1) + +url = f"https://{sandbox.get_host(PORT)}/?token={TOKEN}" +print(f"Gateway: {url}") +``` + + +Open the URL in your browser. It will show **"Pairing required"** — that's expected. + +## Approve device pairing + +The gateway requires each browser to be approved as a paired device. Run this after opening the URL — it polls for pending requests and approves the first one. + + +```typescript JavaScript & TypeScript +// 4. Poll for the browser's pending device request and approve it +for (let i = 0; i < 30; i++) { + const res = await sandbox.commands.run( + `openclaw devices list --json --url ws://127.0.0.1:${PORT} --token ${TOKEN}` + ) + const data = JSON.parse(res.stdout) + if (data.pending?.length) { + const rid = data.pending[0].requestId + await sandbox.commands.run( + `openclaw devices approve ${rid} --token ${TOKEN} --url ws://127.0.0.1:${PORT}` + ) + console.log(`Device approved: ${rid}`) + break + } + await new Promise((r) => setTimeout(r, 2000)) +} +``` +```python Python +import json + +# 4. Poll for the browser's pending device request and approve it +for _ in range(30): + try: + res = sandbox.commands.run( + f"openclaw devices list --json --url ws://127.0.0.1:{PORT} --token {TOKEN}" + ) + data = json.loads(res.stdout) + if data.get("pending"): + rid = data["pending"][0]["requestId"] + sandbox.commands.run( + f"openclaw devices approve {rid} --token {TOKEN} --url ws://127.0.0.1:{PORT}" + ) + print(f"Device approved: {rid}") + break + except Exception: + pass + time.sleep(2) +``` + + +Once approved, the browser connects and the gateway UI loads. + +## How it works + +| Step | What happens | +|------|-------------| +| `--bind lan` | Gateway listens on `0.0.0.0` so E2B can proxy it | +| `--auth token` | Requires `?token=` on the URL for HTTP and WebSocket auth | +| Browser opens URL | Gateway serves the UI, browser opens a WebSocket | +| `code=1008 pairing required` | Gateway closes the WebSocket until the device is approved | +| `devices approve` | Approves the browser's device fingerprint | +| Browser reconnects | WebSocket connects successfully, UI is live | + +## Gateway flags reference + +| Flag | Purpose | +|------|---------| +| `--allow-unconfigured` | Start without a full config file | +| `--bind lan` | Bind to `0.0.0.0` (required for E2B port proxying) | +| `--auth token` | Enable token-based authentication | +| `--token ` | The auth token (passed as `?token=` in the URL) | +| `--port ` | Gateway listen port (default: `18789`) | + +## Related + + + + Run OpenClaw headless in a sandbox + + + Auto-pause, resume, and manage sandbox lifecycle + + + Connect to the sandbox via SSH + + \ No newline at end of file diff --git a/docs/agents/openclaw.mdx b/docs/agents/openclaw.mdx index 374a4509..131bc617 100644 --- a/docs/agents/openclaw.mdx +++ b/docs/agents/openclaw.mdx @@ -426,6 +426,8 @@ print(f"Sandbox ID: {sandbox.sandbox_id}") ``` +For a complete example with token auth, device pairing, and browser auto-approval, see [OpenClaw Gateway](/docs/agents/openclaw-gateway). + ## Build a custom template If you need to customize the environment (e.g. pre-install dependencies, add config files), build your own template on top of the pre-built `openclaw` template. From 329fa1e55ec84950e174713c165249e015de040c Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 24 Feb 2026 17:47:39 -0800 Subject: [PATCH 02/20] also comment --- docs/agents/openclaw-gateway.mdx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx index 999a8a7c..8fc85a54 100644 --- a/docs/agents/openclaw-gateway.mdx +++ b/docs/agents/openclaw-gateway.mdx @@ -40,6 +40,7 @@ for (let i = 0; i < 45; i++) { const url = `https://${sandbox.getHost(PORT)}/?token=${TOKEN}` console.log(`Gateway: ${url}`) +// Open the URL in your browser before running the next step. ``` ```python Python import os, time @@ -73,7 +74,9 @@ print(f"Gateway: {url}") ``` -Open the URL in your browser. It will show **"Pairing required"** — that's expected. + +Open the URL in your browser. It will show **"Pairing required"** — that's expected. Run the next step to approve your device. + ## Approve device pairing From d05769df1f7c82777843bab996410131041a92ba Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 24 Feb 2026 17:58:17 -0800 Subject: [PATCH 03/20] really really remind to open browser in that step --- docs/agents/openclaw-gateway.mdx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx index 8fc85a54..6c936adf 100644 --- a/docs/agents/openclaw-gateway.mdx +++ b/docs/agents/openclaw-gateway.mdx @@ -71,6 +71,7 @@ for _ in range(45): url = f"https://{sandbox.get_host(PORT)}/?token={TOKEN}" print(f"Gateway: {url}") +# Open the URL in your browser before running the next step. ``` @@ -84,6 +85,8 @@ The gateway requires each browser to be approved as a paired device. Run this af ```typescript JavaScript & TypeScript +// Make sure you've opened the gateway URL in your browser before running this. + // 4. Poll for the browser's pending device request and approve it for (let i = 0; i < 30; i++) { const res = await sandbox.commands.run( @@ -104,6 +107,8 @@ for (let i = 0; i < 30; i++) { ```python Python import json +# Make sure you've opened the gateway URL in your browser before running this. + # 4. Poll for the browser's pending device request and approve it for _ in range(30): try: From 6fbfcf80e5d9160b4e87ed80d96176e354dc1e04 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 24 Feb 2026 18:08:45 -0800 Subject: [PATCH 04/20] start on telegram - can make this easier tho --- docs/agents/openclaw-gateway.mdx | 7 +- docs/agents/openclaw-telegram.mdx | 190 ++++++++++++++++++++++++++++++ 2 files changed, 195 insertions(+), 2 deletions(-) create mode 100644 docs/agents/openclaw-telegram.mdx diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx index 6c936adf..c40f787e 100644 --- a/docs/agents/openclaw-gateway.mdx +++ b/docs/agents/openclaw-gateway.mdx @@ -154,14 +154,17 @@ Once approved, the browser connects and the gateway UI loads. ## Related - + Run OpenClaw headless in a sandbox + + Connect OpenClaw to Telegram and approve pairing + Auto-pause, resume, and manage sandbox lifecycle Connect to the sandbox via SSH - \ No newline at end of file + diff --git a/docs/agents/openclaw-telegram.mdx b/docs/agents/openclaw-telegram.mdx new file mode 100644 index 00000000..93335d6c --- /dev/null +++ b/docs/agents/openclaw-telegram.mdx @@ -0,0 +1,190 @@ +--- +title: "OpenClaw Telegram" +description: "Connect OpenClaw to Telegram in an E2B sandbox, approve pairing, and chat through your bot." +icon: "/images/icons/openclaw.svg" +--- + +OpenClaw supports Telegram as a chat channel. In E2B you can run OpenClaw in a sandbox, attach your bot token, and approve user pairing from the terminal. + +This guide covers the working flow we used: + +1. Start OpenClaw in a sandbox. +2. Add Telegram channel credentials. +3. Start the channel runtime in background. +4. Approve Telegram pairing. + +## Prerequisites + +- A Telegram bot token from [@BotFather](https://t.me/BotFather). +- An OpenAI or Anthropic provider key for the OpenClaw model. +- E2B API key configured locally. + +## Quick start + + +```typescript JavaScript & TypeScript +import { Sandbox } from 'e2b' + +const GATEWAY_PORT = 18789 +const GATEWAY_TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-openclaw-token' + +const sandbox = await Sandbox.create('openclaw', { + envs: { + OPENAI_API_KEY: process.env.OPENAI_API_KEY, + TELEGRAM_BOT_TOKEN: process.env.TELEGRAM_BOT_TOKEN, + }, + timeoutMs: 3600_000, +}) + +await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2') +await sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"') + +await sandbox.commands.run( + `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${GATEWAY_TOKEN} --port ${GATEWAY_PORT}`, + { background: true } +) + +for (let i = 0; i < 45; i++) { + const probe = await sandbox.commands.run( + `bash -lc 'ss -ltn | grep -q ":${GATEWAY_PORT} " && echo ready || echo waiting'` + ) + if (probe.stdout.trim() === 'ready') break + await new Promise((r) => setTimeout(r, 1000)) +} +``` +```python Python +import os +import time +from e2b import Sandbox + +GATEWAY_PORT = 18789 +GATEWAY_TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-openclaw-token") + +sandbox = Sandbox.create("openclaw", envs={ + "OPENAI_API_KEY": os.environ["OPENAI_API_KEY"], + "TELEGRAM_BOT_TOKEN": os.environ["TELEGRAM_BOT_TOKEN"], +}, timeout=3600) + +sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2") +sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"') + +sandbox.commands.run( + f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {GATEWAY_TOKEN} --port {GATEWAY_PORT}", + background=True, +) + +for _ in range(45): + probe = sandbox.commands.run( + f"bash -lc 'ss -ltn | grep -q \":{GATEWAY_PORT} \" && echo ready || echo waiting'" + ) + if probe.stdout.strip() == "ready": + break + time.sleep(1) +``` + + + +For Telegram setup, you do **not** need to open the gateway URL in a browser. The gateway process is used here as a long-running channel runtime. + + +## Pair your Telegram user + +1. Open your bot in Telegram and send a message (for example: `hi`). +2. Telegram will return a pairing prompt similar to: + +```text +OpenClaw: access not configured. + +Your Telegram user id: ... +Pairing code: XXXXXXXX + +Ask the bot owner to approve with: +openclaw pairing approve telegram XXXXXXXX +``` + +3. Approve that pairing code via `sandbox.commands.run(...)`: + + +```typescript JavaScript & TypeScript +const PAIRING_CODE = 'XXXXXXXX' // from Telegram + +await sandbox.commands.run( + `openclaw pairing approve --channel telegram ${PAIRING_CODE}` +) +``` +```python Python +PAIRING_CODE = "XXXXXXXX" # from Telegram + +sandbox.commands.run( + f"openclaw pairing approve --channel telegram {PAIRING_CODE}" +) +``` + + +`openclaw pairing approve telegram ` also works if you prefer that form. + +## Verify channel status + + +```typescript JavaScript & TypeScript +const channels = await sandbox.commands.run('openclaw channels list --json') +const status = await sandbox.commands.run('openclaw channels status --json --probe') +const pairing = await sandbox.commands.run('openclaw pairing list --json --channel telegram') + +console.log(JSON.parse(channels.stdout)) +console.log(JSON.parse(status.stdout)) +console.log(JSON.parse(pairing.stdout)) +``` +```python Python +import json + +channels = sandbox.commands.run("openclaw channels list --json") +status = sandbox.commands.run("openclaw channels status --json --probe") +pairing = sandbox.commands.run("openclaw pairing list --json --channel telegram") + +print(json.loads(channels.stdout)) +print(json.loads(status.stdout)) +print(json.loads(pairing.stdout)) +``` + + +If you need logs from channel handlers: + + +```typescript JavaScript & TypeScript +const logs = await sandbox.commands.run( + 'openclaw channels logs --channel telegram --lines 200' +) +console.log(logs.stdout) +``` +```python Python +logs = sandbox.commands.run( + "openclaw channels logs --channel telegram --lines 200" +) +print(logs.stdout) +``` + + +## Troubleshooting + +- `OpenClaw: access not configured` + - Pairing has not been approved yet. Run `openclaw pairing approve ...`. +- `No API key found for provider ...` + - Model/provider mismatch. If model is `openai/...`, set `OPENAI_API_KEY`. + - If model is `anthropic/...`, set `ANTHROPIC_API_KEY`. +- No pending pairing requests from `pairing list` + - Send a fresh message to the bot first, then retry `pairing list --channel telegram`. + +## Related + + + + Run OpenClaw's web gateway with token auth + + + Run OpenClaw headless in a sandbox + + + Protect exposed endpoints with tokens + + From f400c0d2276490c6da91675e3735dc5bdf5f9c0b Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 24 Feb 2026 22:23:37 -0800 Subject: [PATCH 05/20] update telegram instructions for enabling it --- docs/agents/openclaw-telegram.mdx | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/agents/openclaw-telegram.mdx b/docs/agents/openclaw-telegram.mdx index 93335d6c..e9212487 100644 --- a/docs/agents/openclaw-telegram.mdx +++ b/docs/agents/openclaw-telegram.mdx @@ -9,9 +9,10 @@ OpenClaw supports Telegram as a chat channel. In E2B you can run OpenClaw in a s This guide covers the working flow we used: 1. Start OpenClaw in a sandbox. -2. Add Telegram channel credentials. -3. Start the channel runtime in background. -4. Approve Telegram pairing. +2. Enable the Telegram plugin. +3. Add Telegram channel credentials. +4. Start the channel runtime in background. +5. Approve Telegram pairing. ## Prerequisites @@ -37,6 +38,9 @@ const sandbox = await Sandbox.create('openclaw', { }) await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2') + +// Enable the Telegram plugin (required before adding the channel) +await sandbox.commands.run('openclaw config set plugins.entries.telegram.enabled true') await sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"') await sandbox.commands.run( @@ -66,6 +70,9 @@ sandbox = Sandbox.create("openclaw", envs={ }, timeout=3600) sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2") + +# Enable the Telegram plugin (required before adding the channel) +sandbox.commands.run("openclaw config set plugins.entries.telegram.enabled true") sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"') sandbox.commands.run( @@ -167,6 +174,8 @@ print(logs.stdout) ## Troubleshooting +- `Unknown channel: telegram` + - The Telegram plugin is not enabled. Run `openclaw config set plugins.entries.telegram.enabled true` before adding the channel. - `OpenClaw: access not configured` - Pairing has not been approved yet. Run `openclaw pairing approve ...`. - `No API key found for provider ...` From a85ed2b3c7e84520bbe32ef3e8d6d07c8aaca085 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Tue, 24 Feb 2026 23:24:15 -0800 Subject: [PATCH 06/20] oauth usage --- docs/agents/openclaw-oauth.mdx | 210 +++++++++++++++++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 docs/agents/openclaw-oauth.mdx diff --git a/docs/agents/openclaw-oauth.mdx b/docs/agents/openclaw-oauth.mdx new file mode 100644 index 00000000..4d2754cb --- /dev/null +++ b/docs/agents/openclaw-oauth.mdx @@ -0,0 +1,210 @@ +--- +title: "OpenClaw Codex OAuth" +description: "Set up OpenAI Codex OAuth for OpenClaw in an E2B sandbox using the setup UI or programmatically." +icon: "/images/icons/openclaw.svg" +--- + +OpenClaw can authenticate with OpenAI Codex via OAuth instead of a plain API key. This gives access to `openai-codex/gpt-5.2-codex` and other Codex models through OpenAI's OAuth flow. + +The OAuth flow is localhost-based — OpenClaw starts an onboarding process that produces an authorization URL, you complete sign-in in your browser, then the callback is forwarded back to the sandbox. + +This guide covers two approaches: + +1. **Setup UI** — use the web UI hosted in the sandbox. +2. **Programmatic** — drive the flow entirely from SDK calls. + +## Prerequisites + +- An OpenAI account with Codex access. +- E2B API key configured locally. +- A running OpenClaw sandbox (see [OpenClaw](/docs/agents/openclaw)). + +## Setup UI flow + +If you've bootstrapped a sandbox with the setup UI (via `setup_openclaw_sandbox.py` or similar), OAuth is built in. + +1. Open the setup URL: `https:///setup?token=`. +2. Click **Connect OpenAI Codex**. +3. Wait for **OAuth URL ready** in the log panel. +4. Open that URL in your browser and complete sign-in. +5. Your browser will redirect to `http://localhost:1455/auth/callback?code=...&state=...`. Copy the full URL from the address bar. +6. Paste into the callback field and click **Submit Callback**. +7. Confirm status shows `oauth linked for openai-codex`. + + +The localhost redirect is expected — OpenClaw's Codex OAuth is localhost-based. The setup UI captures that URL and forwards it into the sandbox. + + +## Programmatic flow + +You can drive the OAuth flow without the setup UI by calling `openclaw onboard` directly and handling the callback. + + +```typescript JavaScript & TypeScript +import { Sandbox } from 'e2b' + +const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-openclaw-token' +const PORT = 18789 + +const sandbox = await Sandbox.create('openclaw', { + timeoutMs: 3600_000, +}) + +// Start the onboard flow for Codex OAuth +const onboard = await sandbox.commands.run( + 'openclaw onboard --accept-risk --flow quickstart --mode local --auth-choice openai-codex --skip-ui --skip-channels --skip-skills --skip-health', + { background: true, onStdout: (data) => process.stdout.write(data) } +) + +// The process will print an authorization URL containing "oauth/authorize". +// Open that URL in your browser, sign in, and copy the localhost callback URL. +// Then submit it: + +const CALLBACK_URL = 'http://localhost:1455/auth/callback?code=...&state=...' // from browser + +await sandbox.files.write('/tmp/openclaw_oauth_input.txt', CALLBACK_URL) + +// Wait for the onboard process to finish +await onboard.wait() + +// Verify OAuth is configured +const check = await sandbox.commands.run( + 'cat /home/user/.openclaw/agents/main/agent/auth-profiles.json' +) +console.log(check.stdout) + +// Set the default model to Codex +await sandbox.commands.run( + 'openclaw config set agents.defaults.model.primary openai-codex/gpt-5.2-codex' +) +``` +```python Python +import os +from e2b import Sandbox + +TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-openclaw-token") +PORT = 18789 + +sandbox = Sandbox.create("openclaw", timeout=3600) + +# Start the onboard flow for Codex OAuth +onboard = sandbox.commands.run( + "openclaw onboard --accept-risk --flow quickstart --mode local " + "--auth-choice openai-codex --skip-ui --skip-channels --skip-skills --skip-health", + background=True, + on_stdout=lambda data: print(data, end=""), +) + +# The process will print an authorization URL containing "oauth/authorize". +# Open that URL in your browser, sign in, and copy the localhost callback URL. +# Then submit it: + +CALLBACK_URL = "http://localhost:1455/auth/callback?code=...&state=..." # from browser + +sandbox.files.write("/tmp/openclaw_oauth_input.txt", CALLBACK_URL) + +# Wait for the onboard process to finish +onboard.wait() + +# Verify OAuth is configured +check = sandbox.commands.run( + "cat /home/user/.openclaw/agents/main/agent/auth-profiles.json" +) +print(check.stdout) + +# Set the default model to Codex +sandbox.commands.run( + "openclaw config set agents.defaults.model.primary openai-codex/gpt-5.2-codex" +) +``` + + +## Check OAuth status + +If using the setup UI, you can check status via the API: + + +```typescript JavaScript & TypeScript +const status = await sandbox.commands.run( + `curl -fsSL "http://127.0.0.1:8080/api/oauth/status?token=${TOKEN}"` +) +const data = JSON.parse(status.stdout) +console.log(data) +// { running: false, has_openai_codex_oauth: true, provider: 'openai-codex', ... } +``` +```python Python +import json + +status = sandbox.commands.run( + f'curl -fsSL "http://127.0.0.1:8080/api/oauth/status?token={TOKEN}"' +) +data = json.loads(status.stdout) +print(data) +# {"running": false, "has_openai_codex_oauth": true, "provider": "openai-codex", ...} +``` + + +Or check the auth profiles file directly: + + +```typescript JavaScript & TypeScript +const profiles = await sandbox.commands.run( + 'cat /home/user/.openclaw/agents/main/agent/auth-profiles.json' +) +console.log(profiles.stdout) +``` +```python Python +profiles = sandbox.commands.run( + "cat /home/user/.openclaw/agents/main/agent/auth-profiles.json" +) +print(profiles.stdout) +``` + + +## Reset OAuth + +To clear existing OAuth credentials and re-link: + + +```typescript JavaScript & TypeScript +// Via setup UI API +await sandbox.commands.run( + `curl -X POST "http://127.0.0.1:8080/api/oauth/reset?token=${TOKEN}"` +) + +// Then start the connect flow again from the setup UI +``` +```python Python +# Via setup UI API +sandbox.commands.run( + f'curl -X POST "http://127.0.0.1:8080/api/oauth/reset?token={TOKEN}"' +) + +# Then start the connect flow again from the setup UI +``` + + +## Troubleshooting + +- `oauth_login_not_running` + - The callback was submitted after the onboard process ended. Start a fresh connect flow. +- `oauth_state_mismatch` + - The callback URL has a `state` parameter from an older auth URL. Use the latest auth URL from the current session. +- `Failed to bind ... 1455 (EADDRINUSE)` + - OpenClaw falls back to manual paste mode — this is expected and still works. Copy the callback URL from your browser and submit it. +- Popup blocked in browser + - Copy the auth URL from the log panel and paste it into a new tab manually. + +## Related + + + + Run OpenClaw's web gateway with token auth + + + Connect OpenClaw to Telegram and approve pairing + + + Run OpenClaw headless in a sandbox + + From 9192375b092997c5fee0df34505ba3aaeb96a8c0 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Wed, 25 Feb 2026 00:09:26 -0800 Subject: [PATCH 07/20] general env explanation (not sure need this) --- docs/agents/openclaw-environment.mdx | 300 +++++++++++++++++++++++++++ docs/agents/openclaw-gateway.mdx | 5 +- docs/agents/openclaw-telegram.mdx | 5 +- 3 files changed, 308 insertions(+), 2 deletions(-) create mode 100644 docs/agents/openclaw-environment.mdx diff --git a/docs/agents/openclaw-environment.mdx b/docs/agents/openclaw-environment.mdx new file mode 100644 index 00000000..c67be559 --- /dev/null +++ b/docs/agents/openclaw-environment.mdx @@ -0,0 +1,300 @@ +--- +title: "OpenClaw Environment" +description: "Change models, rotate tokens, update channels, and reset OpenClaw state in a running E2B sandbox." +icon: "/images/icons/openclaw.svg" +--- + +Use this guide for **post-deploy changes** to a running OpenClaw sandbox. + +You do not need to tear down the sandbox for most updates. + +## What you can change without rebuilding + +| Setting | Rebuild required? | Notes | +|---|---|---| +| Default model (`agents.defaults.model.primary`) | No | Apply immediately for new runs | +| Gateway token (`--token`) | No | Restart gateway process with new token | +| Telegram bot token | No | Re-run `channels add`, then restart gateway | +| OpenAI / Anthropic API keys | No | Update runtime env used by OpenClaw processes | +| OpenAI Codex OAuth profile | No | Clear auth profile, then relink | + +## Connect to an existing sandbox + + +```typescript JavaScript & TypeScript +import { Sandbox } from 'e2b' + +const sandbox = await Sandbox.connect(process.env.SANDBOX_ID!) +``` +```python Python +import os +from e2b import Sandbox + +sandbox = Sandbox.connect(os.environ["SANDBOX_ID"]) +``` + + +## 1) Change default model + + +```typescript JavaScript & TypeScript +await sandbox.commands.run( + 'openclaw config set agents.defaults.model.primary openai/gpt-5.2' +) + +const models = await sandbox.commands.run('openclaw models status --json') +console.log(JSON.parse(models.stdout)) +``` +```python Python +import json + +sandbox.commands.run( + "openclaw config set agents.defaults.model.primary openai/gpt-5.2" +) + +models = sandbox.commands.run("openclaw models status --json") +print(json.loads(models.stdout)) +``` + + +## 2) Rotate gateway token (no sandbox restart) + +Use a new token and restart only the gateway process. + + +```typescript JavaScript & TypeScript +const NEW_TOKEN = 'replace-me' +const PORT = 18789 + +await sandbox.commands.run(`bash -lc ' + set -euo pipefail + pkill -f "openclaw gateway" >/dev/null 2>&1 || true + nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token "${NEW_TOKEN}" --port "${PORT}" \ + > /home/user/openclaw_gateway.log 2>&1 & + sleep 1 + ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting +'`) +``` +```python Python +NEW_TOKEN = "replace-me" +PORT = 18789 + +sandbox.commands.run( + f"""bash -lc ' +set -euo pipefail +pkill -f "openclaw gateway" >/dev/null 2>&1 || true +nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token "{NEW_TOKEN}" --port "{PORT}" \\ + > /home/user/openclaw_gateway.log 2>&1 & +sleep 1 +ss -ltn | grep -q ":{PORT} " && echo ready || echo waiting +'""" +) +``` + + +After rotation, old URLs with the previous `?token=` stop working. + +## 3) Update provider keys after deploy + +For one-off runs: + + +```typescript JavaScript & TypeScript +const OPENAI_API_KEY = 'sk-...' + +await sandbox.commands.run( + `OPENAI_API_KEY="${OPENAI_API_KEY}" openclaw agent --local --message "hello"` +) +``` +```python Python +OPENAI_API_KEY = "sk-..." + +sandbox.commands.run( + f'OPENAI_API_KEY="{OPENAI_API_KEY}" openclaw agent --local --message "hello"' +) +``` + + +For persistent runtime, write an env file and source it before launching gateway/agent processes: + + +```typescript JavaScript & TypeScript +const envFile = `export OPENAI_API_KEY="sk-..."\nexport ANTHROPIC_API_KEY="sk-ant-..."\n` +await sandbox.files.write('/home/user/.openclaw-runtime.env', envFile) +await sandbox.commands.run('chmod 600 /home/user/.openclaw-runtime.env') + +await sandbox.commands.run(`bash -lc ' + pkill -f "openclaw gateway" >/dev/null 2>&1 || true + nohup bash -lc "source /home/user/.openclaw-runtime.env; openclaw gateway --allow-unconfigured --bind lan --auth token --token my-token --port 18789" \ + > /home/user/openclaw_gateway.log 2>&1 & +'`) +``` +```python Python +env_file = """export OPENAI_API_KEY=\"sk-...\" +export ANTHROPIC_API_KEY=\"sk-ant-...\" +""" +sandbox.files.write('/home/user/.openclaw-runtime.env', env_file) +sandbox.commands.run('chmod 600 /home/user/.openclaw-runtime.env') + +sandbox.commands.run( + """bash -lc ' +pkill -f "openclaw gateway" >/dev/null 2>&1 || true +nohup bash -lc "source /home/user/.openclaw-runtime.env; openclaw gateway --allow-unconfigured --bind lan --auth token --token my-token --port 18789" \\ + > /home/user/openclaw_gateway.log 2>&1 & +'""" +) +``` + + +## 4) Update Telegram bot token + + +```typescript JavaScript & TypeScript +const NEW_TELEGRAM_BOT_TOKEN = '123456:abc' + +await sandbox.commands.run( + `openclaw channels add --channel telegram --token "${NEW_TELEGRAM_BOT_TOKEN}"` +) + +await sandbox.commands.run(`bash -lc ' + pkill -f "openclaw gateway" >/dev/null 2>&1 || true + nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token my-token --port 18789 \ + > /home/user/openclaw_gateway.log 2>&1 & +'`) +``` +```python Python +NEW_TELEGRAM_BOT_TOKEN = "123456:abc" + +sandbox.commands.run( + f'openclaw channels add --channel telegram --token "{NEW_TELEGRAM_BOT_TOKEN}"' +) + +sandbox.commands.run( + """bash -lc ' +pkill -f "openclaw gateway" >/dev/null 2>&1 || true +nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token my-token --port 18789 \\ + > /home/user/openclaw_gateway.log 2>&1 & +'""" +) +``` + + +## 5) Reset Telegram channel + pairing state + + +```typescript JavaScript & TypeScript +await sandbox.commands.run('openclaw channels remove --channel telegram --delete') +await sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"') + +const pending = await sandbox.commands.run('openclaw pairing list --json --channel telegram') +console.log(pending.stdout) + +await sandbox.commands.run('openclaw pairing approve --channel telegram ') +``` +```python Python +sandbox.commands.run("openclaw channels remove --channel telegram --delete") +sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"') + +pending = sandbox.commands.run("openclaw pairing list --json --channel telegram") +print(pending.stdout) + +sandbox.commands.run("openclaw pairing approve --channel telegram ") +``` + + +## 6) Reset OpenAI Codex OAuth profile + + +```typescript JavaScript & TypeScript +await sandbox.commands.run(`python3 - <<'PY' +import json +from pathlib import Path + +p = Path('/home/user/.openclaw/agents/main/agent/auth-profiles.json') +raw = json.loads(p.read_text()) if p.exists() else {} +profiles = raw.get('profiles', {}) if isinstance(raw, dict) else {} +raw['profiles'] = { + k: v for k, v in profiles.items() + if not (isinstance(v, dict) and v.get('provider') == 'openai-codex') +} +p.parent.mkdir(parents=True, exist_ok=True) +p.write_text(json.dumps(raw, indent=2) + '\n') +print('cleared openai-codex oauth profiles') +PY`) +``` +```python Python +sandbox.commands.run("""python3 - <<'PY' +import json +from pathlib import Path + +p = Path('/home/user/.openclaw/agents/main/agent/auth-profiles.json') +raw = json.loads(p.read_text()) if p.exists() else {} +profiles = raw.get('profiles', {}) if isinstance(raw, dict) else {} +raw['profiles'] = { + k: v for k, v in profiles.items() + if not (isinstance(v, dict) and v.get('provider') == 'openai-codex') +} +p.parent.mkdir(parents=True, exist_ok=True) +p.write_text(json.dumps(raw, indent=2) + '\n') +print('cleared openai-codex oauth profiles') +PY""") +``` + + +Then relink OAuth using your setup flow. + +## 7) Verify current state + + +```typescript JavaScript & TypeScript +const checks = [ + 'openclaw channels list --json', + 'openclaw channels status --json --probe', + 'openclaw pairing list --json --channel telegram', + 'openclaw models status --json', +] + +for (const cmd of checks) { + const res = await sandbox.commands.run(cmd) + console.log(`\n$ ${cmd}\n${res.stdout}`) +} +``` +```python Python +checks = [ + "openclaw channels list --json", + "openclaw channels status --json --probe", + "openclaw pairing list --json --channel telegram", + "openclaw models status --json", +] + +for cmd in checks: + res = sandbox.commands.run(cmd) + print(f"\n$ {cmd}\n{res.stdout}") +``` + + +## Troubleshooting + +- `Unknown channel: telegram` + - Telegram channel is not configured in this sandbox runtime yet. + - Run `openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"` first. + - Then retry `openclaw pairing list --json --channel telegram`. +- `No API key found for provider ...` + - The selected model and available key do not match. + - `openai/...` needs `OPENAI_API_KEY`; `anthropic/...` needs `ANTHROPIC_API_KEY`. +- Pairing list is empty + - Send a fresh message to the bot in Telegram first. + +## Related + + + + Run OpenClaw headless in a sandbox + + + Run OpenClaw's web/channel runtime + + + Connect and pair Telegram users + + diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx index c40f787e..8b185fa4 100644 --- a/docs/agents/openclaw-gateway.mdx +++ b/docs/agents/openclaw-gateway.mdx @@ -154,13 +154,16 @@ Once approved, the browser connects and the gateway UI loads. ## Related - + Run OpenClaw headless in a sandbox Connect OpenClaw to Telegram and approve pairing + + Change models, tokens, and channels after deploy + Auto-pause, resume, and manage sandbox lifecycle diff --git a/docs/agents/openclaw-telegram.mdx b/docs/agents/openclaw-telegram.mdx index e9212487..a3212c5e 100644 --- a/docs/agents/openclaw-telegram.mdx +++ b/docs/agents/openclaw-telegram.mdx @@ -186,10 +186,13 @@ print(logs.stdout) ## Related - + Run OpenClaw's web gateway with token auth + + Change models, tokens, and channel settings after deploy + Run OpenClaw headless in a sandbox From 14a122c29f063acd0dce89493e35c48029166836 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Wed, 25 Feb 2026 00:13:25 -0800 Subject: [PATCH 08/20] reorder the docs for openclaw --- docs.json | 11 +++++++++++ docs/agents/openclaw-environment.mdx | 2 +- docs/agents/openclaw-gateway.mdx | 4 ++-- docs/agents/openclaw-oauth.mdx | 2 +- docs/agents/openclaw-telegram.mdx | 2 +- docs/agents/openclaw.mdx | 4 ++-- 6 files changed, 18 insertions(+), 7 deletions(-) diff --git a/docs.json b/docs.json index 1a813472..78a7ae9b 100644 --- a/docs.json +++ b/docs.json @@ -56,6 +56,17 @@ "docs/agents/amp", "docs/agents/claude-code", "docs/agents/codex", + { + "group": "OpenClaw", + "icon": "/images/icons/openclaw.svg", + "pages": [ + "docs/agents/openclaw-gateway", + "docs/agents/openclaw-telegram", + "docs/agents/openclaw-oauth", + "docs/agents/openclaw-environment", + "docs/agents/openclaw" + ] + }, "docs/agents/opencode" ] }, diff --git a/docs/agents/openclaw-environment.mdx b/docs/agents/openclaw-environment.mdx index c67be559..1c5f77ae 100644 --- a/docs/agents/openclaw-environment.mdx +++ b/docs/agents/openclaw-environment.mdx @@ -1,7 +1,7 @@ --- title: "OpenClaw Environment" description: "Change models, rotate tokens, update channels, and reset OpenClaw state in a running E2B sandbox." -icon: "/images/icons/openclaw.svg" +icon: "sliders" --- Use this guide for **post-deploy changes** to a running OpenClaw sandbox. diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx index 8b185fa4..837ba728 100644 --- a/docs/agents/openclaw-gateway.mdx +++ b/docs/agents/openclaw-gateway.mdx @@ -1,7 +1,7 @@ --- -title: "OpenClaw Gateway" +title: "Deploy OpenClaw" description: "Start the OpenClaw gateway in an E2B sandbox, connect your browser, and auto-approve device pairing." -icon: "/images/icons/openclaw.svg" +icon: "globe" --- The OpenClaw [gateway](https://docs.openclaw.ai/gateway) serves a web UI for chatting with agents. When exposed over a network it requires **token auth** and **device pairing** — your browser must be approved as a trusted device before it can connect. diff --git a/docs/agents/openclaw-oauth.mdx b/docs/agents/openclaw-oauth.mdx index 4d2754cb..69a45aed 100644 --- a/docs/agents/openclaw-oauth.mdx +++ b/docs/agents/openclaw-oauth.mdx @@ -1,7 +1,7 @@ --- title: "OpenClaw Codex OAuth" description: "Set up OpenAI Codex OAuth for OpenClaw in an E2B sandbox using the setup UI or programmatically." -icon: "/images/icons/openclaw.svg" +icon: "key" --- OpenClaw can authenticate with OpenAI Codex via OAuth instead of a plain API key. This gives access to `openai-codex/gpt-5.2-codex` and other Codex models through OpenAI's OAuth flow. diff --git a/docs/agents/openclaw-telegram.mdx b/docs/agents/openclaw-telegram.mdx index a3212c5e..d2aed490 100644 --- a/docs/agents/openclaw-telegram.mdx +++ b/docs/agents/openclaw-telegram.mdx @@ -1,7 +1,7 @@ --- title: "OpenClaw Telegram" description: "Connect OpenClaw to Telegram in an E2B sandbox, approve pairing, and chat through your bot." -icon: "/images/icons/openclaw.svg" +icon: "paper-plane" --- OpenClaw supports Telegram as a chat channel. In E2B you can run OpenClaw in a sandbox, attach your bot token, and approve user pairing from the terminal. diff --git a/docs/agents/openclaw.mdx b/docs/agents/openclaw.mdx index 131bc617..143f8b53 100644 --- a/docs/agents/openclaw.mdx +++ b/docs/agents/openclaw.mdx @@ -1,7 +1,7 @@ --- -title: "OpenClaw" +title: "Customize OpenClaw" description: "Run OpenClaw in a secure E2B sandbox with full filesystem, terminal, and git access." -icon: "/images/icons/openclaw.svg" +icon: "wand-magic-sparkles" --- [OpenClaw](https://openclaw.ai) is an open-source personal AI assistant that supports multiple LLM providers and messaging channels. E2B provides a pre-built `openclaw` template with OpenClaw already installed. From 60814455609fc256ded6ca84711748d8a7714236 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Wed, 25 Feb 2026 15:26:43 -0800 Subject: [PATCH 09/20] oauth updated --- docs/agents/openclaw-environment.mdx | 264 ++++++------------------- docs/agents/openclaw-oauth.mdx | 276 ++++++++++++--------------- 2 files changed, 187 insertions(+), 353 deletions(-) diff --git a/docs/agents/openclaw-environment.mdx b/docs/agents/openclaw-environment.mdx index 1c5f77ae..ec1ef63a 100644 --- a/docs/agents/openclaw-environment.mdx +++ b/docs/agents/openclaw-environment.mdx @@ -8,222 +8,99 @@ Use this guide for **post-deploy changes** to a running OpenClaw sandbox. You do not need to tear down the sandbox for most updates. +## Connect to sandbox terminal + +```bash +e2b sbx connect +``` + +All commands below are meant to run inside that terminal session. + ## What you can change without rebuilding | Setting | Rebuild required? | Notes | |---|---|---| -| Default model (`agents.defaults.model.primary`) | No | Apply immediately for new runs | +| Default model (`agents.defaults.model.primary`) | No | Applies to new runs | | Gateway token (`--token`) | No | Restart gateway process with new token | | Telegram bot token | No | Re-run `channels add`, then restart gateway | | OpenAI / Anthropic API keys | No | Update runtime env used by OpenClaw processes | | OpenAI Codex OAuth profile | No | Clear auth profile, then relink | -## Connect to an existing sandbox - - -```typescript JavaScript & TypeScript -import { Sandbox } from 'e2b' - -const sandbox = await Sandbox.connect(process.env.SANDBOX_ID!) -``` -```python Python -import os -from e2b import Sandbox - -sandbox = Sandbox.connect(os.environ["SANDBOX_ID"]) -``` - - ## 1) Change default model - -```typescript JavaScript & TypeScript -await sandbox.commands.run( - 'openclaw config set agents.defaults.model.primary openai/gpt-5.2' -) - -const models = await sandbox.commands.run('openclaw models status --json') -console.log(JSON.parse(models.stdout)) +```bash +openclaw config set agents.defaults.model.primary openai/gpt-5.2 +openclaw models status --json ``` -```python Python -import json - -sandbox.commands.run( - "openclaw config set agents.defaults.model.primary openai/gpt-5.2" -) - -models = sandbox.commands.run("openclaw models status --json") -print(json.loads(models.stdout)) -``` - ## 2) Rotate gateway token (no sandbox restart) -Use a new token and restart only the gateway process. - - -```typescript JavaScript & TypeScript -const NEW_TOKEN = 'replace-me' -const PORT = 18789 - -await sandbox.commands.run(`bash -lc ' - set -euo pipefail - pkill -f "openclaw gateway" >/dev/null 2>&1 || true - nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token "${NEW_TOKEN}" --port "${PORT}" \ - > /home/user/openclaw_gateway.log 2>&1 & - sleep 1 - ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting -'`) -``` -```python Python -NEW_TOKEN = "replace-me" -PORT = 18789 - -sandbox.commands.run( - f"""bash -lc ' -set -euo pipefail -pkill -f "openclaw gateway" >/dev/null 2>&1 || true -nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token "{NEW_TOKEN}" --port "{PORT}" \\ +```bash +NEW_TOKEN="replace-me" +PORT=18789 + +pkill -f 'openclaw gateway' >/dev/null 2>&1 || true +nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token "$NEW_TOKEN" --port "$PORT" \ > /home/user/openclaw_gateway.log 2>&1 & + sleep 1 -ss -ltn | grep -q ":{PORT} " && echo ready || echo waiting -'""" -) +ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting ``` - After rotation, old URLs with the previous `?token=` stop working. ## 3) Update provider keys after deploy -For one-off runs: +### One-off use - -```typescript JavaScript & TypeScript -const OPENAI_API_KEY = 'sk-...' - -await sandbox.commands.run( - `OPENAI_API_KEY="${OPENAI_API_KEY}" openclaw agent --local --message "hello"` -) -``` -```python Python -OPENAI_API_KEY = "sk-..." - -sandbox.commands.run( - f'OPENAI_API_KEY="{OPENAI_API_KEY}" openclaw agent --local --message "hello"' -) +```bash +OPENAI_API_KEY="sk-..." openclaw agent --local --message "hello" ``` - -For persistent runtime, write an env file and source it before launching gateway/agent processes: +### Persistent runtime env - -```typescript JavaScript & TypeScript -const envFile = `export OPENAI_API_KEY="sk-..."\nexport ANTHROPIC_API_KEY="sk-ant-..."\n` -await sandbox.files.write('/home/user/.openclaw-runtime.env', envFile) -await sandbox.commands.run('chmod 600 /home/user/.openclaw-runtime.env') +```bash +cat > /home/user/.openclaw-runtime.env <<'ENV' +export OPENAI_API_KEY="sk-..." +export ANTHROPIC_API_KEY="sk-ant-..." +ENV +chmod 600 /home/user/.openclaw-runtime.env -await sandbox.commands.run(`bash -lc ' - pkill -f "openclaw gateway" >/dev/null 2>&1 || true - nohup bash -lc "source /home/user/.openclaw-runtime.env; openclaw gateway --allow-unconfigured --bind lan --auth token --token my-token --port 18789" \ - > /home/user/openclaw_gateway.log 2>&1 & -'`) -``` -```python Python -env_file = """export OPENAI_API_KEY=\"sk-...\" -export ANTHROPIC_API_KEY=\"sk-ant-...\" -""" -sandbox.files.write('/home/user/.openclaw-runtime.env', env_file) -sandbox.commands.run('chmod 600 /home/user/.openclaw-runtime.env') - -sandbox.commands.run( - """bash -lc ' -pkill -f "openclaw gateway" >/dev/null 2>&1 || true -nohup bash -lc "source /home/user/.openclaw-runtime.env; openclaw gateway --allow-unconfigured --bind lan --auth token --token my-token --port 18789" \\ +pkill -f 'openclaw gateway' >/dev/null 2>&1 || true +nohup bash -lc 'source /home/user/.openclaw-runtime.env; openclaw gateway --allow-unconfigured --bind lan --auth token --token "my-token" --port 18789' \ > /home/user/openclaw_gateway.log 2>&1 & -'""" -) ``` - ## 4) Update Telegram bot token - -```typescript JavaScript & TypeScript -const NEW_TELEGRAM_BOT_TOKEN = '123456:abc' +```bash +NEW_TELEGRAM_BOT_TOKEN="123456:abc" -await sandbox.commands.run( - `openclaw channels add --channel telegram --token "${NEW_TELEGRAM_BOT_TOKEN}"` -) +openclaw channels add --channel telegram --token "$NEW_TELEGRAM_BOT_TOKEN" -await sandbox.commands.run(`bash -lc ' - pkill -f "openclaw gateway" >/dev/null 2>&1 || true - nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token my-token --port 18789 \ - > /home/user/openclaw_gateway.log 2>&1 & -'`) -``` -```python Python -NEW_TELEGRAM_BOT_TOKEN = "123456:abc" - -sandbox.commands.run( - f'openclaw channels add --channel telegram --token "{NEW_TELEGRAM_BOT_TOKEN}"' -) - -sandbox.commands.run( - """bash -lc ' -pkill -f "openclaw gateway" >/dev/null 2>&1 || true -nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token my-token --port 18789 \\ +pkill -f 'openclaw gateway' >/dev/null 2>&1 || true +nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token "my-token" --port 18789 \ > /home/user/openclaw_gateway.log 2>&1 & -'""" -) ``` - ## 5) Reset Telegram channel + pairing state - -```typescript JavaScript & TypeScript -await sandbox.commands.run('openclaw channels remove --channel telegram --delete') -await sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"') - -const pending = await sandbox.commands.run('openclaw pairing list --json --channel telegram') -console.log(pending.stdout) - -await sandbox.commands.run('openclaw pairing approve --channel telegram ') +```bash +openclaw channels remove --channel telegram --delete +openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN" +openclaw pairing list --json --channel telegram ``` -```python Python -sandbox.commands.run("openclaw channels remove --channel telegram --delete") -sandbox.commands.run('openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"') -pending = sandbox.commands.run("openclaw pairing list --json --channel telegram") -print(pending.stdout) +Approve a pending code: -sandbox.commands.run("openclaw pairing approve --channel telegram ") +```bash +openclaw pairing approve --channel telegram ``` - ## 6) Reset OpenAI Codex OAuth profile - -```typescript JavaScript & TypeScript -await sandbox.commands.run(`python3 - <<'PY' -import json -from pathlib import Path - -p = Path('/home/user/.openclaw/agents/main/agent/auth-profiles.json') -raw = json.loads(p.read_text()) if p.exists() else {} -profiles = raw.get('profiles', {}) if isinstance(raw, dict) else {} -raw['profiles'] = { - k: v for k, v in profiles.items() - if not (isinstance(v, dict) and v.get('provider') == 'openai-codex') -} -p.parent.mkdir(parents=True, exist_ok=True) -p.write_text(json.dumps(raw, indent=2) + '\n') -print('cleared openai-codex oauth profiles') -PY`) -``` -```python Python -sandbox.commands.run("""python3 - <<'PY' +```bash +python3 - <<'PY' import json from pathlib import Path @@ -237,53 +114,34 @@ raw['profiles'] = { p.parent.mkdir(parents=True, exist_ok=True) p.write_text(json.dumps(raw, indent=2) + '\n') print('cleared openai-codex oauth profiles') -PY""") +PY ``` - -Then relink OAuth using your setup flow. +Then relink OAuth using the OAuth guide. ## 7) Verify current state - -```typescript JavaScript & TypeScript -const checks = [ - 'openclaw channels list --json', - 'openclaw channels status --json --probe', - 'openclaw pairing list --json --channel telegram', - 'openclaw models status --json', -] - -for (const cmd of checks) { - const res = await sandbox.commands.run(cmd) - console.log(`\n$ ${cmd}\n${res.stdout}`) -} -``` -```python Python -checks = [ - "openclaw channels list --json", - "openclaw channels status --json --probe", - "openclaw pairing list --json --channel telegram", - "openclaw models status --json", -] - -for cmd in checks: - res = sandbox.commands.run(cmd) - print(f"\n$ {cmd}\n{res.stdout}") +```bash +openclaw channels list --json +openclaw channels status --json --probe +openclaw pairing list --json --channel telegram +openclaw models status --json ``` - ## Troubleshooting - `Unknown channel: telegram` - - Telegram channel is not configured in this sandbox runtime yet. - - Run `openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"` first. - - Then retry `openclaw pairing list --json --channel telegram`. + - Telegram channel is not configured yet. + - Run `openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"`, then retry. - `No API key found for provider ...` - - The selected model and available key do not match. + - Model/provider mismatch. - `openai/...` needs `OPENAI_API_KEY`; `anthropic/...` needs `ANTHROPIC_API_KEY`. - Pairing list is empty - - Send a fresh message to the bot in Telegram first. + - Send a fresh message to the bot first. + +## Using SDK instead of terminal + +If you prefer SDK automation, execute the same shell commands with `sandbox.commands.run(...)`. ## Related diff --git a/docs/agents/openclaw-oauth.mdx b/docs/agents/openclaw-oauth.mdx index 69a45aed..907af847 100644 --- a/docs/agents/openclaw-oauth.mdx +++ b/docs/agents/openclaw-oauth.mdx @@ -1,210 +1,186 @@ --- title: "OpenClaw Codex OAuth" -description: "Set up OpenAI Codex OAuth for OpenClaw in an E2B sandbox using the setup UI or programmatically." +description: "Set up OpenAI Codex OAuth for OpenClaw using interactive PTY in an E2B sandbox." icon: "key" --- -OpenClaw can authenticate with OpenAI Codex via OAuth instead of a plain API key. This gives access to `openai-codex/gpt-5.2-codex` and other Codex models through OpenAI's OAuth flow. +OpenClaw can authenticate with OpenAI Codex via OAuth instead of an API key. -The OAuth flow is localhost-based — OpenClaw starts an onboarding process that produces an authorization URL, you complete sign-in in your browser, then the callback is forwarded back to the sandbox. - -This guide covers two approaches: - -1. **Setup UI** — use the web UI hosted in the sandbox. -2. **Programmatic** — drive the flow entirely from SDK calls. +This page uses a PTY-first flow, which works on raw OpenClaw sandboxes even when setup API endpoints are unavailable. ## Prerequisites -- An OpenAI account with Codex access. -- E2B API key configured locally. -- A running OpenClaw sandbox (see [OpenClaw](/docs/agents/openclaw)). - -## Setup UI flow +- Local Python app or notebook with `e2b` SDK installed. +- `E2B_API_KEY` in your local environment. +- OpenAI account with Codex access. -If you've bootstrapped a sandbox with the setup UI (via `setup_openclaw_sandbox.py` or similar), OAuth is built in. +## End-to-end PTY flow -1. Open the setup URL: `https:///setup?token=`. -2. Click **Connect OpenAI Codex**. -3. Wait for **OAuth URL ready** in the log panel. -4. Open that URL in your browser and complete sign-in. -5. Your browser will redirect to `http://localhost:1455/auth/callback?code=...&state=...`. Copy the full URL from the address bar. -6. Paste into the callback field and click **Submit Callback**. -7. Confirm status shows `oauth linked for openai-codex`. +### 1) Connect to your sandbox - -The localhost redirect is expected — OpenClaw's Codex OAuth is localhost-based. The setup UI captures that URL and forwards it into the sandbox. - - -## Programmatic flow +```python +import re +import threading +import time +from e2b import Sandbox +from e2b.sandbox.commands.command_handle import CommandExitException, PtySize -You can drive the OAuth flow without the setup UI by calling `openclaw onboard` directly and handling the callback. +sandbox = Sandbox.connect("", timeout=60 * 30) +``` - -```typescript JavaScript & TypeScript -import { Sandbox } from 'e2b' +### 2) Start onboarding in PTY -const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-openclaw-token' -const PORT = 18789 +```python +pty_chunks = [] -const sandbox = await Sandbox.create('openclaw', { - timeoutMs: 3600_000, -}) +def on_pty(data: bytes): + text = data.decode("utf-8", errors="replace") + print(text, end="") + pty_chunks.append(text) -// Start the onboard flow for Codex OAuth -const onboard = await sandbox.commands.run( - 'openclaw onboard --accept-risk --flow quickstart --mode local --auth-choice openai-codex --skip-ui --skip-channels --skip-skills --skip-health', - { background: true, onStdout: (data) => process.stdout.write(data) } +terminal = sandbox.pty.create( + PtySize(cols=140, rows=40), + timeout=0, # keep PTY open while you complete browser login ) -// The process will print an authorization URL containing "oauth/authorize". -// Open that URL in your browser, sign in, and copy the localhost callback URL. -// Then submit it: - -const CALLBACK_URL = 'http://localhost:1455/auth/callback?code=...&state=...' // from browser - -await sandbox.files.write('/tmp/openclaw_oauth_input.txt', CALLBACK_URL) - -// Wait for the onboard process to finish -await onboard.wait() - -// Verify OAuth is configured -const check = await sandbox.commands.run( - 'cat /home/user/.openclaw/agents/main/agent/auth-profiles.json' +thread = threading.Thread( + target=lambda: terminal.wait(on_pty=on_pty), + daemon=True, ) -console.log(check.stdout) +thread.start() + +def pty_send_line(text: str): + # For OpenClaw TUI prompts, carriage return is more reliable than newline. + sandbox.pty.send_stdin(terminal.pid, (text + "\r").encode()) -// Set the default model to Codex -await sandbox.commands.run( - 'openclaw config set agents.defaults.model.primary openai-codex/gpt-5.2-codex' +onboard_cmd = ( + "openclaw onboard --accept-risk --flow quickstart --mode local " + "--auth-choice openai-codex --skip-ui --skip-channels --skip-skills --skip-health" ) +pty_send_line(onboard_cmd) ``` -```python Python -import os -from e2b import Sandbox -TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-openclaw-token") -PORT = 18789 +### 3) Extract auth URL and open it in your local browser -sandbox = Sandbox.create("openclaw", timeout=3600) +```python +ansi_re = re.compile(r"\x1b\[[0-9;?]*[ -/]*[@-~]") +oauth_re = re.compile(r"https://auth\.openai\.com/oauth/authorize[^\s\"']+") -# Start the onboard flow for Codex OAuth -onboard = sandbox.commands.run( - "openclaw onboard --accept-risk --flow quickstart --mode local " - "--auth-choice openai-codex --skip-ui --skip-channels --skip-skills --skip-health", - background=True, - on_stdout=lambda data: print(data, end=""), -) +auth_url = None +for _ in range(60): + cleaned = ansi_re.sub("", "".join(pty_chunks).replace("\r", "\n")) + match = oauth_re.search(cleaned) + if match: + auth_url = match.group(0) + break + time.sleep(1) -# The process will print an authorization URL containing "oauth/authorize". -# Open that URL in your browser, sign in, and copy the localhost callback URL. -# Then submit it: +if not auth_url: + raise RuntimeError("OAuth URL not found in PTY output") -CALLBACK_URL = "http://localhost:1455/auth/callback?code=...&state=..." # from browser +print("Open this URL in your LOCAL browser:\n") +print(auth_url) +``` -sandbox.files.write("/tmp/openclaw_oauth_input.txt", CALLBACK_URL) +After browser sign-in, copy the localhost callback URL: -# Wait for the onboard process to finish -onboard.wait() +`http://localhost:1455/auth/callback?code=...&state=...` -# Verify OAuth is configured -check = sandbox.commands.run( - "cat /home/user/.openclaw/agents/main/agent/auth-profiles.json" -) -print(check.stdout) +### 4) Submit callback back into PTY -# Set the default model to Codex -sandbox.commands.run( - "openclaw config set agents.defaults.model.primary openai-codex/gpt-5.2-codex" -) -``` - - -## Check OAuth status +```python +callback_url = input("\nPaste localhost callback URL: ").strip() +pty_send_line(callback_url) -If using the setup UI, you can check status via the API: +# Wait briefly so OpenClaw can process the callback, then exit PTY shell. +time.sleep(2) +pty_send_line("exit") - -```typescript JavaScript & TypeScript -const status = await sandbox.commands.run( - `curl -fsSL "http://127.0.0.1:8080/api/oauth/status?token=${TOKEN}"` -) -const data = JSON.parse(status.stdout) -console.log(data) -// { running: false, has_openai_codex_oauth: true, provider: 'openai-codex', ... } +try: + thread.join(timeout=30) +except CommandExitException: + pass ``` -```python Python + +### 5) Verify OAuth link and run a smoke command + +```python +auth_check = sandbox.commands.run( + """python3 - <<'PY' import json +from pathlib import Path +p = Path('/home/user/.openclaw/agents/main/agent/auth-profiles.json') +raw = json.loads(p.read_text()) if p.exists() else {} +profiles = raw.get('profiles', {}) if isinstance(raw, dict) else {} +ok = any( + isinstance(v, dict) and v.get('provider') == 'openai-codex' and v.get('refresh') + for v in profiles.values() +) +print('openai_codex_oauth_linked=' + ('true' if ok else 'false')) +PY""" +) +print(auth_check.stdout) -status = sandbox.commands.run( - f'curl -fsSL "http://127.0.0.1:8080/api/oauth/status?token={TOKEN}"' +smoke = sandbox.commands.run( + 'openclaw agent --session-id main --thinking low --message "Say exactly: oauth-ok"' ) -data = json.loads(status.stdout) -print(data) -# {"running": false, "has_openai_codex_oauth": true, "provider": "openai-codex", ...} +print(smoke.stdout) ``` - -Or check the auth profiles file directly: +## Reset and relink OAuth - -```typescript JavaScript & TypeScript -const profiles = await sandbox.commands.run( - 'cat /home/user/.openclaw/agents/main/agent/auth-profiles.json' -) -console.log(profiles.stdout) -``` -```python Python -profiles = sandbox.commands.run( - "cat /home/user/.openclaw/agents/main/agent/auth-profiles.json" +```python +reset = sandbox.commands.run( + """python3 - <<'PY' +import json +from pathlib import Path + +p = Path('/home/user/.openclaw/agents/main/agent/auth-profiles.json') +raw = json.loads(p.read_text()) if p.exists() else {} +profiles = raw.get('profiles', {}) if isinstance(raw, dict) else {} +raw['profiles'] = { + k: v for k, v in profiles.items() + if not (isinstance(v, dict) and v.get('provider') == 'openai-codex') +} +p.parent.mkdir(parents=True, exist_ok=True) +p.write_text(json.dumps(raw, indent=2) + '\\n') +print('cleared openai-codex oauth profiles') +PY""" ) -print(profiles.stdout) +print(reset.stdout) ``` - - -## Reset OAuth -To clear existing OAuth credentials and re-link: - - -```typescript JavaScript & TypeScript -// Via setup UI API -await sandbox.commands.run( - `curl -X POST "http://127.0.0.1:8080/api/oauth/reset?token=${TOKEN}"` -) +Then rerun steps 2-5. -// Then start the connect flow again from the setup UI -``` -```python Python -# Via setup UI API -sandbox.commands.run( - f'curl -X POST "http://127.0.0.1:8080/api/oauth/reset?token={TOKEN}"' -) +## Cleanup -# Then start the connect flow again from the setup UI +```python +sandbox.kill() ``` - ## Troubleshooting -- `oauth_login_not_running` - - The callback was submitted after the onboard process ended. Start a fresh connect flow. -- `oauth_state_mismatch` - - The callback URL has a `state` parameter from an older auth URL. Use the latest auth URL from the current session. -- `Failed to bind ... 1455 (EADDRINUSE)` - - OpenClaw falls back to manual paste mode — this is expected and still works. Copy the callback URL from your browser and submit it. -- Popup blocked in browser - - Copy the auth URL from the log panel and paste it into a new tab manually. +- PTY output looks noisy or has many `\x1b[...]` codes + - Expected for terminal apps; clean ANSI before regex matching. +- Callback appears typed character-by-character + - Normal for this prompt renderer. +- Callback submitted but no OAuth profile appears + - Ensure callback `state` matches the latest auth URL from this run. +- `No API key found for provider "openai-codex"` on smoke test + - OAuth did not link; verify `openai_codex_oauth_linked=true` before agent calls. +- PTY onboarding hangs or behaves oddly + - Do not pass `cwd=` to `sandbox.pty.create(...)` in this flow. ## Related - - Run OpenClaw's web gateway with token auth + + Run OpenClaw headless in a sandbox Connect OpenClaw to Telegram and approve pairing - - Run OpenClaw headless in a sandbox + + Change models, tokens, and channels after deploy From c5b0fbf0d45072032eb86f3aa2da4085db7073e0 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Wed, 25 Feb 2026 15:32:54 -0800 Subject: [PATCH 10/20] js implementation of oauth --- docs/agents/openclaw-oauth.mdx | 205 +++++++++++++++++++++++++++------ 1 file changed, 172 insertions(+), 33 deletions(-) diff --git a/docs/agents/openclaw-oauth.mdx b/docs/agents/openclaw-oauth.mdx index 907af847..3670062d 100644 --- a/docs/agents/openclaw-oauth.mdx +++ b/docs/agents/openclaw-oauth.mdx @@ -10,7 +10,7 @@ This page uses a PTY-first flow, which works on raw OpenClaw sandboxes even when ## Prerequisites -- Local Python app or notebook with `e2b` SDK installed. +- Local app/notebook with E2B SDK installed (Python or JavaScript). - `E2B_API_KEY` in your local environment. - OpenAI account with Codex access. @@ -18,7 +18,9 @@ This page uses a PTY-first flow, which works on raw OpenClaw sandboxes even when ### 1) Connect to your sandbox -```python + +```python Python +import json import re import threading import time @@ -26,18 +28,48 @@ from e2b import Sandbox from e2b.sandbox.commands.command_handle import CommandExitException, PtySize sandbox = Sandbox.connect("", timeout=60 * 30) -``` - -### 2) Start onboarding in PTY -```python pty_chunks = [] +terminal = None +thread = None def on_pty(data: bytes): text = data.decode("utf-8", errors="replace") print(text, end="") pty_chunks.append(text) +def pty_send_line(text: str): + # For OpenClaw TUI prompts, carriage return is more reliable than newline. + sandbox.pty.send_stdin(terminal.pid, (text + "\r").encode()) +``` +```typescript JavaScript & TypeScript +import { Sandbox } from 'e2b' +import * as readline from 'node:readline/promises' +import { stdin as input, stdout as output } from 'node:process' + +const sandbox = await Sandbox.connect('', { + timeoutMs: 60 * 30 * 1000, +}) + +let ptyOutput = '' +const decoder = new TextDecoder() +const encoder = new TextEncoder() +let terminal +let waitPromise + +const rl = readline.createInterface({ input, output }) +const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms)) + +async function sendLine(line) { + await sandbox.pty.sendInput(terminal.pid, encoder.encode(`${line}\r`)) +} +``` + + +### 2) Start onboarding in PTY + + +```python Python terminal = sandbox.pty.create( PtySize(cols=140, rows=40), timeout=0, # keep PTY open while you complete browser login @@ -49,20 +81,36 @@ thread = threading.Thread( ) thread.start() -def pty_send_line(text: str): - # For OpenClaw TUI prompts, carriage return is more reliable than newline. - sandbox.pty.send_stdin(terminal.pid, (text + "\r").encode()) - onboard_cmd = ( "openclaw onboard --accept-risk --flow quickstart --mode local " "--auth-choice openai-codex --skip-ui --skip-channels --skip-skills --skip-health" ) pty_send_line(onboard_cmd) ``` +```typescript JavaScript & TypeScript +terminal = await sandbox.pty.create({ + cols: 140, + rows: 40, + timeoutMs: 10 * 60_000, + onData: (data) => { + const text = decoder.decode(data) + process.stdout.write(text) + ptyOutput += text + }, +}) + +waitPromise = terminal.wait() + +await sendLine( + 'openclaw onboard --accept-risk --flow quickstart --mode local --auth-choice openai-codex --skip-ui --skip-channels --skip-skills --skip-health' +) +``` + ### 3) Extract auth URL and open it in your local browser -```python + +```python Python ansi_re = re.compile(r"\x1b\[[0-9;?]*[ -/]*[@-~]") oauth_re = re.compile(r"https://auth\.openai\.com/oauth/authorize[^\s\"']+") @@ -81,6 +129,27 @@ if not auth_url: print("Open this URL in your LOCAL browser:\n") print(auth_url) ``` +```typescript JavaScript & TypeScript +const ansiRe = /\x1b\[[0-9;?]*[ -/]*[@-~]/g +const oauthRe = /https:\/\/auth\.openai\.com\/oauth\/authorize[^\s"']+/ + +let authUrl = null +for (let i = 0; i < 60; i++) { + const cleaned = ptyOutput.replace(/\r/g, '\n').replace(ansiRe, '') + const match = cleaned.match(oauthRe) + if (match) { + authUrl = match[0] + break + } + await sleep(1000) +} + +if (!authUrl) throw new Error('OAuth URL not found in PTY output') + +console.log('\nOpen this URL in your LOCAL browser:\n') +console.log(authUrl) +``` + After browser sign-in, copy the localhost callback URL: @@ -88,7 +157,8 @@ After browser sign-in, copy the localhost callback URL: ### 4) Submit callback back into PTY -```python + +```python Python callback_url = input("\nPaste localhost callback URL: ").strip() pty_send_line(callback_url) @@ -101,10 +171,28 @@ try: except CommandExitException: pass ``` +```typescript JavaScript & TypeScript +const callbackUrl = (await rl.question('\nPaste localhost callback URL: ')).trim() +await sendLine(callbackUrl) + +await sleep(2000) +await sendLine('exit') + +await Promise.race([ + waitPromise, + new Promise((_, reject) => + setTimeout(() => reject(new Error('PTY wait timeout after 30s')), 30_000) + ), +]).catch((e) => console.warn(String(e))) + +rl.close() +``` + ### 5) Verify OAuth link and run a smoke command -```python + +```python Python auth_check = sandbox.commands.run( """python3 - <<'PY' import json @@ -121,15 +209,66 @@ PY""" ) print(auth_check.stdout) +sandbox.commands.run( + "openclaw config set agents.defaults.model.primary openai-codex/gpt-5.2-codex" +) + smoke = sandbox.commands.run( 'openclaw agent --session-id main --thinking low --message "Say exactly: oauth-ok"' ) print(smoke.stdout) ``` +```typescript JavaScript & TypeScript +const authRaw = await sandbox.commands.run( + "cat /home/user/.openclaw/agents/main/agent/auth-profiles.json 2>/dev/null || echo '{}'" +) +const authStore = JSON.parse(authRaw.stdout || '{}') +const profiles = authStore?.profiles ?? {} +const linked = Object.values(profiles).some( + (p) => p?.provider === 'openai-codex' && p?.refresh +) +console.log({ openai_codex_oauth_linked: linked }) +if (!linked) throw new Error('openai-codex OAuth profile not linked') -## Reset and relink OAuth +await sandbox.commands.run( + 'openclaw config set agents.defaults.model.primary openai-codex/gpt-5.2-codex' +) -```python +const smoke = await sandbox.commands.run( + 'openclaw agent --session-id main --thinking low --message "Say exactly: oauth-ok"' +) +console.log(smoke.stdout) +``` + + +## Cleanup + + +```python Python +sandbox.kill() +``` +```typescript JavaScript & TypeScript +await sandbox.kill() +``` + + +## Troubleshooting + +- PTY output looks noisy or has many `\x1b[...]` codes + - Expected for terminal apps; clean ANSI before regex matching. +- Callback appears typed character-by-character + - Normal for this prompt renderer. +- Callback submitted but no OAuth profile appears + - Ensure callback `state` matches the latest auth URL from this run. +- `No API key found for provider "openai-codex"` on smoke test + - OAuth did not link; verify `openai_codex_oauth_linked=true` before agent calls. +- PTY onboarding hangs or behaves oddly + - Do not pass `cwd=` to `sandbox.pty.create(...)` in this flow. + +## Optional: reset and relink OAuth + + +```python Python reset = sandbox.commands.run( """python3 - <<'PY' import json @@ -149,27 +288,27 @@ PY""" ) print(reset.stdout) ``` +```typescript JavaScript & TypeScript +const reset = await sandbox.commands.run(`python3 - <<'PY' +import json +from pathlib import Path -Then rerun steps 2-5. - -## Cleanup - -```python -sandbox.kill() +p = Path('/home/user/.openclaw/agents/main/agent/auth-profiles.json') +raw = json.loads(p.read_text()) if p.exists() else {} +profiles = raw.get('profiles', {}) if isinstance(raw, dict) else {} +raw['profiles'] = { + k: v for k, v in profiles.items() + if not (isinstance(v, dict) and v.get('provider') == 'openai-codex') +} +p.parent.mkdir(parents=True, exist_ok=True) +p.write_text(json.dumps(raw, indent=2) + '\\n') +print('cleared openai-codex oauth profiles') +PY`) +console.log(reset.stdout) ``` + -## Troubleshooting - -- PTY output looks noisy or has many `\x1b[...]` codes - - Expected for terminal apps; clean ANSI before regex matching. -- Callback appears typed character-by-character - - Normal for this prompt renderer. -- Callback submitted but no OAuth profile appears - - Ensure callback `state` matches the latest auth URL from this run. -- `No API key found for provider "openai-codex"` on smoke test - - OAuth did not link; verify `openai_codex_oauth_linked=true` before agent calls. -- PTY onboarding hangs or behaves oddly - - Do not pass `cwd=` to `sandbox.pty.create(...)` in this flow. +After reset, rerun steps 2-5. ## Related From 816982cc5aac44f73a2422656480ce8f5a609cb3 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Wed, 25 Feb 2026 15:35:29 -0800 Subject: [PATCH 11/20] cleanup a bit --- docs/agents/openclaw-oauth.mdx | 17 ++----------- docs/agents/openclaw.mdx | 46 ---------------------------------- 2 files changed, 2 insertions(+), 61 deletions(-) diff --git a/docs/agents/openclaw-oauth.mdx b/docs/agents/openclaw-oauth.mdx index 3670062d..a70640c4 100644 --- a/docs/agents/openclaw-oauth.mdx +++ b/docs/agents/openclaw-oauth.mdx @@ -6,11 +6,11 @@ icon: "key" OpenClaw can authenticate with OpenAI Codex via OAuth instead of an API key. -This page uses a PTY-first flow, which works on raw OpenClaw sandboxes even when setup API endpoints are unavailable. +This page uses a PTY-first flow, which works on raw OpenClaw sandboxes. ## Prerequisites -- Local app/notebook with E2B SDK installed (Python or JavaScript). +- App started from setup. - `E2B_API_KEY` in your local environment. - OpenAI account with Codex access. @@ -252,19 +252,6 @@ await sandbox.kill() ``` -## Troubleshooting - -- PTY output looks noisy or has many `\x1b[...]` codes - - Expected for terminal apps; clean ANSI before regex matching. -- Callback appears typed character-by-character - - Normal for this prompt renderer. -- Callback submitted but no OAuth profile appears - - Ensure callback `state` matches the latest auth URL from this run. -- `No API key found for provider "openai-codex"` on smoke test - - OAuth did not link; verify `openai_codex_oauth_linked=true` before agent calls. -- PTY onboarding hangs or behaves oddly - - Do not pass `cwd=` to `sandbox.pty.create(...)` in this flow. - ## Optional: reset and relink OAuth diff --git a/docs/agents/openclaw.mdx b/docs/agents/openclaw.mdx index 143f8b53..292e9708 100644 --- a/docs/agents/openclaw.mdx +++ b/docs/agents/openclaw.mdx @@ -382,52 +382,6 @@ sandbox.kill() ``` -## Launch the gateway - -OpenClaw has a built-in [web UI and chat interface](https://openclaw.ai) served by its gateway. Start it inside a sandbox and connect from your browser. - - -This sandbox is created with a 10-minute timeout and auto-pause enabled — after 10 minutes of inactivity it pauses and can be resumed later. See [Sandbox Persistence](/docs/sandbox/persistence) and [Sandbox Lifecycle](/docs/sandbox) for more details. - - - -```typescript JavaScript & TypeScript -import { Sandbox } from 'e2b' - -const sandbox = await Sandbox.betaCreate('openclaw', { - envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY }, - autoPause: true, - timeoutMs: 10 * 60 * 1000, -}) - -// Start the gateway -sandbox.commands.run('openclaw gateway --port 18789 --verbose') - -const host = sandbox.getHost(18789) -const url = `https://${host}` -console.log(`OpenClaw Gateway: ${url}`) -console.log(`Sandbox ID: ${sandbox.sandboxId}`) -``` -```python Python -import os -from e2b import Sandbox - -sandbox = Sandbox.beta_create("openclaw", envs={ - "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"], -}, auto_pause=True, timeout=10 * 60) - -# Start the gateway -sandbox.commands.run("openclaw gateway --port 18789 --verbose") - -host = sandbox.get_host(18789) -url = f"https://{host}" -print(f"OpenClaw Gateway: {url}") -print(f"Sandbox ID: {sandbox.sandbox_id}") -``` - - -For a complete example with token auth, device pairing, and browser auto-approval, see [OpenClaw Gateway](/docs/agents/openclaw-gateway). - ## Build a custom template If you need to customize the environment (e.g. pre-install dependencies, add config files), build your own template on top of the pre-built `openclaw` template. From ce674ce7b4cabfe24b709b76a95dd47396c1243a Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Wed, 25 Feb 2026 15:38:58 -0800 Subject: [PATCH 12/20] rm numbering --- docs/agents/openclaw-environment.mdx | 14 +++++++------- docs/agents/openclaw-oauth.mdx | 12 ++++++------ 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/agents/openclaw-environment.mdx b/docs/agents/openclaw-environment.mdx index ec1ef63a..a9b69592 100644 --- a/docs/agents/openclaw-environment.mdx +++ b/docs/agents/openclaw-environment.mdx @@ -26,14 +26,14 @@ All commands below are meant to run inside that terminal session. | OpenAI / Anthropic API keys | No | Update runtime env used by OpenClaw processes | | OpenAI Codex OAuth profile | No | Clear auth profile, then relink | -## 1) Change default model +## Change default model ```bash openclaw config set agents.defaults.model.primary openai/gpt-5.2 openclaw models status --json ``` -## 2) Rotate gateway token (no sandbox restart) +## Rotate gateway token (no sandbox restart) ```bash NEW_TOKEN="replace-me" @@ -49,7 +49,7 @@ ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting After rotation, old URLs with the previous `?token=` stop working. -## 3) Update provider keys after deploy +## Update provider keys after deploy ### One-off use @@ -71,7 +71,7 @@ nohup bash -lc 'source /home/user/.openclaw-runtime.env; openclaw gateway --allo > /home/user/openclaw_gateway.log 2>&1 & ``` -## 4) Update Telegram bot token +## Update Telegram bot token ```bash NEW_TELEGRAM_BOT_TOKEN="123456:abc" @@ -83,7 +83,7 @@ nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token "my- > /home/user/openclaw_gateway.log 2>&1 & ``` -## 5) Reset Telegram channel + pairing state +## Reset Telegram channel + pairing state ```bash openclaw channels remove --channel telegram --delete @@ -97,7 +97,7 @@ Approve a pending code: openclaw pairing approve --channel telegram ``` -## 6) Reset OpenAI Codex OAuth profile +## Reset OpenAI Codex OAuth profile ```bash python3 - <<'PY' @@ -119,7 +119,7 @@ PY Then relink OAuth using the OAuth guide. -## 7) Verify current state +## Verify current state ```bash openclaw channels list --json diff --git a/docs/agents/openclaw-oauth.mdx b/docs/agents/openclaw-oauth.mdx index a70640c4..4e803e4f 100644 --- a/docs/agents/openclaw-oauth.mdx +++ b/docs/agents/openclaw-oauth.mdx @@ -16,7 +16,7 @@ This page uses a PTY-first flow, which works on raw OpenClaw sandboxes. ## End-to-end PTY flow -### 1) Connect to your sandbox +### Connect to your sandbox ```python Python @@ -66,7 +66,7 @@ async function sendLine(line) { ``` -### 2) Start onboarding in PTY +### Start onboarding in PTY ```python Python @@ -107,7 +107,7 @@ await sendLine( ``` -### 3) Extract auth URL and open it in your local browser +### Extract auth URL and open it in your local browser ```python Python @@ -155,7 +155,7 @@ After browser sign-in, copy the localhost callback URL: `http://localhost:1455/auth/callback?code=...&state=...` -### 4) Submit callback back into PTY +### Submit callback back into PTY ```python Python @@ -189,7 +189,7 @@ rl.close() ``` -### 5) Verify OAuth link and run a smoke command +### Verify OAuth link and run a smoke command ```python Python @@ -295,7 +295,7 @@ console.log(reset.stdout) ``` -After reset, rerun steps 2-5. +After reset, rerun onboarding and verification. ## Related From c4804828b6abba4aed4440fda8f8395609ea6b1f Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Wed, 25 Feb 2026 15:45:55 -0800 Subject: [PATCH 13/20] env has js/python too, but possibly can yeet this guy and put in the cookbook or something --- docs/agents/openclaw-environment.mdx | 292 +++++++++++++++++++++------ 1 file changed, 231 insertions(+), 61 deletions(-) diff --git a/docs/agents/openclaw-environment.mdx b/docs/agents/openclaw-environment.mdx index a9b69592..e7f223bd 100644 --- a/docs/agents/openclaw-environment.mdx +++ b/docs/agents/openclaw-environment.mdx @@ -4,17 +4,7 @@ description: "Change models, rotate tokens, update channels, and reset OpenClaw icon: "sliders" --- -Use this guide for **post-deploy changes** to a running OpenClaw sandbox. - -You do not need to tear down the sandbox for most updates. - -## Connect to sandbox terminal - -```bash -e2b sbx connect -``` - -All commands below are meant to run inside that terminal session. +Use this guide for post-deploy changes in a running OpenClaw sandbox using SDK calls. ## What you can change without rebuilding @@ -26,81 +16,208 @@ All commands below are meant to run inside that terminal session. | OpenAI / Anthropic API keys | No | Update runtime env used by OpenClaw processes | | OpenAI Codex OAuth profile | No | Clear auth profile, then relink | +## Connect to your sandbox + + +```python Python +from e2b import Sandbox + +sandbox = Sandbox.connect("", timeout=60 * 30) +``` +```typescript JavaScript & TypeScript +import { Sandbox } from 'e2b' + +const sandbox = await Sandbox.connect('', { + timeoutMs: 60 * 30 * 1000, +}) +``` + + ## Change default model -```bash -openclaw config set agents.defaults.model.primary openai/gpt-5.2 -openclaw models status --json + +```python Python +sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2") +print(sandbox.commands.run("openclaw models status --json").stdout) +``` +```typescript JavaScript & TypeScript +await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2') +const models = await sandbox.commands.run('openclaw models status --json') +console.log(models.stdout) ``` + ## Rotate gateway token (no sandbox restart) -```bash -NEW_TOKEN="replace-me" -PORT=18789 +After rotation, old URLs with the previous `?token=` stop working. + + +```python Python +NEW_TOKEN = "replace-me" +PORT = 18789 -pkill -f 'openclaw gateway' >/dev/null 2>&1 || true +rotate = sandbox.commands.run( + """bash -lc ' +set -euo pipefail +pkill -f "openclaw gateway" >/dev/null 2>&1 || true nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token "$NEW_TOKEN" --port "$PORT" \ > /home/user/openclaw_gateway.log 2>&1 & - sleep 1 -ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting +ss -ltn | grep -q ":$PORT " && echo ready || echo waiting +'""", + envs={"NEW_TOKEN": NEW_TOKEN, "PORT": str(PORT)}, +) +print(rotate.stdout) ``` - -After rotation, old URLs with the previous `?token=` stop working. +```typescript JavaScript & TypeScript +const NEW_TOKEN = 'replace-me' +const PORT = 18789 + +const rotate = await sandbox.commands.run( + `bash -lc ' +set -euo pipefail +pkill -f "openclaw gateway" >/dev/null 2>&1 || true +nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token "$NEW_TOKEN" --port "$PORT" \ + > /home/user/openclaw_gateway.log 2>&1 & +sleep 1 +ss -ltn | grep -q ":$PORT " && echo ready || echo waiting +'`, + { envs: { NEW_TOKEN, PORT: String(PORT) } } +) +console.log(rotate.stdout) +``` + ## Update provider keys after deploy -### One-off use +### One-off command -```bash -OPENAI_API_KEY="sk-..." openclaw agent --local --message "hello" + +```python Python +one_off = sandbox.commands.run( + 'openclaw agent --local --message "hello"', + envs={"OPENAI_API_KEY": "sk-..."}, +) +print(one_off.stdout) ``` +```typescript JavaScript & TypeScript +const oneOff = await sandbox.commands.run( + 'openclaw agent --local --message "hello"', + { envs: { OPENAI_API_KEY: 'sk-...' } } +) +console.log(oneOff.stdout) +``` + ### Persistent runtime env -```bash -cat > /home/user/.openclaw-runtime.env <<'ENV' -export OPENAI_API_KEY="sk-..." -export ANTHROPIC_API_KEY="sk-ant-..." -ENV + +```python Python +persist = sandbox.commands.run( + """bash -lc ' +set -euo pipefail +cat > /home/user/.openclaw-runtime.env </dev/null 2>&1 || true -nohup bash -lc 'source /home/user/.openclaw-runtime.env; openclaw gateway --allow-unconfigured --bind lan --auth token --token "my-token" --port 18789' \ - > /home/user/openclaw_gateway.log 2>&1 & +'""", + envs={ + "OPENAI_API_KEY": "sk-...", + "ANTHROPIC_API_KEY": "sk-ant-...", + }, +) +print(persist.stdout) +``` +```typescript JavaScript & TypeScript +const persist = await sandbox.commands.run( + `bash -lc ' +set -euo pipefail +cat > /home/user/.openclaw-runtime.env < ## Update Telegram bot token -```bash -NEW_TELEGRAM_BOT_TOKEN="123456:abc" - + +```python Python +telegram = sandbox.commands.run( + """bash -lc ' +set -euo pipefail +openclaw config set plugins.entries.telegram.enabled true openclaw channels add --channel telegram --token "$NEW_TELEGRAM_BOT_TOKEN" - -pkill -f 'openclaw gateway' >/dev/null 2>&1 || true -nohup openclaw gateway --allow-unconfigured --bind lan --auth token --token "my-token" --port 18789 \ - > /home/user/openclaw_gateway.log 2>&1 & +'""", + envs={"NEW_TELEGRAM_BOT_TOKEN": "123456:abc"}, +) +print(telegram.stdout) ``` +```typescript JavaScript & TypeScript +const telegram = await sandbox.commands.run( + `bash -lc ' +set -euo pipefail +openclaw config set plugins.entries.telegram.enabled true +openclaw channels add --channel telegram --token "$NEW_TELEGRAM_BOT_TOKEN" +'`, + { envs: { NEW_TELEGRAM_BOT_TOKEN: '123456:abc' } } +) +console.log(telegram.stdout) +``` + ## Reset Telegram channel + pairing state -```bash -openclaw channels remove --channel telegram --delete -openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN" -openclaw pairing list --json --channel telegram + +```python Python +sandbox.commands.run("openclaw channels remove --channel telegram --delete") +sandbox.commands.run( + 'openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"', + envs={"TELEGRAM_BOT_TOKEN": "123456:abc"}, +) +print(sandbox.commands.run("openclaw pairing list --json --channel telegram").stdout) +``` +```typescript JavaScript & TypeScript +await sandbox.commands.run('openclaw channels remove --channel telegram --delete') +await sandbox.commands.run( + 'openclaw channels add --channel telegram --token "$TELEGRAM_BOT_TOKEN"', + { envs: { TELEGRAM_BOT_TOKEN: '123456:abc' } } +) +const pairing = await sandbox.commands.run('openclaw pairing list --json --channel telegram') +console.log(pairing.stdout) ``` + -Approve a pending code: +Approve a pending pairing code: -```bash -openclaw pairing approve --channel telegram + +```python Python +PAIRING_CODE = "ABCDEFGH" +sandbox.commands.run(f"openclaw pairing approve --channel telegram {PAIRING_CODE}") ``` +```typescript JavaScript & TypeScript +const PAIRING_CODE = 'ABCDEFGH' +await sandbox.commands.run(`openclaw pairing approve --channel telegram ${PAIRING_CODE}`) +``` + ## Reset OpenAI Codex OAuth profile -```bash -python3 - <<'PY' + +```python Python +reset = sandbox.commands.run( + """python3 - <<'PY' import json from pathlib import Path @@ -112,21 +229,78 @@ raw['profiles'] = { if not (isinstance(v, dict) and v.get('provider') == 'openai-codex') } p.parent.mkdir(parents=True, exist_ok=True) -p.write_text(json.dumps(raw, indent=2) + '\n') +p.write_text(json.dumps(raw, indent=2) + '\\n') print('cleared openai-codex oauth profiles') -PY +PY""" +) +print(reset.stdout) ``` +```typescript JavaScript & TypeScript +const reset = await sandbox.commands.run(`python3 - <<'PY' +import json +from pathlib import Path + +p = Path('/home/user/.openclaw/agents/main/agent/auth-profiles.json') +raw = json.loads(p.read_text()) if p.exists() else {} +profiles = raw.get('profiles', {}) if isinstance(raw, dict) else {} +raw['profiles'] = { + k: v for k, v in profiles.items() + if not (isinstance(v, dict) and v.get('provider') == 'openai-codex') +} +p.parent.mkdir(parents=True, exist_ok=True) +p.write_text(json.dumps(raw, indent=2) + '\\n') +print('cleared openai-codex oauth profiles') +PY`) +console.log(reset.stdout) +``` + Then relink OAuth using the OAuth guide. ## Verify current state -```bash -openclaw channels list --json -openclaw channels status --json --probe -openclaw pairing list --json --channel telegram -openclaw models status --json + +```python Python +print(sandbox.commands.run("openclaw channels list --json").stdout) +print(sandbox.commands.run("openclaw channels status --json --probe").stdout) +print(sandbox.commands.run("openclaw pairing list --json --channel telegram").stdout) +print(sandbox.commands.run("openclaw models status --json").stdout) +``` +```typescript JavaScript & TypeScript +console.log((await sandbox.commands.run('openclaw channels list --json')).stdout) +console.log((await sandbox.commands.run('openclaw channels status --json --probe')).stdout) +console.log((await sandbox.commands.run('openclaw pairing list --json --channel telegram')).stdout) +console.log((await sandbox.commands.run('openclaw models status --json')).stdout) +``` + + +## Interactive tasks with PTY (optional) + +Use PTY when the command expects interactive input (for example OAuth onboarding). + + +```python Python +from e2b.sandbox.commands.command_handle import PtySize + +terminal = sandbox.pty.create(PtySize(cols=140, rows=40), timeout=0) +sandbox.pty.send_stdin(terminal.pid, b"openclaw onboard --accept-risk --flow quickstart --mode local --auth-choice openai-codex\\r") ``` +```typescript JavaScript & TypeScript +const terminal = await sandbox.pty.create({ cols: 140, rows: 40, timeoutMs: 10 * 60_000, onData: (d) => process.stdout.write(new TextDecoder().decode(d)) }) +await sandbox.pty.sendInput(terminal.pid, new TextEncoder().encode('openclaw onboard --accept-risk --flow quickstart --mode local --auth-choice openai-codex\\r')) +``` + + +## Cleanup + + +```python Python +sandbox.kill() +``` +```typescript JavaScript & TypeScript +await sandbox.kill() +``` + ## Troubleshooting @@ -139,10 +313,6 @@ openclaw models status --json - Pairing list is empty - Send a fresh message to the bot first. -## Using SDK instead of terminal - -If you prefer SDK automation, execute the same shell commands with `sandbox.commands.run(...)`. - ## Related From 677820afa102afa06b40bbdf4a6a3f34aa471608 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Thu, 26 Feb 2026 19:18:14 -0800 Subject: [PATCH 14/20] bottom links --- docs/agents/openclaw-environment.mdx | 5 ++++- docs/agents/openclaw-gateway.mdx | 11 ++++------- docs/agents/openclaw-oauth.mdx | 5 ++++- docs/agents/openclaw-telegram.mdx | 6 +++--- docs/agents/openclaw.mdx | 29 +++++++++++++++------------- 5 files changed, 31 insertions(+), 25 deletions(-) diff --git a/docs/agents/openclaw-environment.mdx b/docs/agents/openclaw-environment.mdx index e7f223bd..2ecc9aca 100644 --- a/docs/agents/openclaw-environment.mdx +++ b/docs/agents/openclaw-environment.mdx @@ -315,7 +315,7 @@ await sandbox.kill() ## Related - + Run OpenClaw headless in a sandbox @@ -325,4 +325,7 @@ await sandbox.kill() Connect and pair Telegram users + + Link OpenAI Codex OAuth in a sandbox + diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx index 837ba728..4e09e949 100644 --- a/docs/agents/openclaw-gateway.mdx +++ b/docs/agents/openclaw-gateway.mdx @@ -154,20 +154,17 @@ Once approved, the browser connects and the gateway UI loads. ## Related - + Run OpenClaw headless in a sandbox Connect OpenClaw to Telegram and approve pairing + + Link OpenAI Codex OAuth in a sandbox + Change models, tokens, and channels after deploy - - Auto-pause, resume, and manage sandbox lifecycle - - - Connect to the sandbox via SSH - diff --git a/docs/agents/openclaw-oauth.mdx b/docs/agents/openclaw-oauth.mdx index 4e803e4f..6fdcd991 100644 --- a/docs/agents/openclaw-oauth.mdx +++ b/docs/agents/openclaw-oauth.mdx @@ -299,10 +299,13 @@ After reset, rerun onboarding and verification. ## Related - + Run OpenClaw headless in a sandbox + + Run OpenClaw's web gateway with token auth + Connect OpenClaw to Telegram and approve pairing diff --git a/docs/agents/openclaw-telegram.mdx b/docs/agents/openclaw-telegram.mdx index d2aed490..c3f56b71 100644 --- a/docs/agents/openclaw-telegram.mdx +++ b/docs/agents/openclaw-telegram.mdx @@ -190,13 +190,13 @@ print(logs.stdout) Run OpenClaw's web gateway with token auth + + Link OpenAI Codex OAuth in a sandbox + Change models, tokens, and channel settings after deploy Run OpenClaw headless in a sandbox - - Protect exposed endpoints with tokens - diff --git a/docs/agents/openclaw.mdx b/docs/agents/openclaw.mdx index 292e9708..7051d3f5 100644 --- a/docs/agents/openclaw.mdx +++ b/docs/agents/openclaw.mdx @@ -451,16 +451,19 @@ python build.py ``` -## Related guides - - - - Auto-pause, resume, and manage sandbox lifecycle - - - Clone repos, manage branches, and push changes - - - Connect to the sandbox via SSH for interactive sessions - - +## Related + + + + Run OpenClaw's web gateway with token auth + + + Connect OpenClaw to Telegram and approve pairing + + + Link OpenAI Codex OAuth in a sandbox + + + Change models, tokens, and channel settings after deploy + + From 099ff48a618540ba8d7a7f817cefc7e4a5df5528 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Thu, 26 Feb 2026 19:21:19 -0800 Subject: [PATCH 15/20] standardize on openai --- docs/agents/openclaw-gateway.mdx | 18 ++++++++++++------ docs/agents/openclaw-telegram.mdx | 9 ++++----- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx index 4e09e949..5826c355 100644 --- a/docs/agents/openclaw-gateway.mdx +++ b/docs/agents/openclaw-gateway.mdx @@ -19,17 +19,20 @@ const PORT = 18789 // 1. Create sandbox const sandbox = await Sandbox.create('openclaw', { - envs: { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY }, + envs: { OPENAI_API_KEY: process.env.OPENAI_API_KEY }, timeoutMs: 3600_000, }) -// 2. Start the gateway with token auth +// 2. Set the default model +await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.3-codex') + +// 3. Start the gateway with token auth sandbox.commands.run( `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}`, { background: true } ) -// 3. Wait for the gateway to start listening +// 4. Wait for the gateway to start listening for (let i = 0; i < 45; i++) { const probe = await sandbox.commands.run( `bash -lc 'ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting'` @@ -51,16 +54,19 @@ PORT = 18789 # 1. Create sandbox sandbox = Sandbox.create("openclaw", envs={ - "ANTHROPIC_API_KEY": os.environ["ANTHROPIC_API_KEY"], + "OPENAI_API_KEY": os.environ["OPENAI_API_KEY"], }, timeout=3600) -# 2. Start the gateway with token auth +# 2. Set the default model +sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.3-codex") + +# 3. Start the gateway with token auth sandbox.commands.run( f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}", background=True, ) -# 3. Wait for the gateway to start listening +# 4. Wait for the gateway to start listening for _ in range(45): probe = sandbox.commands.run( f'bash -lc \'ss -ltn | grep -q ":{PORT} " && echo ready || echo waiting\'' diff --git a/docs/agents/openclaw-telegram.mdx b/docs/agents/openclaw-telegram.mdx index c3f56b71..d33bc986 100644 --- a/docs/agents/openclaw-telegram.mdx +++ b/docs/agents/openclaw-telegram.mdx @@ -17,7 +17,7 @@ This guide covers the working flow we used: ## Prerequisites - A Telegram bot token from [@BotFather](https://t.me/BotFather). -- An OpenAI or Anthropic provider key for the OpenClaw model. +- An OpenAI API key for the OpenClaw model. - E2B API key configured locally. ## Quick start @@ -37,7 +37,7 @@ const sandbox = await Sandbox.create('openclaw', { timeoutMs: 3600_000, }) -await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2') +await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.3-codex') // Enable the Telegram plugin (required before adding the channel) await sandbox.commands.run('openclaw config set plugins.entries.telegram.enabled true') @@ -69,7 +69,7 @@ sandbox = Sandbox.create("openclaw", envs={ "TELEGRAM_BOT_TOKEN": os.environ["TELEGRAM_BOT_TOKEN"], }, timeout=3600) -sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2") +sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.3-codex") # Enable the Telegram plugin (required before adding the channel) sandbox.commands.run("openclaw config set plugins.entries.telegram.enabled true") @@ -179,8 +179,7 @@ print(logs.stdout) - `OpenClaw: access not configured` - Pairing has not been approved yet. Run `openclaw pairing approve ...`. - `No API key found for provider ...` - - Model/provider mismatch. If model is `openai/...`, set `OPENAI_API_KEY`. - - If model is `anthropic/...`, set `ANTHROPIC_API_KEY`. + - This guide uses `openai/gpt-5.3-codex`. Set `OPENAI_API_KEY` in sandbox envs. - No pending pairing requests from `pairing list` - Send a fresh message to the bot first, then retry `pairing list --channel telegram`. From 36fd2ab886c328dc9d17d58f425d6ee03f9f0874 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Thu, 26 Feb 2026 20:47:11 -0800 Subject: [PATCH 16/20] default to 5.2 --- docs/agents/openclaw-gateway.mdx | 71 +++++++++++++++++++++++++++++++- 1 file changed, 69 insertions(+), 2 deletions(-) diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx index 5826c355..a1da10c0 100644 --- a/docs/agents/openclaw-gateway.mdx +++ b/docs/agents/openclaw-gateway.mdx @@ -24,7 +24,7 @@ const sandbox = await Sandbox.create('openclaw', { }) // 2. Set the default model -await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.3-codex') +await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2') // 3. Start the gateway with token auth sandbox.commands.run( @@ -58,7 +58,7 @@ sandbox = Sandbox.create("openclaw", envs={ }, timeout=3600) # 2. Set the default model -sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.3-codex") +sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2") # 3. Start the gateway with token auth sandbox.commands.run( @@ -158,6 +158,73 @@ Once approved, the browser connects and the gateway UI loads. | `--token ` | The auth token (passed as `?token=` in the URL) | | `--port ` | Gateway listen port (default: `18789`) | +## How to restart the gateway + +Use this when the gateway is already running and you want a clean restart (for example, after changing model or env settings). + + +We can't use the `openclaw gateway restart` command here. Some SDK environments cannot target a specific Unix user in `commands.run`. The commands below use the default command user context. + + + +```typescript JavaScript & TypeScript +const TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-gateway-token' +const PORT = 18789 + +// 1) Kill existing gateway processes if present +await sandbox.commands.run( + `bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do for pid in $(pgrep -f "$p" || true); do kill "$pid" >/dev/null 2>&1 || true; done; done'` +) +await new Promise((r) => setTimeout(r, 1000)) + +// 2) Start gateway again +await sandbox.commands.run( + `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${TOKEN} --port ${PORT}`, + { background: true } +) + +// 3) Wait for listening socket +for (let i = 0; i < 45; i++) { + const probe = await sandbox.commands.run( + `bash -lc 'ss -ltn | grep -q ":${PORT} " && echo ready || echo waiting'` + ) + if (probe.stdout.trim() === 'ready') break + await new Promise((r) => setTimeout(r, 1000)) +} +``` +```python Python +import os, time + +TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-gateway-token") +PORT = 18789 + +# 1) Kill existing gateway processes if present +sandbox.commands.run( + """bash -lc 'for p in "[o]penclaw gateway" "[o]penclaw-gateway"; do +for pid in $(pgrep -f "$p" || true); do + kill "$pid" >/dev/null 2>&1 || true +done +done'""" +) +time.sleep(1) + +# 2) Start gateway again +sandbox.commands.run( + f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {TOKEN} --port {PORT}", + background=True, +) + +# 3) Wait for listening socket +for _ in range(45): + probe = sandbox.commands.run( + f'bash -lc \'ss -ltn | grep -q ":{PORT} " && echo ready || echo waiting\'' + ) + if probe.stdout.strip() == "ready": + break + time.sleep(1) +``` + + ## Related From 8d1a91c76e95750b5e3eac762d42a854c29cacc5 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Thu, 26 Feb 2026 20:48:32 -0800 Subject: [PATCH 17/20] use 5.2 for telegram instead of 5.3-codex; having issues for some reason --- docs/agents/openclaw-telegram.mdx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/agents/openclaw-telegram.mdx b/docs/agents/openclaw-telegram.mdx index d33bc986..b72e7a6e 100644 --- a/docs/agents/openclaw-telegram.mdx +++ b/docs/agents/openclaw-telegram.mdx @@ -37,7 +37,7 @@ const sandbox = await Sandbox.create('openclaw', { timeoutMs: 3600_000, }) -await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.3-codex') +await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2') // Enable the Telegram plugin (required before adding the channel) await sandbox.commands.run('openclaw config set plugins.entries.telegram.enabled true') @@ -69,7 +69,7 @@ sandbox = Sandbox.create("openclaw", envs={ "TELEGRAM_BOT_TOKEN": os.environ["TELEGRAM_BOT_TOKEN"], }, timeout=3600) -sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.3-codex") +sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2") # Enable the Telegram plugin (required before adding the channel) sandbox.commands.run("openclaw config set plugins.entries.telegram.enabled true") @@ -179,7 +179,7 @@ print(logs.stdout) - `OpenClaw: access not configured` - Pairing has not been approved yet. Run `openclaw pairing approve ...`. - `No API key found for provider ...` - - This guide uses `openai/gpt-5.3-codex`. Set `OPENAI_API_KEY` in sandbox envs. + - This guide uses `openai/gpt-5.2`. Set `OPENAI_API_KEY` in sandbox envs. - No pending pairing requests from `pairing list` - Send a fresh message to the bot first, then retry `pairing list --channel telegram`. From 6f317edabeaa90335b737e6da4e4e8c8cf5d4732 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Thu, 26 Feb 2026 23:29:25 -0800 Subject: [PATCH 18/20] simplify openclaw docs to what we know works to get initial version out --- docs.json | 5 +---- docs/agents/openclaw-gateway.mdx | 11 +---------- docs/agents/openclaw-telegram.mdx | 11 +---------- 3 files changed, 3 insertions(+), 24 deletions(-) diff --git a/docs.json b/docs.json index 78a7ae9b..83d43470 100644 --- a/docs.json +++ b/docs.json @@ -61,10 +61,7 @@ "icon": "/images/icons/openclaw.svg", "pages": [ "docs/agents/openclaw-gateway", - "docs/agents/openclaw-telegram", - "docs/agents/openclaw-oauth", - "docs/agents/openclaw-environment", - "docs/agents/openclaw" + "docs/agents/openclaw-telegram" ] }, "docs/agents/opencode" diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx index a1da10c0..0b84f6de 100644 --- a/docs/agents/openclaw-gateway.mdx +++ b/docs/agents/openclaw-gateway.mdx @@ -227,17 +227,8 @@ for _ in range(45): ## Related - - - Run OpenClaw headless in a sandbox - + Connect OpenClaw to Telegram and approve pairing - - Link OpenAI Codex OAuth in a sandbox - - - Change models, tokens, and channels after deploy - diff --git a/docs/agents/openclaw-telegram.mdx b/docs/agents/openclaw-telegram.mdx index b72e7a6e..9e2dc88c 100644 --- a/docs/agents/openclaw-telegram.mdx +++ b/docs/agents/openclaw-telegram.mdx @@ -185,17 +185,8 @@ print(logs.stdout) ## Related - + Run OpenClaw's web gateway with token auth - - Link OpenAI Codex OAuth in a sandbox - - - Change models, tokens, and channel settings after deploy - - - Run OpenClaw headless in a sandbox - From ce90d0acfb096239f04ccf02a82946ffd6b350f2 Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Fri, 27 Feb 2026 02:33:10 -0800 Subject: [PATCH 19/20] slack docs - dm works, may need more guide on @ usage --- docs.json | 3 +- docs/agents/openclaw-gateway.mdx | 5 +- docs/agents/openclaw-slack.mdx | 182 ++++++++++++++++++++++++++++++ docs/agents/openclaw-telegram.mdx | 5 +- docs/agents/openclaw.mdx | 5 +- 5 files changed, 196 insertions(+), 4 deletions(-) create mode 100644 docs/agents/openclaw-slack.mdx diff --git a/docs.json b/docs.json index 83d43470..0ebb722e 100644 --- a/docs.json +++ b/docs.json @@ -61,7 +61,8 @@ "icon": "/images/icons/openclaw.svg", "pages": [ "docs/agents/openclaw-gateway", - "docs/agents/openclaw-telegram" + "docs/agents/openclaw-telegram", + "docs/agents/openclaw-slack" ] }, "docs/agents/opencode" diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx index 0b84f6de..819504a0 100644 --- a/docs/agents/openclaw-gateway.mdx +++ b/docs/agents/openclaw-gateway.mdx @@ -227,8 +227,11 @@ for _ in range(45): ## Related - + Connect OpenClaw to Telegram and approve pairing + + Connect OpenClaw to Slack and approve pairing + diff --git a/docs/agents/openclaw-slack.mdx b/docs/agents/openclaw-slack.mdx new file mode 100644 index 00000000..8b564a8c --- /dev/null +++ b/docs/agents/openclaw-slack.mdx @@ -0,0 +1,182 @@ +--- +title: "OpenClaw Slack" +description: "Connect OpenClaw to Slack in an E2B sandbox, approve pairing, and chat through your Slack app." +icon: "message-circle" +--- + +OpenClaw supports Slack as a chat channel. Using E2B you can run OpenClaw in a sandbox, attach Slack tokens, and approve user pairing from the terminal. + +Official OpenClaw Slack channel docs: [docs.openclaw.ai/channels/slack](https://docs.openclaw.ai/channels/slack) + +## Prerequisites + +- Slack app tokens from the same Slack app: + - `SLACK_APP_TOKEN` (`xapp-...`, Socket Mode) + - `SLACK_BOT_TOKEN` (`xoxb-...`) +- `OPENAI_API_KEY` (this guide uses `openai/gpt-5.2`) +- E2B API key configured locally + +## Quick start + + +```typescript JavaScript & TypeScript +import { Sandbox } from 'e2b' + +const GATEWAY_PORT = 18789 +const GATEWAY_TOKEN = process.env.OPENCLAW_APP_TOKEN || 'my-openclaw-token' + +const sandbox = await Sandbox.create('openclaw', { + envs: { + OPENAI_API_KEY: process.env.OPENAI_API_KEY, + SLACK_APP_TOKEN: process.env.SLACK_APP_TOKEN, + SLACK_BOT_TOKEN: process.env.SLACK_BOT_TOKEN, + }, + timeoutMs: 3600_000, +}) + +await sandbox.commands.run('openclaw config set agents.defaults.model.primary openai/gpt-5.2') +await sandbox.commands.run('openclaw config set plugins.entries.slack.enabled true') +await sandbox.commands.run( + 'openclaw channels add --channel slack --app-token "$SLACK_APP_TOKEN" --bot-token "$SLACK_BOT_TOKEN"' +) + +await sandbox.commands.run( + `openclaw gateway --allow-unconfigured --bind lan --auth token --token ${GATEWAY_TOKEN} --port ${GATEWAY_PORT}`, + { background: true } +) + +for (let i = 0; i < 45; i++) { + const probe = await sandbox.commands.run( + `bash -lc 'ss -ltn | grep -q ":${GATEWAY_PORT} " && echo ready || echo waiting'` + ) + if (probe.stdout.trim() === 'ready') break + await new Promise((r) => setTimeout(r, 1000)) +} +``` +```python Python +import os +import time +from e2b import Sandbox + +GATEWAY_PORT = 18789 +GATEWAY_TOKEN = os.environ.get("OPENCLAW_APP_TOKEN", "my-openclaw-token") + +sandbox = Sandbox.create("openclaw", envs={ + "OPENAI_API_KEY": os.environ["OPENAI_API_KEY"], + "SLACK_APP_TOKEN": os.environ["SLACK_APP_TOKEN"], + "SLACK_BOT_TOKEN": os.environ["SLACK_BOT_TOKEN"], +}, timeout=3600) + +sandbox.commands.run("openclaw config set agents.defaults.model.primary openai/gpt-5.2") +sandbox.commands.run("openclaw config set plugins.entries.slack.enabled true") +sandbox.commands.run( + 'openclaw channels add --channel slack --app-token "$SLACK_APP_TOKEN" --bot-token "$SLACK_BOT_TOKEN"' +) + +sandbox.commands.run( + f"openclaw gateway --allow-unconfigured --bind lan --auth token --token {GATEWAY_TOKEN} --port {GATEWAY_PORT}", + background=True, +) + +for _ in range(45): + probe = sandbox.commands.run( + f"bash -lc 'ss -ltn | grep -q \":{GATEWAY_PORT} \" && echo ready || echo waiting'" + ) + if probe.stdout.strip() == "ready": + break + time.sleep(1) +``` + + + +For Slack setup, you do **not** need to open the gateway URL in a browser. The gateway process is used as a long-running channel runtime. + + +## Configure your Slack app + +Before testing messages, make sure Slack app settings are complete: + +- Socket Mode enabled (`xapp-...` token with `connections:write`) +- Bot scopes include: + - `app_mentions:read` + - `chat:write` + - `channels:history` + - `im:history` (for DMs) +- Event Subscriptions enabled with bot events: + - `app_mention` + - `message.im` (for DMs) +- App reinstalled to workspace after scope/event changes +- Bot invited to target channel (`/invite @your-bot`) + +## Pair your Slack user + +On first message, Slack may return: + +```text +OpenClaw: access not configured. +Your Slack user id: ... +Pairing code: XXXXXXXX +Ask the bot owner to approve with: +openclaw pairing approve slack XXXXXXXX +``` + +Approve that code: + + +```typescript JavaScript & TypeScript +const PAIRING_CODE = 'XXXXXXXX' // from Slack +await sandbox.commands.run(`openclaw pairing approve slack ${PAIRING_CODE}`) +``` +```python Python +PAIRING_CODE = "XXXXXXXX" # from Slack +sandbox.commands.run(f"openclaw pairing approve slack {PAIRING_CODE}") +``` + + +## Verify channel status + + +```typescript JavaScript & TypeScript +const channels = await sandbox.commands.run('openclaw channels list --json') +const status = await sandbox.commands.run('openclaw channels status --json --probe') +const logs = await sandbox.commands.run('openclaw channels logs --channel slack --lines 200') + +console.log(channels.stdout) +console.log(status.stdout) // may print text instead of JSON when gateway is unreachable +console.log(logs.stdout) +``` +```python Python +channels = sandbox.commands.run("openclaw channels list --json") +status = sandbox.commands.run("openclaw channels status --json --probe") +logs = sandbox.commands.run("openclaw channels logs --channel slack --lines 200") + +print(channels.stdout) +print(status.stdout) # may print text instead of JSON when gateway is unreachable +print(logs.stdout) +``` + + +Healthy Slack logs should show a connected provider, then incoming events and replies after you message the bot. + +## Troubleshooting + +- Slack logs show only `slack socket mode connected` and nothing else + - Slack is connected but events are not being delivered. + - Recheck Event Subscriptions (`app_mention`, `message.im`), reinstall app, and invite bot to channel. +- `OpenClaw: access not configured` + - Pairing is required for this Slack user. Approve the pairing code. +- `No API key found for provider ...` + - This guide uses `openai/gpt-5.2`. Set `OPENAI_API_KEY` in sandbox envs. +- `Sending messages to this app has been turned off` + - In Slack App Home, enable users sending messages to the app, then reinstall. + +## Related + + + + Run OpenClaw's web gateway with token auth + + + Connect OpenClaw to Telegram and approve pairing + + diff --git a/docs/agents/openclaw-telegram.mdx b/docs/agents/openclaw-telegram.mdx index 9e2dc88c..b40999b5 100644 --- a/docs/agents/openclaw-telegram.mdx +++ b/docs/agents/openclaw-telegram.mdx @@ -185,8 +185,11 @@ print(logs.stdout) ## Related - + Run OpenClaw's web gateway with token auth + + Connect OpenClaw to Slack and approve pairing + diff --git a/docs/agents/openclaw.mdx b/docs/agents/openclaw.mdx index 7051d3f5..b55e1c4f 100644 --- a/docs/agents/openclaw.mdx +++ b/docs/agents/openclaw.mdx @@ -453,13 +453,16 @@ python build.py ## Related - + Run OpenClaw's web gateway with token auth Connect OpenClaw to Telegram and approve pairing + + Connect OpenClaw to Slack and approve pairing + Link OpenAI Codex OAuth in a sandbox From f22e9f1b20d894b6ecd54403cd7119182c29ff7f Mon Sep 17 00:00:00 2001 From: Matt Brockman Date: Fri, 27 Feb 2026 11:31:39 -0800 Subject: [PATCH 20/20] try/catch around the commands json for js --- docs/agents/openclaw-gateway.mdx | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/docs/agents/openclaw-gateway.mdx b/docs/agents/openclaw-gateway.mdx index 819504a0..f04d65c6 100644 --- a/docs/agents/openclaw-gateway.mdx +++ b/docs/agents/openclaw-gateway.mdx @@ -95,18 +95,20 @@ The gateway requires each browser to be approved as a paired device. Run this af // 4. Poll for the browser's pending device request and approve it for (let i = 0; i < 30; i++) { - const res = await sandbox.commands.run( - `openclaw devices list --json --url ws://127.0.0.1:${PORT} --token ${TOKEN}` - ) - const data = JSON.parse(res.stdout) - if (data.pending?.length) { - const rid = data.pending[0].requestId - await sandbox.commands.run( - `openclaw devices approve ${rid} --token ${TOKEN} --url ws://127.0.0.1:${PORT}` + try { + const res = await sandbox.commands.run( + `openclaw devices list --json --url ws://127.0.0.1:${PORT} --token ${TOKEN}` ) - console.log(`Device approved: ${rid}`) - break - } + const data = JSON.parse(res.stdout) + if (data.pending?.length) { + const rid = data.pending[0].requestId + await sandbox.commands.run( + `openclaw devices approve ${rid} --token ${TOKEN} --url ws://127.0.0.1:${PORT}` + ) + console.log(`Device approved: ${rid}`) + break + } + } catch {} await new Promise((r) => setTimeout(r, 2000)) } ```