refactor: split gateway port parsing helpers

This commit is contained in:
Peter Steinberger
2026-03-24 17:04:01 -07:00
parent 0709224ce3
commit 0311b51944
5 changed files with 47 additions and 18 deletions

View File

@@ -0,0 +1,25 @@
import { describe, expect, it } from "vitest";
import { type OpenClawPluginApi, resolveGatewayPort } from "./api.js";
function envWith(overrides: Record<string, string | undefined>): NodeJS.ProcessEnv {
return { ...overrides };
}
describe("device-pair api", () => {
it("forwards Compose-style env parsing through the plugin sdk seam", () => {
const cfg = { gateway: { port: 19002 } } as OpenClawPluginApi["config"];
expect(resolveGatewayPort(cfg, envWith({ OPENCLAW_GATEWAY_PORT: "127.0.0.1:18789" }))).toBe(
18789,
);
expect(resolveGatewayPort(cfg, envWith({ OPENCLAW_GATEWAY_PORT: "[::1]:28789" }))).toBe(28789);
});
it("keeps ignoring the legacy env name at the plugin seam", () => {
const cfg = { gateway: { port: 19002 } } as OpenClawPluginApi["config"];
expect(resolveGatewayPort(cfg, envWith({ CLAWDBOT_GATEWAY_PORT: "127.0.0.1:18789" }))).toBe(
19002,
);
});
});

View File

@@ -27,7 +27,10 @@ async function readGeneratedProvider(providerKey: string) {
return parsed.providers[providerKey];
}
async function expectGeneratedProvider(providerKey: string, params: { ids: string[]; baseUrl?: string }) {
async function expectGeneratedProvider(
providerKey: string,
params: { ids: string[]; baseUrl?: string },
) {
const provider = await readGeneratedProvider(providerKey);
expect(provider?.models?.map((model) => model.id)).toEqual(params.ids);
if (params.baseUrl !== undefined) {

View File

@@ -1,5 +1,6 @@
import type { OpenClawConfig } from "../config/config.js";
import { coerceSecretRef, resolveSecretInputRef } from "../config/types.secrets.js";
import { normalizeGoogleApiBaseUrl } from "../infra/google-api-base-url.js";
import { createSubsystemLogger } from "../logging/subsystem.js";
import {
buildAnthropicVertexProvider,
@@ -17,7 +18,6 @@ import {
} from "../plugin-sdk/provider-catalog.js";
import { isRecord } from "../utils.js";
import { normalizeOptionalSecretInput } from "../utils/normalize-secret-input.js";
import { normalizeGoogleApiBaseUrl } from "../infra/google-api-base-url.js";
import { hasAnthropicVertexAvailableAuth } from "./anthropic-vertex-provider.js";
import { ensureAuthProfileStore, listProfilesForProvider } from "./auth-profiles.js";
import { discoverBedrockModels } from "./bedrock-discovery.js";

View File

@@ -2,6 +2,7 @@ import type { Api, Model } from "@mariozechner/pi-ai";
import type { AuthStorage, ModelRegistry } from "@mariozechner/pi-coding-agent";
import type { OpenClawConfig } from "../../config/config.js";
import type { ModelDefinitionConfig } from "../../config/types.js";
import { normalizeGoogleApiBaseUrl } from "../../infra/google-api-base-url.js";
import {
clearProviderRuntimeHookCache,
prepareProviderDynamicModel,
@@ -19,7 +20,6 @@ import {
shouldSuppressBuiltInModel,
} from "../model-suppression.js";
import { discoverAuthStorage, discoverModels } from "../pi-model-discovery.js";
import { normalizeGoogleApiBaseUrl } from "../../infra/google-api-base-url.js";
import { normalizeResolvedProviderModel } from "./model.provider-normalization.js";
function normalizeGoogleGenerativeAiBaseUrl(baseUrl: string | undefined): string | undefined {

View File

@@ -256,30 +256,31 @@ function parseGatewayPortEnvValue(raw: string | undefined): number | null {
if (!trimmed) {
return null;
}
if (/^\d+$/.test(trimmed)) {
const parsed = Number.parseInt(trimmed, 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
}
return parsePositivePort(trimmed) ?? parseComposePublishedPort(trimmed);
}
function parsePositivePort(raw: string): number | null {
if (!/^\d+$/.test(raw)) {
return null;
}
const parsed = Number.parseInt(raw, 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
}
function parseComposePublishedPort(raw: string): number | null {
// Docker Compose publish strings can leak into host CLI env loading via repo `.env`,
// for example `127.0.0.1:18789` or `[::1]:18789`. Accept only explicit host:port forms.
const bracketedIpv6Match = trimmed.match(/^\[[^\]]+\]:(\d+)$/);
const bracketedIpv6Match = raw.match(/^\[[^\]]+\]:(\d+)$/);
if (bracketedIpv6Match?.[1]) {
const parsed = Number.parseInt(bracketedIpv6Match[1], 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
return parsePositivePort(bracketedIpv6Match[1]);
}
const firstColon = trimmed.indexOf(":");
const lastColon = trimmed.lastIndexOf(":");
const firstColon = raw.indexOf(":");
const lastColon = raw.lastIndexOf(":");
if (firstColon <= 0 || firstColon !== lastColon) {
return null;
}
const suffix = trimmed.slice(firstColon + 1);
if (!/^\d+$/.test(suffix)) {
return null;
}
const parsed = Number.parseInt(suffix, 10);
return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
return parsePositivePort(raw.slice(firstColon + 1));
}
export function resolveGatewayPort(