fix(cliproxy): harden custom local port handling

This commit is contained in:
Tam Nhu Tran committed 2026-05-03 14:07:31 -04:00
1 parent a7c10be72e
commit 3862411bb7
6 files changed
+155 -8

No files matched your search

+2 -1
View File
@@ -10,6 +10,7 @@ import {
CLIPROXY_DEFAULT_PORT,
getRemoteDefaultPort,
normalizeProtocol,
validatePort,
validateRemotePort,
} from '../config/port-manager';
import { getProxyEnvVars } from './proxy-config-resolver';
@@ -69,7 +70,7 @@ export function getProxyTarget(): ProxyTarget {
};
}
const localPort = config?.local?.port ?? CLIPROXY_DEFAULT_PORT;
const localPort = validatePort(config?.local?.port ?? CLIPROXY_DEFAULT_PORT);
return {
host: '127.0.0.1',
+19 -5
View File
@@ -3,7 +3,7 @@ import { spawn } from 'child_process';
import type { CursorConfig } from '../config/unified-config-types';
import { ensureCliproxyService } from '../cliproxy';
import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager';
import { resolveLifecyclePort } from '../cliproxy/config/port-manager';
import { fail, info, ok } from '../utils/ui';
import {
appendThirdPartyWebSearchToolArgs,
@@ -22,6 +22,13 @@ interface CursorImageAnalysisResolution {
warning: string | null;
}
interface CursorImageAnalysisDeps {
getImageAnalysisHookEnv?: typeof getImageAnalysisHookEnv;
resolveImageAnalysisRuntimeStatus?: typeof resolveImageAnalysisRuntimeStatus;
ensureCliproxyService?: typeof ensureCliproxyService;
resolveLifecyclePort?: typeof resolveLifecyclePort;
}
export function generateCursorEnv(
config: CursorConfig,
claudeConfigDir?: string
@@ -45,9 +52,16 @@ export function generateCursorEnv(
}
export async function resolveCursorImageAnalysisEnv(
verbose = false
verbose = false,
deps: CursorImageAnalysisDeps = {}
): Promise<CursorImageAnalysisResolution> {
const env = getImageAnalysisHookEnv({
const getImageAnalysisHookEnvFn = deps.getImageAnalysisHookEnv ?? getImageAnalysisHookEnv;
const resolveImageAnalysisRuntimeStatusFn =
deps.resolveImageAnalysisRuntimeStatus ?? resolveImageAnalysisRuntimeStatus;
const ensureCliproxyServiceFn = deps.ensureCliproxyService ?? ensureCliproxyService;
const resolveLifecyclePortFn = deps.resolveLifecyclePort ?? resolveLifecyclePort;
const env = getImageAnalysisHookEnvFn({
profileName: 'cursor',
profileType: 'cursor',
});
@@ -56,7 +70,7 @@ export async function resolveCursorImageAnalysisEnv(
return { env, warning: null };
}
const status = await resolveImageAnalysisRuntimeStatus({
const status = await resolveImageAnalysisRuntimeStatusFn({
profileName: 'cursor',
profileType: 'cursor',
});
@@ -73,7 +87,7 @@ export async function resolveCursorImageAnalysisEnv(
}
if (status.proxyReadiness === 'stopped') {
const ensureServiceResult = await ensureCliproxyService(CLIPROXY_DEFAULT_PORT, verbose);
const ensureServiceResult = await ensureCliproxyServiceFn(resolveLifecyclePortFn(), verbose);
if (!ensureServiceResult.started) {
return {
env: {
+2 -2
View File
@@ -43,7 +43,7 @@ import {
} from '../utils/hooks/image-analyzer-profile-hook-injector';
import { resolveCliproxyBridgeMetadata } from '../api/services';
import { ensureCliproxyService } from '../cliproxy';
import { CLIPROXY_DEFAULT_PORT } from '../cliproxy/config/port-manager';
import { resolveLifecyclePort } from '../cliproxy/config/port-manager';
import {
buildOpenAICompatProxyEnv,
resolveOpenAICompatProfileConfig,
@@ -212,7 +212,7 @@ export class HeadlessExecutor {
imageAnalysisProvider &&
imageAnalysisStatus.proxyReadiness === 'stopped'
) {
const ensureServiceResult = await ensureCliproxyService(CLIPROXY_DEFAULT_PORT, false);
const ensureServiceResult = await ensureCliproxyService(resolveLifecyclePort(), false);
if (!ensureServiceResult.started) {
console.error(
warn(
+31
View File
@@ -12,6 +12,7 @@ import { Router, Request, Response } from 'express';
import { testConnection } from '../../cliproxy/services/remote-proxy-client';
import { isProxyRunning } from '../../cliproxy/services/proxy-lifecycle-service';
import { DEFAULT_BACKEND } from '../../cliproxy/binary/platform-detector';
import { validatePort } from '../../cliproxy/config/port-manager';
import {
DEFAULT_CLIPROXY_SERVER_CONFIG,
CliproxyServerConfig,
@@ -58,6 +59,36 @@ router.get('/', async (_req: Request, res: Response) => {
router.put('/', (req: Request, res: Response) => {
try {
const updates = req.body as Partial<CliproxyServerConfig>;
const currentConfig = loadOrCreateUnifiedConfig();
const currentLocalPort = validatePort(
currentConfig.cliproxy_server?.local?.port ?? DEFAULT_CLIPROXY_SERVER_CONFIG.local.port
);
const requestedLocalPort = updates.local?.port;
if (
requestedLocalPort !== undefined &&
(!Number.isInteger(requestedLocalPort) ||
requestedLocalPort < 1 ||
requestedLocalPort > 65535)
) {
res.status(400).json({
error: 'Invalid local port. Must be an integer between 1 and 65535.',
});
return;
}
const nextLocalPort =
requestedLocalPort === undefined ? currentLocalPort : validatePort(requestedLocalPort);
if (nextLocalPort !== currentLocalPort && isProxyRunning()) {
res.status(409).json({
error:
'Proxy is running on the current local port. Stop CLIProxy before changing local.port.',
proxyRunning: true,
currentLocalPort,
});
return;
}
// Atomic read-modify-write — avoids race between load and save
const updated = mutateConfig((config) => {