diff --git a/src/commands/configure.gateway.ts b/src/commands/configure.gateway.ts index 162e1a0cf7d..84db572eb26 100644 --- a/src/commands/configure.gateway.ts +++ b/src/commands/configure.gateway.ts @@ -2,6 +2,7 @@ import type { OpenClawConfig } from "../config/config.js"; import type { RuntimeEnv } from "../runtime.js"; import { resolveGatewayPort } from "../config/config.js"; import { findTailscaleBinary } from "../infra/tailscale.js"; +import { validateIPv4AddressInput } from "../shared/net/ipv4.js"; import { note } from "../terminal/note.js"; import { buildGatewayAuthConfig } from "./configure.gateway-auth.js"; import { confirm, select, text } from "./configure.shared.js"; @@ -72,25 +73,7 @@ export async function promptGatewayConfig( await text({ message: "Custom IP address", placeholder: "192.168.1.100", - validate: (value) => { - if (!value) { - return "IP address is required for custom bind mode"; - } - const trimmed = value.trim(); - const parts = trimmed.split("."); - if (parts.length !== 4) { - return "Invalid IPv4 address (e.g., 192.168.1.100)"; - } - if ( - parts.every((part) => { - const n = parseInt(part, 10); - return !Number.isNaN(n) && n >= 0 && n <= 255 && part === String(n); - }) - ) { - return undefined; - } - return "Invalid IPv4 address (each octet must be 0-255)"; - }, + validate: validateIPv4AddressInput, }), runtime, ); diff --git a/src/shared/net/ipv4.ts b/src/shared/net/ipv4.ts new file mode 100644 index 00000000000..3c511823c77 --- /dev/null +++ b/src/shared/net/ipv4.ts @@ -0,0 +1,19 @@ +export function validateIPv4AddressInput(value: string | undefined): string | undefined { + if (!value) { + return "IP address is required for custom bind mode"; + } + const trimmed = value.trim(); + const parts = trimmed.split("."); + if (parts.length !== 4) { + return "Invalid IPv4 address (e.g., 192.168.1.100)"; + } + if ( + parts.every((part) => { + const n = parseInt(part, 10); + return !Number.isNaN(n) && n >= 0 && n <= 255 && part === String(n); + }) + ) { + return undefined; + } + return "Invalid IPv4 address (each octet must be 0-255)"; +} diff --git a/src/wizard/onboarding.gateway-config.ts b/src/wizard/onboarding.gateway-config.ts index e7ecac2b6d2..dae687d12cf 100644 --- a/src/wizard/onboarding.gateway-config.ts +++ b/src/wizard/onboarding.gateway-config.ts @@ -13,6 +13,7 @@ import { validateGatewayPasswordInput, } from "../commands/onboard-helpers.js"; import { findTailscaleBinary } from "../infra/tailscale.js"; +import { validateIPv4AddressInput } from "../shared/net/ipv4.js"; // These commands are "high risk" (privacy writes/recording) and should be // explicitly armed by the user when they want to use them. @@ -85,25 +86,7 @@ export async function configureGatewayForOnboarding( message: "Custom IP address", placeholder: "192.168.1.100", initialValue: customBindHost ?? "", - validate: (value) => { - if (!value) { - return "IP address is required for custom bind mode"; - } - const trimmed = value.trim(); - const parts = trimmed.split("."); - if (parts.length !== 4) { - return "Invalid IPv4 address (e.g., 192.168.1.100)"; - } - if ( - parts.every((part) => { - const n = parseInt(part, 10); - return !Number.isNaN(n) && n >= 0 && n <= 255 && part === String(n); - }) - ) { - return undefined; - } - return "Invalid IPv4 address (each octet must be 0-255)"; - }, + validate: validateIPv4AddressInput, }); customBindHost = typeof input === "string" ? input.trim() : undefined; }