Files
openclaw/docs/start/wizard-cli-automation.md
Peter Steinberger df2cc8f259 fix(onboard): honor secret-input-mode ref for the generated gateway token (#126877)
* fix(onboard): honor secret-input-mode ref for the generated gateway token

`openclaw onboard --secret-input-mode ref` was silently ignored for
`gateway.auth.token`: onboarding generated the token and wrote it into
`openclaw.json` as a plaintext string, so `openclaw doctor` warned about
`gateway.auth.token` on the install it had just created. The flag was
honored for provider credentials, so an operator who explicitly opted into
references still ended up with a plaintext secret and a remediation
(`openclaw secrets configure`) that cannot migrate a self-generated value,
because it validates a ref by resolving one that already exists.

Setup mints this token itself, so reference mode now provisions it:

- an ambient OPENCLAW_GATEWAY_TOKEN keeps an `env` ref to that variable, so a
  later rotation stays authoritative instead of being pinned by a stale copy
- anything else (freshly generated, or an existing plaintext token being
  migrated) goes into the shared SQLite secret store as a write-only `secret`
  entry, with config holding only `{source:"store",...}`

An existing store entry wins over a freshly generated one, so reruns never
rotate a token already paired with clients. The store write precedes the
config write: a ref persisted without its value would leave the gateway
unauthenticatable, while an orphaned entry is reused by the next run.

The interactive wizard had the same dead end and is fixed the same way.
Default (plaintext) onboarding is unchanged.

User impact: `--secret-input-mode ref` now keeps the gateway token out of
openclaw.json, and a fresh install no longer self-reports a plaintext-secret
warning.

* test(onboard): split gateway onboarding suite under the max-lines gate

The added gateway auth-token tests pushed
onboard-non-interactive.gateway.test.ts to 1014 lines, over the max-lines
limit (check-lint-core-3). Repo policy is to split, never suppress.

Extract the shared vi.mock/harness preamble into
onboard-non-interactive.gateway.test-mocks.ts, following the existing
agent-command.test-mocks.ts pattern, and move the four gateway auth-token
storage tests into their own suite. The reachability mock becomes a holder
object so both suites can swap it across the module boundary, and hoisted
mocks are re-exported in a separate export clause because Vitest rejects
exporting a vi.hoisted binding at its declaration.

Test set is unchanged: the it-declaration multiset matches the pre-split
file exactly, with no duplication across the two suites.

* test(onboard): give the shared gateway onboarding mocks unique export names

check-export-name-collisions flagged `runtime` and `readConfigFileSnapshotMock`
as colliding with program.test-mocks.ts and plugins-cli-test-helpers.ts once the
gateway onboarding preamble became a shared module. Rename the exports to
gatewayOnboardRuntime / gatewayOnboardConfigSnapshotMock per the repo's
unique-export-name rule; suites alias them locally so the assertions read the
same as before.

* test(tooling): route the new gateway auth-token suite from its test helper

test-projects asserts which suites a change to
onboard-non-interactive.test-helpers.ts should run. The new
onboard-non-interactive.gateway-auth-token.test.ts imports that helper, so it
belongs in the expected routing plan.
2026-08-20 17:46:59 -07:00

8.8 KiB

summary, read_when, title, sidebarTitle
summary read_when title sidebarTitle
Scripted onboarding and agent setup for the OpenClaw CLI
You are automating onboarding in scripts or CI
You need non-interactive examples for specific providers
CLI automation CLI automation

Use openclaw onboard --non-interactive to script setup. It requires --accept-risk: non-interactive setup can write credentials and daemon config without a confirmation prompt, so the flag is the explicit risk acknowledgement.

Each command can install a managed Gateway with --install-daemon, require an already-running compatible Gateway by omitting daemon flags, explicitly leave the Gateway stopped with --skip-daemon, or use --skip-health for config-only setup. The explicit skip still probes for an existing Gateway and reports whether one is reachable, but an absent listener is informational rather than a setup failure.

`--json` does not imply non-interactive mode. Pass `--non-interactive --accept-risk` explicitly for scripts.

Baseline non-interactive example

openclaw onboard --non-interactive --accept-risk \
  --mode local \
  --auth-choice apiKey \
  --anthropic-api-key "$ANTHROPIC_API_KEY" \
  --secret-input-mode plaintext \
  --gateway-bind loopback \
  --install-daemon \
  --daemon-runtime node \
  --skip-bootstrap \
  --skip-skills

Add --json for a machine-readable summary.

  • --gateway-port defaults to 18789; only pass it to override.
  • --skip-bootstrap skips creating default workspace files, for automation that pre-seeds its own workspace.
  • --secret-input-mode ref stores new credentials as env-backed references ({ source: "env", provider: "default", id: "<ENV_VAR>" }); set the provider env var when adding a credential or passing an inline key flag. Existing resolvable named profiles and their env, file, exec, or store references are reused unchanged, without a new credential write or additional provider env var. Existing plaintext is not migrated; run openclaw secrets configure --apply, then openclaw secrets audit --check. See Secrets management.
  • The gateway token follows the same mode. Setup generates that value itself, so reference mode has no env var to point at unless you supply one: with OPENCLAW_GATEWAY_TOKEN exported, gateway.auth.token becomes an env ref to it; otherwise the token goes into the SQLite secret store as OPENCLAW_GATEWAY_TOKEN and config keeps a store ref. Either way openclaw.json holds no plaintext gateway token. Inspect the entry with openclaw secrets store list.
openclaw onboard --non-interactive --accept-risk --skip-health \
  --mode local \
  --auth-choice openai-api-key \
  --secret-input-mode ref

Provider-specific examples

```bash openclaw onboard --non-interactive --accept-risk --skip-health \ --mode local \ --auth-choice apiKey \ --anthropic-api-key "$ANTHROPIC_API_KEY" \ --gateway-bind loopback ``` ```bash openclaw onboard --non-interactive --accept-risk --skip-health \ --mode local \ --auth-choice cloudflare-ai-gateway-api-key \ --cloudflare-ai-gateway-account-id "your-account-id" \ --cloudflare-ai-gateway-gateway-id "your-gateway-id" \ --cloudflare-ai-gateway-api-key "$CLOUDFLARE_AI_GATEWAY_API_KEY" \ --gateway-bind loopback ``` ```bash openclaw onboard --non-interactive --accept-risk --skip-health \ --mode local \ --auth-choice gemini-api-key \ --gemini-api-key "$GEMINI_API_KEY" \ --gateway-bind loopback ``` ```bash openclaw onboard --non-interactive --accept-risk --skip-health \ --mode local \ --auth-choice mistral-api-key \ --mistral-api-key "$MISTRAL_API_KEY" \ --gateway-bind loopback ``` ```bash openclaw onboard --non-interactive --accept-risk --skip-health \ --mode local \ --auth-choice moonshot-api-key \ --moonshot-api-key "$MOONSHOT_API_KEY" \ --gateway-bind loopback ``` ```bash openclaw onboard --non-interactive --accept-risk --skip-health \ --mode local \ --auth-choice ollama \ --custom-model-id "qwen3.5:27b" \ --gateway-bind loopback ``` ```bash openclaw onboard --non-interactive --accept-risk --skip-health \ --mode local \ --auth-choice opencode-zen \ --opencode-zen-api-key "$OPENCODE_API_KEY" \ --gateway-bind loopback ``` Swap to `--auth-choice opencode-go --opencode-go-api-key "$OPENCODE_API_KEY"` for the Go catalog. ```bash openclaw onboard --non-interactive --accept-risk --skip-health \ --mode local \ --auth-choice synthetic-api-key \ --synthetic-api-key "$SYNTHETIC_API_KEY" \ --gateway-bind loopback ``` ```bash openclaw onboard --non-interactive --accept-risk --skip-health \ --mode local \ --auth-choice ai-gateway-api-key \ --ai-gateway-api-key "$AI_GATEWAY_API_KEY" \ --gateway-bind loopback ``` ```bash openclaw onboard --non-interactive --accept-risk --skip-health \ --mode local \ --auth-choice zai-api-key \ --zai-api-key "$ZAI_API_KEY" \ --gateway-bind loopback ``` ```bash openclaw onboard --non-interactive --accept-risk --skip-health \ --mode local \ --auth-choice custom-api-key \ --custom-base-url "https://llm.example.com/v1" \ --custom-model-id "foo-large" \ --custom-api-key "$CUSTOM_API_KEY" \ --custom-provider-id "my-custom" \ --custom-compatibility anthropic \ --custom-image-input \ --gateway-bind loopback ```
`--custom-api-key` is optional; some endpoints do not require auth. If omitted, onboarding checks `CUSTOM_API_KEY` in env. `--custom-provider-id` is optional and auto-derived from the base URL when omitted. `--custom-compatibility` defaults to `openai` (other values: `openai-responses`, `anthropic`).

OpenClaw infers image-input support from known vision model-id patterns (`gpt-4o`, `claude-3/4`, `gemini`, `-vl`/`vision` suffixes, and similar). Add `--custom-image-input` to force it on for an unrecognized vision model, or `--custom-text-input` to force text-only.

Ref-mode variant, storing `apiKey` as `{ source: "env", provider: "default", id: "CUSTOM_API_KEY" }`:

```bash
export CUSTOM_API_KEY="your-key"
openclaw onboard --non-interactive --accept-risk --skip-health \
  --mode local \
  --auth-choice custom-api-key \
  --custom-base-url "https://llm.example.com/v1" \
  --custom-model-id "foo-large" \
  --secret-input-mode ref \
  --custom-provider-id "my-custom" \
  --custom-compatibility anthropic \
  --custom-image-input \
  --gateway-bind loopback
```

Anthropic setup-token auth remains supported, but OpenClaw prefers Claude CLI reuse when a local Claude CLI login is available. For production, prefer an Anthropic API key.

Add another agent

openclaw agents add <name> creates a separate agent with its own workspace, sessions, and auth profiles. Running it without --workspace (and no other flags) launches the interactive wizard; passing any of --workspace, --model, --agent-dir, --bind, or --non-interactive runs it non-interactively and then requires --workspace.

openclaw agents add work \
  --workspace ~/.openclaw/workspace-work \
  --model openai/gpt-5.6-sol \
  --bind whatsapp:biz \
  --non-interactive \
  --json

Config keys it writes (agents.entries.* entry for the new agent id):

  • name
  • workspace
  • agentDir
  • model (only when --model is passed)

Notes:

  • Default workspace (when --workspace is omitted in the interactive wizard): ~/.openclaw/workspace-<agentId>.
  • --bind <channel[:accountId]> is repeatable; add bindings to route inbound messages to the new agent (the wizard can also do this interactively).
  • The agent name is normalized to a valid agent id. main is allowed, but an existing named installation may require openclaw doctor --fix to finish legacy-session and shared-auth ownership migrations before creating it.