fix(codex): align runtime compatibility and dashboard types

This commit is contained in:
Tam Nhu Tran
2026-03-29 13:14:15 -04:00
parent 8c5da9f9e8
commit 3c52b1ab6d
7 changed files with 192 additions and 35 deletions
+50 -27
View File
@@ -87,6 +87,11 @@ interface DetectedProfile {
remainingArgs: string[];
}
interface RuntimeReasoningResolution {
argsWithoutReasoningFlags: string[];
reasoningOverride: string | number | undefined;
}
/**
* Smart profile detection
*/
@@ -100,6 +105,42 @@ function detectProfile(args: string[]): DetectedProfile {
}
}
function resolveRuntimeReasoningFlags(
args: string[],
envThinkingValue: string | undefined
): RuntimeReasoningResolution {
const runtime = resolveDroidReasoningRuntime(args, envThinkingValue);
if (runtime.duplicateDisplays.length > 0) {
console.error(
warn(
`[!] Multiple reasoning flags detected. Using first occurrence: ${runtime.sourceDisplay || '<first-flag>'}`
)
);
}
return {
argsWithoutReasoningFlags: runtime.argsWithoutReasoningFlags,
reasoningOverride: runtime.reasoningOverride,
};
}
function exitWithRuntimeReasoningFlagError(
message: string,
options: {
codexAliasLevels: string;
includeDroidExecExample?: boolean;
}
): never {
console.error(fail(message));
console.error(' Examples: --thinking low, --thinking 8192, --thinking off');
console.error(` Codex alias: --effort ${options.codexAliasLevels}`);
if (options.includeDroidExecExample) {
console.error(' Droid exec: --reasoning-effort high');
}
process.exit(1);
}
// ========== Main Execution ==========
interface ProfileError extends Error {
@@ -463,17 +504,9 @@ async function main(): Promise<void> {
targetRemainingArgs = droidRoute.argsForDroid;
if (droidRoute.mode === 'interactive') {
const runtime = resolveDroidReasoningRuntime(remainingArgs, process.env.CCS_THINKING);
const runtime = resolveRuntimeReasoningFlags(remainingArgs, process.env.CCS_THINKING);
targetRemainingArgs = runtime.argsWithoutReasoningFlags;
runtimeReasoningOverride = runtime.reasoningOverride;
if (runtime.duplicateDisplays.length > 0) {
console.error(
warn(
`[!] Multiple reasoning flags detected. Using first occurrence: ${runtime.sourceDisplay || '<first-flag>'}`
)
);
}
} else {
if (droidRoute.duplicateReasoningDisplays.length > 0) {
console.error(
@@ -490,33 +523,23 @@ async function main(): Promise<void> {
}
} catch (error) {
if (error instanceof DroidReasoningFlagError || error instanceof DroidCommandRouterError) {
console.error(fail(error.message));
console.error(' Examples: --thinking low, --thinking 8192, --thinking off');
console.error(' Codex alias: --effort medium|high|xhigh');
console.error(' Droid exec: --reasoning-effort high');
process.exit(1);
exitWithRuntimeReasoningFlagError(error.message, {
codexAliasLevels: 'medium|high|xhigh',
includeDroidExecExample: true,
});
}
throw error;
}
} else if (resolvedTarget === 'codex') {
try {
const runtime = resolveDroidReasoningRuntime(remainingArgs, process.env.CCS_THINKING);
const runtime = resolveRuntimeReasoningFlags(remainingArgs, process.env.CCS_THINKING);
targetRemainingArgs = runtime.argsWithoutReasoningFlags;
runtimeReasoningOverride = runtime.reasoningOverride;
if (runtime.duplicateDisplays.length > 0) {
console.error(
warn(
`[!] Multiple reasoning flags detected. Using first occurrence: ${runtime.sourceDisplay || '<first-flag>'}`
)
);
}
} catch (error) {
if (error instanceof DroidReasoningFlagError) {
console.error(fail(error.message));
console.error(' Examples: --thinking low, --thinking 8192, --thinking off');
console.error(' Codex alias: --effort minimal|low|medium|high|xhigh');
process.exit(1);
exitWithRuntimeReasoningFlagError(error.message, {
codexAliasLevels: 'minimal|low|medium|high|xhigh',
});
}
throw error;
}
+3 -1
View File
@@ -229,6 +229,8 @@ export class CodexAdapter implements TargetAdapter {
}
supportsProfileType(profileType: ProfileType): boolean {
return profileType === 'default' || profileType === 'settings' || profileType === 'cliproxy';
// Bridge-backed settings profiles need additional compatibility context that the
// adapter contract does not receive, so keep the adapter-level claim conservative.
return profileType === 'default' || profileType === 'cliproxy';
}
}
+1 -1
View File
@@ -25,7 +25,7 @@ export const TARGET_METADATA: Record<TargetType, TargetMetadata> = {
legacyAliasEnvVar: 'CCS_CODEX_ALIASES',
persistedTarget: false,
},
};
} satisfies Record<TargetType, TargetMetadata>;
export const RUNTIME_TARGET_TYPES = Object.freeze(
Object.keys(TARGET_METADATA) as TargetType[]
+2 -2
View File
@@ -5,10 +5,10 @@ import { CodexAdapter } from '../../../src/targets/codex-adapter';
describe('CodexAdapter', () => {
const adapter = new CodexAdapter();
test('supports default, settings, and cliproxy profile types', () => {
test('supports only adapter-level default and cliproxy profile types', () => {
expect(adapter.supportsProfileType('default')).toBe(true);
expect(adapter.supportsProfileType('settings')).toBe(true);
expect(adapter.supportsProfileType('cliproxy')).toBe(true);
expect(adapter.supportsProfileType('settings')).toBe(false);
expect(adapter.supportsProfileType('account')).toBe(false);
expect(adapter.supportsProfileType('copilot')).toBe(false);
});
@@ -60,6 +60,13 @@ describe('evaluateTargetRuntimeCompatibility', () => {
});
expect(compatibility.supported).toBe(false);
expect(compatibility.reason).toMatch(/only supports CLIProxy Codex bridge profiles/);
const genericSettingsCompatibility = evaluateTargetRuntimeCompatibility({
target: 'codex',
profileType: 'settings',
});
expect(genericSettingsCompatibility.supported).toBe(false);
expect(genericSettingsCompatibility.reason).toMatch(/currently supports native default sessions/);
});
test('rejects account and copilot profiles on Codex target', () => {
+128
View File
@@ -0,0 +1,128 @@
export interface CompatibleCliDocLink {
id: string;
label: string;
url: string;
category: 'overview' | 'configuration' | 'byok' | 'reference';
source: 'factory' | 'provider' | 'openai' | 'github';
description: string;
}
export interface CompatibleCliProviderDocLink {
provider: string;
label: string;
apiFormat: string;
url: string;
}
export interface CompatibleCliDocsReference {
providerValues: string[];
settingsHierarchy: string[];
notes: string[];
links: CompatibleCliDocLink[];
providerDocs: CompatibleCliProviderDocLink[];
}
export interface CodexBinaryDiagnostics {
installed: boolean;
path: string | null;
installDir: string | null;
source: 'CCS_CODEX_PATH' | 'PATH' | 'missing';
version: string | null;
overridePath: string | null;
supportsConfigOverrides: boolean;
}
export interface CodexConfigFileDiagnostics {
label: string;
path: string;
resolvedPath: string;
exists: boolean;
isSymlink: boolean;
isRegularFile: boolean;
sizeBytes: number | null;
mtimeMs: number | null;
parseError: string | null;
readError: string | null;
}
export interface CodexModelProviderDiagnostics {
name: string;
baseUrl: string | null;
envKey: string | null;
wireApi: string | null;
requiresOpenaiAuth: boolean;
supportsWebsockets: boolean;
hasQueryParams: boolean;
hasHttpHeaders: boolean;
usesExperimentalBearerToken: boolean;
}
export interface CodexFeatureFlagDiagnostics {
name: string;
state: 'enabled' | 'disabled' | 'custom';
}
export interface CodexProjectTrustDiagnostics {
path: string;
trustLevel: string;
}
export interface CodexMcpServerDiagnostics {
name: string;
transport: 'stdio' | 'streamable-http' | 'unknown';
enabled: boolean;
required: boolean;
startupTimeoutSec: number | null;
toolTimeoutSec: number | null;
enabledToolsCount: number;
disabledToolsCount: number;
usesInlineBearerToken: boolean;
}
export interface CodexSupportMatrixEntry {
id: string;
label: string;
supported: boolean;
notes: string;
}
export interface CodexUserConfigDiagnostics {
model: string | null;
modelProvider: string | null;
activeProfile: string | null;
approvalPolicy: string | null;
sandboxMode: string | null;
webSearch: string | null;
topLevelKeys: string[];
profileCount: number;
profileNames: string[];
modelProviderCount: number;
modelProviders: CodexModelProviderDiagnostics[];
featureCount: number;
enabledFeatures: CodexFeatureFlagDiagnostics[];
disabledFeatures: CodexFeatureFlagDiagnostics[];
trustedProjectCount: number;
untrustedProjectCount: number;
projectTrust: CodexProjectTrustDiagnostics[];
mcpServerCount: number;
mcpServers: CodexMcpServerDiagnostics[];
}
export interface CodexDashboardDiagnostics {
binary: CodexBinaryDiagnostics;
file: CodexConfigFileDiagnostics;
config: CodexUserConfigDiagnostics;
supportMatrix: CodexSupportMatrixEntry[];
warnings: string[];
docsReference: CompatibleCliDocsReference;
}
export interface CodexRawConfigResponse {
path: string;
resolvedPath: string;
exists: boolean;
mtime: number;
rawText: string;
config: Record<string, unknown> | null;
parseError: string | null;
}
+1 -4
View File
@@ -2,10 +2,7 @@ import { useMemo } from 'react';
import { parse as parseToml } from 'smol-toml';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { ApiConflictError, withApiBase } from '@/lib/api-client';
import type {
CodexDashboardDiagnostics,
CodexRawConfigResponse,
} from '../../../src/web-server/services/compatible-cli-types';
import type { CodexDashboardDiagnostics, CodexRawConfigResponse } from './use-codex-types';
type CodexRawConfig = CodexRawConfigResponse;