From e45b9d7a748bfe397a0ea7fd51ea46c11297218c Mon Sep 17 00:00:00 2001 From: Vincent Koc Date: Sat, 9 May 2026 09:22:04 +0800 Subject: [PATCH] fix(cli): clarify remaining required options --- src/cli/acp-cli.ts | 5 +++-- src/cli/cron-cli/register.cron-add.ts | 2 +- src/cli/cron-cli/schedule-options.ts | 4 ++-- src/cli/daemon-cli/status.ts | 4 +++- src/cli/devices-cli.ts | 9 +++++++-- src/cli/nodes-cli/register.pairing.ts | 9 +++++++-- src/cli/system-cli.ts | 5 ++++- src/cli/webhooks-cli.ts | 5 ++++- src/commands/models/auth.ts | 4 +++- .../onboard-non-interactive/local/gateway-config.ts | 4 +++- src/commands/onboard-non-interactive/remote.ts | 4 +++- 11 files changed, 40 insertions(+), 15 deletions(-) diff --git a/src/cli/acp-cli.ts b/src/cli/acp-cli.ts index ce5bd9efa5b..44390dfc128 100644 --- a/src/cli/acp-cli.ts +++ b/src/cli/acp-cli.ts @@ -2,6 +2,7 @@ import type { Command } from "commander"; import { runAcpClientInteractive } from "../acp/client.js"; import { serveAcpGateway } from "../acp/server.js"; import { normalizeAcpProvenanceMode } from "../acp/types.js"; +import { formatErrorMessage } from "../infra/errors.js"; import { defaultRuntime } from "../runtime.js"; import { formatDocsLink } from "../terminal/links.js"; import { theme } from "../terminal/theme.js"; @@ -33,7 +34,7 @@ export function registerAcpCli(program: Command) { const { gatewayToken, gatewayPassword } = resolveGatewayAuthOptions(opts); const provenanceMode = normalizeAcpProvenanceMode(opts.provenance as string | undefined); if (opts.provenance && !provenanceMode) { - throw new Error("Invalid --provenance value. Use off, meta, or meta+receipt."); + throw new Error('Invalid --provenance. Use "off", "meta", or "meta+receipt".'); } await serveAcpGateway({ gatewayUrl: opts.url as string | undefined, @@ -48,7 +49,7 @@ export function registerAcpCli(program: Command) { verbose: Boolean(opts.verbose), }); } catch (err) { - defaultRuntime.error(String(err)); + defaultRuntime.error(`ACP bridge failed: ${formatErrorMessage(err)}`); defaultRuntime.exit(1); } }); diff --git a/src/cli/cron-cli/register.cron-add.ts b/src/cli/cron-cli/register.cron-add.ts index 7040011582d..e5b4e38323c 100644 --- a/src/cli/cron-cli/register.cron-add.ts +++ b/src/cli/cron-cli/register.cron-add.ts @@ -232,7 +232,7 @@ export function registerCronAddCommand(cron: Command) { const name = normalizeOptionalString(opts.name) ?? ""; if (!name) { - throw new Error("--name is required"); + throw new Error("Cron job name is required. Pass --name ."); } const description = normalizeOptionalString(opts.description); diff --git a/src/cli/cron-cli/schedule-options.ts b/src/cli/cron-cli/schedule-options.ts index d8c1f63da07..3f3fde68bc5 100644 --- a/src/cli/cron-cli/schedule-options.ts +++ b/src/cli/cron-cli/schedule-options.ts @@ -104,14 +104,14 @@ function resolveDirectSchedule(options: NormalizedScheduleOptions): CronSchedule if (options.at) { const atIso = parseAt(options.at, options.tz); if (!atIso) { - throw new Error("Invalid --at; use ISO time or duration like 20m"); + throw new Error("Invalid --at. Use an ISO timestamp or a duration like 20m."); } return { kind: "at", at: atIso }; } if (options.every) { const everyMs = parseDurationMs(options.every); if (!everyMs) { - throw new Error("Invalid --every; use e.g. 10m, 1h, 1d"); + throw new Error("Invalid --every. Use a duration like 10m, 1h, or 1d."); } return { kind: "every", everyMs }; } diff --git a/src/cli/daemon-cli/status.ts b/src/cli/daemon-cli/status.ts index 5092d1185c8..cf510aa1e49 100644 --- a/src/cli/daemon-cli/status.ts +++ b/src/cli/daemon-cli/status.ts @@ -7,7 +7,9 @@ import type { DaemonStatusOptions } from "./types.js"; export async function runDaemonStatus(opts: DaemonStatusOptions) { try { if (opts.requireRpc && !opts.probe) { - defaultRuntime.error("Gateway status failed: --require-rpc cannot be used with --no-probe."); + defaultRuntime.error( + "Gateway status failed: --require-rpc needs probing enabled. Remove --no-probe or drop --require-rpc.", + ); defaultRuntime.exit(1); return; } diff --git a/src/cli/devices-cli.ts b/src/cli/devices-cli.ts index e1a13a8a4c0..3dd3713da8d 100644 --- a/src/cli/devices-cli.ts +++ b/src/cli/devices-cli.ts @@ -30,6 +30,7 @@ import { import { sanitizeForLog } from "../terminal/ansi.js"; import { getTerminalTableWidth, renderTable } from "../terminal/table.js"; import { theme } from "../terminal/theme.js"; +import { formatCliCommand } from "./command-format.js"; import { applyParentDefaultHelpAction } from "./program/parent-default-help.js"; import { withProgress } from "./progress.js"; @@ -509,7 +510,9 @@ function resolveRequiredDeviceRole( if (deviceId && role) { return { deviceId, role }; } - defaultRuntime.error("--device and --role required"); + defaultRuntime.error( + `--device and --role are required. Run ${formatCliCommand("openclaw devices list")} to choose a paired device.`, + ); defaultRuntime.exit(1); return null; } @@ -608,7 +611,9 @@ export function registerDevicesCli(program: Command) { .action(async (deviceId: string, opts: DevicesRpcOpts) => { const trimmed = deviceId.trim(); if (!trimmed) { - defaultRuntime.error("deviceId is required"); + defaultRuntime.error( + `deviceId is required. Run ${formatCliCommand("openclaw devices list")} to choose a paired device.`, + ); defaultRuntime.exit(1); return; } diff --git a/src/cli/nodes-cli/register.pairing.ts b/src/cli/nodes-cli/register.pairing.ts index 4ca09992460..53dec5836d2 100644 --- a/src/cli/nodes-cli/register.pairing.ts +++ b/src/cli/nodes-cli/register.pairing.ts @@ -2,6 +2,7 @@ import type { Command } from "commander"; import { defaultRuntime } from "../../runtime.js"; import { normalizeOptionalString } from "../../shared/string-coerce.js"; import { getTerminalTableWidth } from "../../terminal/table.js"; +import { formatCliCommand } from "../command-format.js"; import { getNodesTheme, runNodesCommand } from "./cli-utils.js"; import { parsePairingList } from "./format.js"; import { renderPendingPairingRequestsTable } from "./pairing-render.js"; @@ -80,7 +81,9 @@ export function registerNodesPairingCommands(nodes: Command) { await runNodesCommand("remove", async () => { const nodeId = await resolveNodeId(opts, normalizeOptionalString(opts.node) ?? ""); if (!nodeId) { - defaultRuntime.error("--node required"); + defaultRuntime.error( + `--node is required. Run ${formatCliCommand("openclaw nodes pairing pending")} to choose a node request.`, + ); defaultRuntime.exit(1); return; } @@ -106,7 +109,9 @@ export function registerNodesPairingCommands(nodes: Command) { const nodeId = await resolveNodeId(opts, normalizeOptionalString(opts.node) ?? ""); const name = normalizeOptionalString(opts.name) ?? ""; if (!nodeId || !name) { - defaultRuntime.error("--node and --name required"); + defaultRuntime.error( + `--node and --name are required. Run ${formatCliCommand("openclaw nodes pairing pending")} to choose a node, then rerun with --name .`, + ); defaultRuntime.exit(1); return; } diff --git a/src/cli/system-cli.ts b/src/cli/system-cli.ts index 60b22cc6cd1..50895218651 100644 --- a/src/cli/system-cli.ts +++ b/src/cli/system-cli.ts @@ -4,6 +4,7 @@ import { defaultRuntime } from "../runtime.js"; import { normalizeOptionalString } from "../shared/string-coerce.js"; import { formatDocsLink } from "../terminal/links.js"; import { theme } from "../terminal/theme.js"; +import { formatCliCommand } from "./command-format.js"; import type { GatewayRpcOpts } from "./gateway-rpc.js"; import { addGatewayClientOptions, callGatewayFromCli } from "./gateway-rpc.js"; @@ -62,7 +63,9 @@ export function registerSystemCli(program: Command) { async () => { const text = normalizeOptionalString(opts.text) ?? ""; if (!text) { - throw new Error("--text is required"); + throw new Error( + `--text is required. Example: ${formatCliCommand('openclaw system event --text "deploy finished"')}.`, + ); } const mode = normalizeWakeMode(opts.mode); return await callGatewayFromCli("wake", opts, { mode, text }, { expectFinal: false }); diff --git a/src/cli/webhooks-cli.ts b/src/cli/webhooks-cli.ts index df464c49b3c..ba608228ffa 100644 --- a/src/cli/webhooks-cli.ts +++ b/src/cli/webhooks-cli.ts @@ -20,6 +20,7 @@ import { defaultRuntime } from "../runtime.js"; import { normalizeOptionalString } from "../shared/string-coerce.js"; import { formatDocsLink } from "../terminal/links.js"; import { theme } from "../terminal/theme.js"; +import { formatCliCommand } from "./command-format.js"; export function registerWebhooksCli(program: Command) { const webhooks = program @@ -109,7 +110,9 @@ function parseGmailSetupOptions(raw: Record): GmailSetupOptions const accountRaw = raw.account; const account = normalizeOptionalString(accountRaw) ?? ""; if (!account) { - throw new Error("--account is required"); + throw new Error( + `--account is required. Example: ${formatCliCommand("openclaw webhooks gmail setup --account default")}.`, + ); } const common = parseGmailCommonOptions(raw); return { diff --git a/src/commands/models/auth.ts b/src/commands/models/auth.ts index 5665eebfc9d..9c9fe052616 100644 --- a/src/commands/models/auth.ts +++ b/src/commands/models/auth.ts @@ -413,7 +413,9 @@ export async function modelsAuthPasteTokenCommand( const agentDir = await resolveModelsAuthAgentDir(opts.agent); const rawProvider = normalizeOptionalString(opts.provider); if (!rawProvider) { - throw new Error("Missing --provider."); + throw new Error( + `Missing --provider. Run ${formatCliCommand("openclaw models status")} or ${formatCliCommand("openclaw plugins list")} to choose a provider.`, + ); } const provider = normalizeProviderId(rawProvider); const profileId = diff --git a/src/commands/onboard-non-interactive/local/gateway-config.ts b/src/commands/onboard-non-interactive/local/gateway-config.ts index cdc4d09c5b3..4d7e79778cc 100644 --- a/src/commands/onboard-non-interactive/local/gateway-config.ts +++ b/src/commands/onboard-non-interactive/local/gateway-config.ts @@ -146,7 +146,9 @@ export function applyNonInteractiveGatewayConfig(params: { if (authMode === "password") { const password = opts.gatewayPassword?.trim(); if (!password) { - runtime.error("Missing --gateway-password for password auth."); + runtime.error( + "Missing --gateway-password for password auth. Pass --gateway-password or use --gateway-auth token.", + ); runtime.exit(1); return null; } diff --git a/src/commands/onboard-non-interactive/remote.ts b/src/commands/onboard-non-interactive/remote.ts index ec3a662dd20..f09a2560c96 100644 --- a/src/commands/onboard-non-interactive/remote.ts +++ b/src/commands/onboard-non-interactive/remote.ts @@ -19,7 +19,9 @@ export async function runNonInteractiveRemoteSetup(params: { const remoteUrl = normalizeOptionalString(opts.remoteUrl); if (!remoteUrl) { - runtime.error("Missing --remote-url for remote mode."); + runtime.error( + `Missing --remote-url for remote mode. Example: ${formatCliCommand("openclaw onboard --non-interactive --mode remote --remote-url ws://127.0.0.1:3000")}.`, + ); runtime.exit(1); return; }