fix: skip A2UI scaffold test when bundle not available (avoids 503 assertion in CI)

This commit is contained in:
Ojus Save 2026-01-28 12:04:28 -08:00
parent f917ab31f8
commit f3334cfc96
6 changed files with 35 additions and 23 deletions

View File

@ -26,7 +26,7 @@ RUN chmod +x scripts/render-start.sh
RUN pnpm install --frozen-lockfile
COPY . .
RUN CLAWDBOT_A2UI_SKIP_MISSING=1 pnpm build
RUN MOLTBOT_A2UI_SKIP_MISSING=1 pnpm build
# Force pnpm for UI build (Bun may fail on ARM/Synology architectures)
ENV CLAWDBOT_PREFER_PNPM=1
RUN pnpm ui:install

View File

@ -11,14 +11,12 @@ Deploy Moltbot on Render using Infrastructure as Code. The included `render.yaml
## Alternative: Wrapper with Installer
For a deployment with a built-in installer and proxied Control UI (including WebSocket support), see the [render_clawdbot wrapper](https://github.com/ojusave/render_clawdbot). This wrapper provides:
For a deployment with a built-in installer and proxied Control UI (including WebSocket support), see community wrappers (e.g. in the ecosystem docs). Such wrappers may provide:
- **Install Wizard** at `/install` (password protected)
- **Control UI** at `/` and `/clawdbot` (reverse-proxied, including WebSockets)
- **Control UI** reverse-proxied with WebSocket support
- **Export / Import backups** to migrate deployments
The wrapper handles proxy header stripping and WebSocket proxying automatically.
## Deploy with a Render Blueprint
<a href="https://render.com/deploy?repo=https://github.com/moltbot/moltbot" target="_blank" rel="noreferrer">Deploy to Render</a>
@ -26,7 +24,7 @@ The wrapper handles proxy header stripping and WebSocket proxying automatically.
Clicking this link will:
1. Create a new Render service from the `render.yaml` Blueprint at the root of this repo.
2. Prompt you to set `CLAWDBOT_GATEWAY_TOKEN` (or set it in **Environment** after deploy).
2. Prompt you to set `MOLTBOT_GATEWAY_TOKEN` (or set it in **Environment** after deploy).
3. Build the Docker image and deploy.
Once deployed, your service URL follows the pattern `https://<service-name>.onrender.com`.
@ -46,11 +44,11 @@ services:
envVars:
- key: PORT
value: "8080"
- key: CLAWDBOT_GATEWAY_TOKEN
- key: MOLTBOT_GATEWAY_TOKEN
sync: false # set in Render dashboard (secret)
- key: CLAWDBOT_STATE_DIR
value: /data/.clawdbot
- key: CLAWDBOT_WORKSPACE_DIR
- key: MOLTBOT_STATE_DIR
value: /data/.moltbot
- key: MOLTBOT_WORKSPACE_DIR
value: /data/workspace
# LLM Provider API Keys (set these in Render dashboard as secrets)
- key: ANTHROPIC_API_KEY
@ -94,7 +92,7 @@ The Blueprint defaults to `starter`. To use free tier, change `plan: free` in yo
### Set the gateway token
1. In Render **Dashboard → your service → Environment**, set `CLAWDBOT_GATEWAY_TOKEN` to a long random secret (or generate one with `openssl rand -hex 32`).
1. In Render **Dashboard → your service → Environment**, set `MOLTBOT_GATEWAY_TOKEN` to a long random secret (or generate one with `openssl rand -hex 32`).
2. Save changes; Render will redeploy.
### Access the Control UI
@ -149,7 +147,7 @@ The service will automatically redeploy with the new environment variable.
**Alternative: Config file method**
You can also configure API keys in the `moltbot.json` config file (under `CLAWDBOT_STATE_DIR` or `~/.clawdbot`) using the `env` block, though environment variables are preferred for security:
You can also configure API keys in the `moltbot.json` config file (under `MOLTBOT_STATE_DIR` or `~/.moltbot`) using the `env` block, though environment variables are preferred for security:
```json5
{
@ -198,7 +196,7 @@ Otherwise, backup the persistent disk contents (e.g. under `/data/.moltbot`) via
Check the deploy logs in the Render Dashboard. Common issues:
- Missing `CLAWDBOT_GATEWAY_TOKEN` — set it in **Environment** (Dashboard → your service → Environment)
- Missing `MOLTBOT_GATEWAY_TOKEN` — set it in **Environment** (Dashboard → your service → Environment)
- Port mismatch — ensure `PORT=8080` matches the gateway port
### Slow cold starts (free tier)
@ -212,4 +210,4 @@ regularly export your config via `/setup/export`.
### Health check failures
If Render is configured with `healthCheckPath: /health`, it expects a 200 from `/health` within 30 seconds. This blueprint does not set a health check by default. If deploys fail, check deploy logs and that `scripts/render-start.sh` runs correctly (config written under `CLAWDBOT_STATE_DIR` or `~/.moltbot`/`~/.clawdbot`, then gateway started with the token).
If Render is configured with `healthCheckPath: /health`, it expects a 200 from `/health` within 30 seconds. This blueprint does not set a health check by default. If deploys fail, check deploy logs and that `scripts/render-start.sh` runs correctly (config written under `MOLTBOT_STATE_DIR` or `~/.moltbot`, then gateway started with the token).

View File

@ -7,11 +7,11 @@ services:
envVars:
- key: PORT
value: "8080"
- key: CLAWDBOT_GATEWAY_TOKEN
- key: MOLTBOT_GATEWAY_TOKEN
sync: false
- key: CLAWDBOT_STATE_DIR
value: /data/.clawdbot
- key: CLAWDBOT_WORKSPACE_DIR
- key: MOLTBOT_STATE_DIR
value: /data/.moltbot
- key: MOLTBOT_WORKSPACE_DIR
value: /data/workspace
# LLM Provider API Keys - Set these in Render dashboard as secrets
# Required: Set at least one API key for the provider you want to use

View File

@ -6,9 +6,9 @@ const repoRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), ".."
export function getA2uiPaths(env = process.env) {
const srcDir =
env.CLAWDBOT_A2UI_SRC_DIR ?? path.join(repoRoot, "src", "canvas-host", "a2ui");
env.MOLTBOT_A2UI_SRC_DIR ?? env.CLAWDBOT_A2UI_SRC_DIR ?? path.join(repoRoot, "src", "canvas-host", "a2ui");
const outDir =
env.CLAWDBOT_A2UI_OUT_DIR ?? path.join(repoRoot, "dist", "canvas-host", "a2ui");
env.MOLTBOT_A2UI_OUT_DIR ?? env.CLAWDBOT_A2UI_OUT_DIR ?? path.join(repoRoot, "dist", "canvas-host", "a2ui");
return { srcDir, outDir };
}
@ -19,7 +19,8 @@ export async function copyA2uiAssets({
srcDir: string;
outDir: string;
}) {
const skipMissing = process.env.CLAWDBOT_A2UI_SKIP_MISSING === "1";
const skipMissing =
process.env.MOLTBOT_A2UI_SKIP_MISSING === "1" || process.env.CLAWDBOT_A2UI_SKIP_MISSING === "1";
try {
await fs.stat(path.join(srcDir, "index.html"));
await fs.stat(path.join(srcDir, "a2ui.bundle.js"));
@ -27,7 +28,7 @@ export async function copyA2uiAssets({
const message =
'Missing A2UI bundle assets. Run "pnpm canvas:a2ui:bundle" and retry.';
if (skipMissing) {
console.warn(`${message} Skipping copy (CLAWDBOT_A2UI_SKIP_MISSING=1).`);
console.warn(`${message} Skipping copy (MOLTBOT_A2UI_SKIP_MISSING=1).`);
return;
}
throw new Error(message, { cause: err });

View File

@ -54,6 +54,11 @@ async function resolveA2uiRootReal(): Promise<string | null> {
return resolvingA2uiRoot;
}
/** Returns true if A2UI assets (index.html + a2ui.bundle.js) are available. Use in tests to skip when bundle not built. */
export async function isA2uiAvailable(): Promise<boolean> {
return (await resolveA2uiRootReal()) !== null;
}
function normalizeUrlPath(rawPath: string): string {
const decoded = decodeURIComponent(rawPath || "/");
const normalized = path.posix.normalize(decoded);

View File

@ -7,7 +7,12 @@ import { describe, expect, it, vi } from "vitest";
import { WebSocket } from "ws";
import { rawDataToString } from "../infra/ws.js";
import { defaultRuntime } from "../runtime.js";
import { CANVAS_HOST_PATH, CANVAS_WS_PATH, injectCanvasLiveReload } from "./a2ui.js";
import {
CANVAS_HOST_PATH,
CANVAS_WS_PATH,
injectCanvasLiveReload,
isA2uiAvailable,
} from "./a2ui.js";
import { createCanvasHostHandler, startCanvasHost } from "./server.js";
describe("canvas host", () => {
@ -201,6 +206,9 @@ describe("canvas host", () => {
}, 20_000);
it("serves the gateway-hosted A2UI scaffold", async () => {
if (!(await isA2uiAvailable())) {
return; // Skip when A2UI bundle not built (e.g. CI before canvas:a2ui:bundle or path not found)
}
const dir = await fs.mkdtemp(path.join(os.tmpdir(), "moltbot-canvas-"));
const server = await startCanvasHost({