assertNotRoot only checked process.getuid(), so the guard was bypassed
when the CLI was launched with a non-root real UID but an effective UID
of 0 (e.g. via a setuid-root wrapper). In that context the process still
has root write privileges and can cause the same state/config corruption
the guard was added to prevent.
Now checks both getuid() and geteuid() — either being 0 triggers the
guard. Added three tests covering setuid-root scenarios.
OPENCLAW_CLI_CONTAINER_BYPASS alone is an internal recursion sentinel,
not a user-facing opt-in. Require OPENCLAW_CONTAINER_HINT to also be
present — this combination only occurs in the container-forwarding flow
(container-target.ts), so inherited or accidental env vars can no
longer silently skip the root guard.
Remove the --help/--version exemption from the legacy entrypoint
(src/index.ts). Unlike src/entry.ts which has fast-path exits before
startup work, the legacy path always calls runCli() which runs dotenv
loading and debug capture initialization before rendering output. The
assertNotRoot() error message already shows the OPENCLAW_ALLOW_ROOT=1
escape hatch, so users can still discover the override.
runLegacyCliEntry now calls assertNotRoot() before runCli, matching
the protection already present in src/entry.ts. Help and version
invocations are exempted so users can still discover OPENCLAW_ALLOW_ROOT.
The container forwarder sets OPENCLAW_CLI_CONTAINER_BYPASS=1 but not
OPENCLAW_ALLOW_ROOT. When the child CLI inside a root-based container
hits assertNotRoot(), it would exit before command handling. Exempt
container-forwarded invocations from the root guard.
Replace dynamic import helper with a static import since root-guard.ts
has no module-level mutable state and vi.resetModules() is not used,
making the dynamic import unnecessary.
Block openclaw CLI from running as root (uid 0) to prevent:
- Separate state directory at /root/.openclaw/
- Conflicting systemd user services racing on port 18789
- Root-owned files in the service user state dir (EACCES)
The guard runs early in src/entry.ts before any state/config operations.
Root-level --help and --version bypass the guard so users can discover
the OPENCLAW_ALLOW_ROOT=1 override. Subcommand help paths still enforce
the guard since they enter runCli() and resolve state directories.
Closes#67478
Summary:
- Hide retired and non-public Google Gemini model IDs from Control UI/chat model catalogs.
- Route the bare gemini-3-pro alias to gemini-3.1-pro-preview.
- Keep models.list fallback rows filtered by manifest suppressions and update stale pricing-cache expectations.
Verification:
- pnpm test src/commands/models/list.list-command.forward-compat.test.ts src/commands/models/list.rows.test.ts extensions/google/manifest.test.ts extensions/google/model-id.test.ts extensions/google/provider-models.test.ts extensions/google/provider-policy-api.test.ts extensions/google/media-understanding-provider.video.test.ts src/plugin-sdk/provider-model-id-normalize.test.ts src/plugins/manifest-model-suppression.test.ts src/gateway/server-methods/models.test.ts ui/src/ui/chat-model-select-state.test.ts ui/src/ui/chat-model-ref.test.ts
- pnpm test src/gateway/model-pricing-cache.test.ts
- pnpm --silent openclaw models list --all --json --provider google / google-vertex hidden-row probe
- Testbox pnpm check:changed: https://github.com/openclaw/openclaw/actions/runs/25534551033
Defer OpenClaw Codex dynamic tools behind Codex tool_search, keep required turn-control tools direct, pin the managed Codex harness to 0.129.0-alpha.15, and document the real behavior/token impact from the live dev-agent watch.
## Summary
- Add catalog-backed repair hints for official external channel plugins.
- Show configured Feishu/WhatsApp-style external channels as missing-plugin warning rows in status surfaces.
- Keep installed-but-unconfigured, disabled, allowlist-denied, and untrusted plugins on their real activation/configuration error paths.
Fixes#78702Fixes#78593