From 7752e3b30f10208a7422061e98b232c07735bc0a Mon Sep 17 00:00:00 2001 From: Patrick Erichsen Date: Mon, 20 Apr 2026 19:38:46 -0700 Subject: [PATCH] onboard: clearer security disclaimer, loading spinners, api key placeholder --- CHANGELOG.md | 1 + src/flows/model-picker.ts | 17 +++++++++++-- src/plugins/provider-auth-input.ts | 1 + src/terminal/note.ts | 1 + src/wizard/setup.security-note.ts | 40 ++++++++++++++++++++++++++++++ src/wizard/setup.ts | 40 ++++++------------------------ 6 files changed, 65 insertions(+), 35 deletions(-) create mode 100644 src/wizard/setup.security-note.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index a454525fcdd..a6d143d8fdb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,7 @@ Docs: https://docs.openclaw.ai ### Changes +- Onboard/wizard: restyle the setup security disclaimer with a single yellow warning banner, section headings and bulleted checklists, and un-dim the note body so key guidance is easy to scan; add a loading spinner during the initial model catalog load so the wizard no longer goes blank while it runs; add an "API key" placeholder to provider API key prompts. - Agents/prompts: strengthen the default system prompt and OpenAI GPT-5 overlay with clearer completion bias, live-state checks, weak-result recovery, and verification-before-final guidance. - Models/costs: support tiered model pricing from cached catalogs and configured models, and include bundled Moonshot Kimi K2.6/K2.5 cost estimates for token-usage reports. (#67605) Thanks @sliverp. - Sessions/Maintenance: enforce the built-in entry cap and age prune by default, and prune oversized stores at load time so accumulated cron/executor session backlogs cannot OOM the gateway before the write path runs. (#69404) Thanks @bobrenze-bot. diff --git a/src/flows/model-picker.ts b/src/flows/model-picker.ts index 6a9095f7935..a0891c1a483 100644 --- a/src/flows/model-picker.ts +++ b/src/flows/model-picker.ts @@ -427,7 +427,13 @@ export async function promptDefaultModel( const resolvedKey = modelKey(resolved.provider, resolved.model); const configuredKey = configuredRaw ? resolvedKey : ""; - const catalog = await loadModelCatalog({ config: cfg, useCache: false }); + const catalogProgress = params.prompter.progress("Loading available models"); + let catalog: Awaited>; + try { + catalog = await loadModelCatalog({ config: cfg, useCache: false }); + } finally { + catalogProgress.stop(); + } if (catalog.length === 0) { return promptManualModel({ prompter: params.prompter, @@ -532,6 +538,7 @@ export async function promptDefaultModel( message: params.message ?? "Default model", options, initialValue, + searchable: true, }); const selectedValue = selection ?? ""; if (selectedValue === KEEP_VALUE) { @@ -603,7 +610,13 @@ export async function promptModelAllowlist(params: { ? initialSeeds.filter((key) => allowedKeySet.has(key)) : initialSeeds; - const catalog = await loadModelCatalog({ config: cfg, useCache: false }); + const allowlistProgress = params.prompter.progress("Loading available models"); + let catalog: Awaited>; + try { + catalog = await loadModelCatalog({ config: cfg, useCache: false }); + } finally { + allowlistProgress.stop(); + } if (catalog.length === 0 && allowedKeys.length === 0) { const raw = await params.prompter.text({ message: diff --git a/src/plugins/provider-auth-input.ts b/src/plugins/provider-auth-input.ts index 14f920bb294..9a587a720b8 100644 --- a/src/plugins/provider-auth-input.ts +++ b/src/plugins/provider-auth-input.ts @@ -214,6 +214,7 @@ export async function ensureApiKeyFromEnvOrPrompt(params: { const key = await params.prompter.text({ message: params.promptMessage, + placeholder: "API key", validate: params.validate, }); const apiKey = params.normalize(key ?? ""); diff --git a/src/terminal/note.ts b/src/terminal/note.ts index 0978f32770c..81d38fde18c 100644 --- a/src/terminal/note.ts +++ b/src/terminal/note.ts @@ -186,5 +186,6 @@ export function note(message: string, title?: string) { const columns = resolveNoteColumns(process.stdout.columns); clackNote(wrapNoteMessage(message, { columns }), stylePromptTitle(title), { output: createNoteOutput(columns), + format: (line) => line, }); } diff --git a/src/wizard/setup.security-note.ts b/src/wizard/setup.security-note.ts new file mode 100644 index 00000000000..26fe2d434f3 --- /dev/null +++ b/src/wizard/setup.security-note.ts @@ -0,0 +1,40 @@ +import chalk from "chalk"; +import { formatCliCommand } from "../cli/command-format.js"; +import { theme } from "../terminal/theme.js"; + +export const SECURITY_NOTE_TITLE = "Security disclaimer"; + +export const SECURITY_CONFIRM_MESSAGE = + "I understand this is personal-by-default and shared/multi-user use requires lock-down. Continue?"; + +const heading = (text: string) => chalk.bold(text); + +export const SECURITY_NOTE_MESSAGE = [ + theme.warn("⚠ OpenClaw is in Beta - expect sharp edges"), + "- By default, OpenClaw is a personal agent: one trusted operator boundary.", + "- This bot can read files and run actions if tools are enabled.", + "- A bad prompt can trick it into doing unsafe things.", + "", + heading("How OpenClaw treats trust"), + "- OpenClaw is not a hostile multi-tenant boundary by default.", + "- If multiple users can message one tool-enabled agent, they share that delegated tool authority.", + "", + heading("When not to run OpenClaw"), + "- If you’re not comfortable with security hardening and access control, don’t run OpenClaw.", + "- Ask someone experienced to help before enabling tools or exposing it to the internet.", + "", + heading("Recommended baseline"), + "- Pairing/allowlists + mention gating.", + "- Multi-user/shared inbox: split trust boundaries (separate gateway/credentials, ideally separate OS users/hosts).", + "- Sandbox + least-privilege tools.", + "- Shared inboxes: isolate DM sessions (session.dmScope: per-channel-peer) and keep tool access minimal.", + "- Keep secrets out of the agent’s reachable filesystem.", + "- Use the strongest available model for any bot with tools or untrusted inboxes.", + "", + heading("Run regularly"), + formatCliCommand("openclaw security audit --deep"), + formatCliCommand("openclaw security audit --fix"), + "", + heading("Learn more"), + "- https://docs.openclaw.ai/gateway/security", +].join("\n"); diff --git a/src/wizard/setup.ts b/src/wizard/setup.ts index 158d670d8d9..ec5b0e848a8 100644 --- a/src/wizard/setup.ts +++ b/src/wizard/setup.ts @@ -19,6 +19,11 @@ import { defaultRuntime } from "../runtime.js"; import { resolveUserPath } from "../utils.js"; import { WizardCancelledError, type WizardPrompter } from "./prompts.js"; import { resolveSetupSecretInputString } from "./setup.secret-input.js"; +import { + SECURITY_CONFIRM_MESSAGE, + SECURITY_NOTE_MESSAGE, + SECURITY_NOTE_TITLE, +} from "./setup.security-note.js"; import type { QuickstartGatewayDefaults, WizardFlow } from "./setup.types.js"; type AuthChoiceModule = typeof import("../commands/auth-choice.js"); @@ -108,41 +113,10 @@ async function requireRiskAcknowledgement(params: { return; } - await params.prompter.note( - [ - "Security warning — please read.", - "", - "OpenClaw is a hobby project and still in beta. Expect sharp edges.", - "By default, OpenClaw is a personal agent: one trusted operator boundary.", - "This bot can read files and run actions if tools are enabled.", - "A bad prompt can trick it into doing unsafe things.", - "", - "OpenClaw is not a hostile multi-tenant boundary by default.", - "If multiple users can message one tool-enabled agent, they share that delegated tool authority.", - "", - "If you’re not comfortable with security hardening and access control, don’t run OpenClaw.", - "Ask someone experienced to help before enabling tools or exposing it to the internet.", - "", - "Recommended baseline:", - "- Pairing/allowlists + mention gating.", - "- Multi-user/shared inbox: split trust boundaries (separate gateway/credentials, ideally separate OS users/hosts).", - "- Sandbox + least-privilege tools.", - "- Shared inboxes: isolate DM sessions (`session.dmScope: per-channel-peer`) and keep tool access minimal.", - "- Keep secrets out of the agent’s reachable filesystem.", - "- Use the strongest available model for any bot with tools or untrusted inboxes.", - "", - "Run regularly:", - "openclaw security audit --deep", - "openclaw security audit --fix", - "", - "Must read: https://docs.openclaw.ai/gateway/security", - ].join("\n"), - "Security", - ); + await params.prompter.note(SECURITY_NOTE_MESSAGE, SECURITY_NOTE_TITLE); const ok = await params.prompter.confirm({ - message: - "I understand this is personal-by-default and shared/multi-user use requires lock-down. Continue?", + message: SECURITY_CONFIRM_MESSAGE, initialValue: false, }); if (!ok) {