- Apply normalizeEnvVarKey({ portable: true }) before security
filtering, matching the established pattern in env-vars.ts.
Rejects non-portable key names (spaces, special chars) that
would produce invalid plist/systemd syntax.
- Isolate existing tests from the developer's real ~/.openclaw/.env
by providing a temp HOME directory, preventing flaky failures
when the test machine has a populated .env file.
When building the gateway install plan, read and parse
~/.openclaw/.env (or $OPENCLAW_STATE_DIR/.env) and merge those
key-value pairs into the service environment at the lowest
priority — below config env vars, auth-profile refs, and the
core service environment (HOME, PATH, OPENCLAW_*).
This ensures that user-defined secrets stored in .env (e.g.
BRAVE_API_KEY, OPENROUTER_API_KEY, DISCORD_BOT_TOKEN) are
embedded in the LaunchAgent plist (macOS), systemd unit (Linux),
and Scheduled Task (Windows) at install time, rather than
relying solely on the gateway process loading them via
dotenv.config() at startup.
Previously, on macOS the LaunchAgent plist never included .env
vars, which meant:
- launchctl print did not show user secrets (hard to debug)
- Child processes spawned before dotenv loaded had no access
- If the same key existed in both .env and the plist, the stale
plist value won via dotenv override:false semantics
Dangerous host env vars (NODE_OPTIONS, LD_PRELOAD, etc.) are
filtered using the same security policy applied to config env
vars.
Fixes#37101
Relates to #22663
When an exec command fails (e.g. timeout), the tool previously rejected
with an Error, which the tool adapter caught and wrapped in a JSON object
({ status, tool, error }). The model then received this raw JSON as the
tool result and could parrot it verbatim to the user.
Now exec failures resolve with a proper tool result containing the error
as human-readable text in content[], matching the success path structure.
The model sees plain text it can naturally incorporate into its reply.
Also fixes a pre-existing format issue in update-cli.test.ts.
Fixes#52484
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Function is now async after switching to resolveGatewayProbeAuthSafeWithSecretInputs.
Missing await caused TS error: Property 'auth' does not exist on type 'Promise<...>'.
Address two Codex P1/P2 issues:
1. (P1) Plain 'openclaw status' and 'openclaw status --json' still went
through the sync resolveGatewayProbeAuthSafe path in
status.gateway-probe.ts, which cannot expand SecretRef objects.
Switched to async resolveGatewayProbeAuthSafeWithSecretInputs.
2. (P2) status-all.ts was eagerly resolving both local and remote probe
auth before deciding which to use. A stale SecretRef in the unused
branch could abort the command. Collapsed to a single resolution
call using the correct mode upfront.
Updated status.scan.test.ts to use mockResolvedValue since
resolveGatewayProbeAuthResolution is now async.
Fixes#52360
resolveGatewayProbeAuthSafe was called from status-all.ts without an
env argument, causing the credential resolution chain to fall back to
an empty object instead of process.env. This made env-backed SecretRef
tokens (gateway.auth.token, Telegram botToken, etc.) appear unresolved
in the status command path even when the runtime was healthy.
Added process.env as default fallback in buildGatewayProbeCredentialPolicy
and passed env explicitly from status-all.ts callers.
Related: #33070, #38973, #39415, #46014, #49730