diff --git a/packages/cli/src/commands/doctor.ts b/packages/cli/src/commands/doctor.ts index d949a9d5f..4d40bc348 100644 --- a/packages/cli/src/commands/doctor.ts +++ b/packages/cli/src/commands/doctor.ts @@ -5,7 +5,11 @@ import chalk from "chalk"; import postgres from "postgres"; import { checkMemoryHealth } from "./memory/_lib/openclaw-cmd.js"; import { resolveServerUrl } from "./memory/_lib/openclaw-auth.js"; -import { isPortFree } from "./dev.js"; +import { + isExternalDatabaseUrl, + isPortFree, + resolveEmbeddedDataRoot, +} from "./dev.js"; import { parseEnvContent } from "../internal/env-file.js"; import { loadProviderRegistry } from "./providers/registry.js"; import { loadProjectConfig } from "./_lib/apply/desired-state.js"; @@ -203,8 +207,21 @@ export async function doctorCommand( checks.push(checkBinaryExists("git")); const databaseUrl = env.DATABASE_URL ?? process.env.DATABASE_URL; - if (databaseUrl) { + if (databaseUrl && isExternalDatabaseUrl(databaseUrl)) { checks.push(...(await checkDatabaseAndPgvector(databaseUrl))); + } else if (databaseUrl) { + // Embedded Postgres: DATABASE_URL is a filesystem path (often `file://`, + // the scaffold default `file://.`), not a connection string. `lobu run` + // boots a self-contained PG18 + bundled pgvector under `/.lobu/pgdata`, + // so there is nothing to dial until it's running. Report the resolved data + // root instead of feeding the path to postgres() — `postgres("file://.")` + // parses host "." and fails with `getaddrinfo ENOTFOUND .`. + const root = resolveEmbeddedDataRoot(databaseUrl); + checks.push({ + name: "database", + status: "ok", + detail: `local embedded Postgres (data: ${join(root, ".lobu", "pgdata")})`, + }); } else { checks.push({ name: "database", diff --git a/packages/server/src/dev-vite.ts b/packages/server/src/dev-vite.ts index 2156d3e8a..bea38bd25 100644 --- a/packages/server/src/dev-vite.ts +++ b/packages/server/src/dev-vite.ts @@ -54,6 +54,19 @@ export async function mountViteDev( honoListener: HttpListener ): Promise<{ close: () => Promise } | null> { if (process.env.NODE_ENV !== 'development') return null; + // A bundled CLI (`lobu run` from an npm install) ships the prebuilt SPA and + // points WEB_DIST_DIR at it; the Hono app serves that statically. There is no + // SPA *source* to run Vite against — and no HMR wanted for a prebuilt run — so + // skip Vite entirely. Otherwise we'd probe for `packages/owletto`, fail, and + // log a misleading "frontend will not be available" error even though the + // frontend is in fact served from the bundle. + if (process.env.WEB_DIST_DIR?.trim()) { + logger.info( + { webDistDir: process.env.WEB_DIST_DIR.trim() }, + 'Serving prebuilt SPA from WEB_DIST_DIR — skipping Vite dev server' + ); + return null; + } try { const { createServer } = await import('vite'); const vite = await createServer({ diff --git a/scripts/sdk-e2e.sh b/scripts/sdk-e2e.sh index 370cab9b6..5acc0485f 100755 --- a/scripts/sdk-e2e.sh +++ b/scripts/sdk-e2e.sh @@ -205,6 +205,28 @@ TS export LOBU_PROVIDER_REGISTRY_PATH="$HARNESS/providers.json" +# 2c) Static CLI checks (no server needed): the typed-config validator and the +# doctor health check. doctor must NOT false-fail the DB check on the scaffold's +# embedded `DATABASE_URL=file://.` — it once fed that path straight to +# postgres(), which parses host "." and dies with `getaddrinfo ENOTFOUND .`. +# We assert the embedded backend is recognized and that no connect error is +# printed. doctor's own exit code is ignored: the gateway isn't up yet, so its +# "server unreachable" check is expected to trip independently of the DB line. +VALIDATE_OUT="$RUN_DIR/validate.out" +( cd "$PROJ" && $LOBU validate > "$VALIDATE_OUT" 2>&1 ) || { cat "$VALIDATE_OUT" >&2; fail "lobu validate failed on the fixture config"; } +grep -qi "is valid" "$VALIDATE_OUT" || { cat "$VALIDATE_OUT" >&2; fail "lobu validate did not report the config valid"; } +echo "✓ lobu validate accepts the fixture config" + +DOCTOR_OUT="$RUN_DIR/doctor.out" +( cd "$PROJ" && $LOBU doctor > "$DOCTOR_OUT" 2>&1 ) || true # non-zero ok (gateway not up yet) +if grep -qiE "connect failed|ENOTFOUND" "$DOCTOR_OUT"; then cat "$DOCTOR_OUT" >&2; fail "lobu doctor false-failed the DB check (embedded file:// fed to postgres())"; fi +# The embedded-recognition message only applies when running against embedded PG +# (the default). With an external DATABASE_URL, doctor connects for real instead. +if [ -z "${DATABASE_URL:-}" ]; then + grep -qi "embedded Postgres" "$DOCTOR_OUT" || { cat "$DOCTOR_OUT" >&2; fail "lobu doctor did not recognize the embedded Postgres backend"; } +fi +echo "✓ lobu doctor reports a healthy DB (no false connect failure on embedded file://)" + # 3) Boot lobu run — it auto-applies the project (the apply + prune E2E). ( cd "$PROJ" && $LOBU run --port "$GW_PORT" > "$RUN_LOG" 2>&1 ) & for _ in $(seq 1 80); do