refactor: dedupe cli lowercase helpers

This commit is contained in:
Peter Steinberger
2026-04-07 12:27:55 +01:00
parent 50265c8b1f
commit 978a0a720e
9 changed files with 33 additions and 19 deletions

View File

@@ -189,8 +189,7 @@ function classifyRuntimeWebTargetPathState(params: {
return "inactive";
}
const configuredProvider =
typeof search?.provider === "string" ? search.provider.trim().toLowerCase() : "";
const configuredProvider = normalizeLowercaseStringOrEmpty(search?.provider);
if (!configuredProvider) {
return "active";
}
@@ -250,8 +249,7 @@ function describeInactiveRuntimeWebTargetPath(params: {
return "tools.web.search is disabled.";
}
const configuredProvider =
typeof search?.provider === "string" ? search.provider.trim().toLowerCase() : "";
const configuredProvider = normalizeLowercaseStringOrEmpty(search?.provider);
if (configuredProvider && configuredProvider !== match[1]) {
return `tools.web.search.provider is "${configuredProvider}".`;
}

View File

@@ -3,7 +3,10 @@ import type { CronJob } from "../../cron/types.js";
import { danger } from "../../globals.js";
import { sanitizeAgentId } from "../../routing/session-key.js";
import { defaultRuntime } from "../../runtime.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
} from "../../shared/string-coerce.js";
import { addGatewayClientOptions, callGatewayFromCli } from "../gateway-rpc.js";
import {
applyExistingCronSchedulePatch,
@@ -279,8 +282,7 @@ export function registerCronEditCommand(cron: Command) {
failureAlert.after = after;
}
if (hasFailureAlertChannel) {
const channel = String(opts.failureAlertChannel).trim().toLowerCase();
failureAlert.channel = channel ? channel : undefined;
failureAlert.channel = normalizeOptionalLowercaseString(opts.failureAlertChannel);
}
if (hasFailureAlertTo) {
const to = String(opts.failureAlertTo).trim();
@@ -294,7 +296,7 @@ export function registerCronEditCommand(cron: Command) {
failureAlert.cooldownMs = cooldownMs;
}
if (hasFailureAlertMode) {
const mode = String(opts.failureAlertMode).trim().toLowerCase();
const mode = normalizeOptionalLowercaseString(opts.failureAlertMode);
if (mode !== "announce" && mode !== "webhook") {
throw new Error("Invalid --failure-alert-mode (must be 'announce' or 'webhook').");
}

View File

@@ -6,6 +6,7 @@ import { resolveDefaultAgentWorkspaceDir } from "../../agents/workspace.js";
import { handleReset } from "../../commands/onboard-helpers.js";
import { createConfigIO, writeConfigFile } from "../../config/config.js";
import { defaultRuntime } from "../../runtime.js";
import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js";
import { resolveUserPath, shortenHomePath } from "../../utils.js";
const DEV_IDENTITY_NAME = "C3-PO";
@@ -32,7 +33,7 @@ async function loadDevTemplate(name: string, fallback: string): Promise<string>
const resolveDevWorkspaceDir = (env: NodeJS.ProcessEnv = process.env): string => {
const baseDir = resolveDefaultAgentWorkspaceDir(env, os.homedir);
const profile = env.OPENCLAW_PROFILE?.trim().toLowerCase();
const profile = normalizeOptionalLowercaseString(env.OPENCLAW_PROFILE);
if (profile === "dev") {
return baseDir;
}

View File

@@ -29,7 +29,10 @@ import { detectRespawnSupervisor } from "../../infra/supervisor-markers.js";
import { setConsoleSubsystemFilter, setConsoleTimestampPrefix } from "../../logging/console.js";
import { createSubsystemLogger } from "../../logging/subsystem.js";
import { defaultRuntime } from "../../runtime.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
} from "../../shared/string-coerce.js";
import { formatCliCommand } from "../command-format.js";
import { inheritOptionFromParent } from "../command-options.js";
import { forceFreePortAndWait, waitForPortBindable } from "../ports.js";
@@ -238,7 +241,7 @@ function isHealthyGatewayLockError(err: unknown): boolean {
}
async function runGatewayCommand(opts: GatewayRunOpts) {
const isDevProfile = process.env.OPENCLAW_PROFILE?.trim().toLowerCase() === "dev";
const isDevProfile = normalizeOptionalLowercaseString(process.env.OPENCLAW_PROFILE) === "dev";
const devMode = Boolean(opts.dev) || isDevProfile;
if (opts.reset && !devMode) {
defaultRuntime.error("Use --reset with --dev.");

View File

@@ -1,6 +1,7 @@
import type { Command } from "commander";
import { randomIdempotencyKey } from "../../gateway/call.js";
import { defaultRuntime } from "../../runtime.js";
import { normalizeOptionalLowercaseString } from "../../shared/string-coerce.js";
import { runNodesCommand } from "./cli-utils.js";
import { callGatewayCli, nodesCallOpts, resolveNodeId } from "./rpc.js";
import type { NodesRpcOpts } from "./types.js";
@@ -24,8 +25,7 @@ export function registerNodesLocationCommands(nodes: Command) {
await runNodesCommand("location get", async () => {
const nodeId = await resolveNodeId(opts, String(opts.node ?? ""));
const maxAgeMs = opts.maxAge ? Number.parseInt(String(opts.maxAge), 10) : undefined;
const desiredAccuracyRaw =
typeof opts.accuracy === "string" ? opts.accuracy.trim().toLowerCase() : undefined;
const desiredAccuracyRaw = normalizeOptionalLowercaseString(opts.accuracy);
const desiredAccuracy =
desiredAccuracyRaw === "coarse" ||
desiredAccuracyRaw === "balanced" ||

View File

@@ -2,7 +2,10 @@ import type { Command } from "commander";
import { formatErrorMessage } from "../../infra/errors.js";
import { formatTimeAgo } from "../../infra/format-time/format-relative.ts";
import { defaultRuntime } from "../../runtime.js";
import { normalizeOptionalString } from "../../shared/string-coerce.js";
import {
normalizeOptionalLowercaseString,
normalizeOptionalString,
} from "../../shared/string-coerce.js";
import { getTerminalTableWidth, renderTable } from "../../terminal/table.js";
import { shortenHomeInString } from "../../utils.js";
import { parseDurationMs } from "../parse-duration.js";
@@ -38,7 +41,7 @@ function resolveNodeVersions(node: {
if (!legacy) {
return { core: undefined, ui: undefined };
}
const platform = node.platform?.trim().toLowerCase() ?? "";
const platform = normalizeOptionalLowercaseString(node.platform) ?? "";
const headless =
platform === "darwin" || platform === "linux" || platform === "win32" || platform === "windows";
return headless ? { core: legacy, ui: undefined } : { core: undefined, ui: legacy };

View File

@@ -1,10 +1,11 @@
import type { Command } from "commander";
import { getChannelPlugin } from "../../../channels/plugins/index.js";
import type { ChannelMessageActionName } from "../../../channels/plugins/types.js";
import { normalizeLowercaseStringOrEmpty } from "../../../shared/string-coerce.js";
import type { MessageCliHelpers } from "./helpers.js";
function resolveThreadCreateRequest(opts: Record<string, unknown>) {
const channel = typeof opts.channel === "string" ? opts.channel.trim().toLowerCase() : "";
const channel = normalizeLowercaseStringOrEmpty(opts.channel);
if (channel) {
const request = getChannelPlugin(channel)?.actions?.resolveCliActionRequest?.({
action: "thread-create",

View File

@@ -1,6 +1,7 @@
import { stdin as input, stdout as output } from "node:process";
import readline from "node:readline/promises";
import { isVerbose, isYes } from "../globals.js";
import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
export async function promptYesNo(question: string, defaultYes = false): Promise<boolean> {
// Simple Y/N prompt honoring global --yes and verbosity flags.
@@ -12,7 +13,7 @@ export async function promptYesNo(question: string, defaultYes = false): Promise
}
const rl = readline.createInterface({ input, output });
const suffix = defaultYes ? " [Y/n] " : " [y/N] ";
const answer = (await rl.question(`${question}${suffix}`)).trim().toLowerCase();
const answer = normalizeLowercaseStringOrEmpty(await rl.question(`${question}${suffix}`));
rl.close();
if (!answer) {
return defaultYes;

View File

@@ -12,6 +12,10 @@ import { ensureOpenClawCliOnPath } from "../infra/path-env.js";
import { assertSupportedRuntime } from "../infra/runtime-guard.js";
import { enableConsoleCapture } from "../logging.js";
import { hasMemoryRuntime } from "../plugins/memory-state.js";
import {
normalizeLowercaseStringOrEmpty,
normalizeOptionalLowercaseString,
} from "../shared/string-coerce.js";
import { resolveCliArgvInvocation } from "./argv-invocation.js";
import {
shouldRegisterPrimaryCommandOnly,
@@ -62,7 +66,7 @@ export function resolveMissingPluginCommandMessage(
pluginId: string,
config?: OpenClawConfig,
): string | null {
const normalizedPluginId = pluginId.trim().toLowerCase();
const normalizedPluginId = normalizeLowercaseStringOrEmpty(pluginId);
if (!normalizedPluginId) {
return null;
}
@@ -70,7 +74,8 @@ export function resolveMissingPluginCommandMessage(
Array.isArray(config?.plugins?.allow) && config.plugins.allow.length > 0
? config.plugins.allow
.filter((entry): entry is string => typeof entry === "string")
.map((entry) => entry.trim().toLowerCase())
.map((entry) => normalizeOptionalLowercaseString(entry))
.filter(Boolean)
: [];
if (allow.length > 0 && !allow.includes(normalizedPluginId)) {
return (