fix(persist): block codex claude settings bridge

This commit is contained in:
Kai (Tam Nhu) Tran authored and GitHub committed 2026-05-19 07:32:04 -04:00
1 parent a6ee3536c4
commit 3b2016462a
8 files changed
+219 -27

No files matched your search

+5 -2
View File
@@ -680,7 +680,7 @@ async function showHelp(): Promise<void> {
console.log('');
console.log(subheader('Supported Profile Types'));
console.log(` ${color('API profiles', 'command')} glm, km, custom API profiles`);
console.log(` ${color('CLIProxy', 'command')} gemini, codex, agy, qwen, kiro, ghcp`);
console.log(` ${color('CLIProxy', 'command')} gemini, agy, qwen, kiro, ghcp`);
console.log(` ${color('Copilot', 'command')} copilot (requires copilot-api daemon)`);
console.log(
` ${color('Account profiles', 'command')} work, personal, client (persists CLAUDE_CONFIG_DIR)`
@@ -700,7 +700,7 @@ async function showHelp(): Promise<void> {
console.log(` ${color('ccs persist glm --permission-mode acceptEdits', 'command')}`);
console.log('');
console.log(` ${dim('# Persist with auto-approve enabled')}`);
console.log(` ${color('ccs persist codex --dangerously-skip-permissions', 'command')}`);
console.log(` ${color('ccs persist glm --dangerously-skip-permissions', 'command')}`);
console.log('');
console.log(` ${dim('# Persist an account profile for IDE/native Claude use')}`);
console.log(` ${color('ccs persist work --yes', 'command')}`);
@@ -719,6 +719,9 @@ async function showHelp(): Promise<void> {
console.log('');
console.log(subheader('Notes'));
console.log(' [i] CLIProxy profiles require the proxy to be running.');
console.log(
' [i] Codex CLIProxy profiles are native Codex-only: use ccsxp or ccs codex --target codex.'
);
console.log(' [i] Copilot profiles require copilot-api daemon.');
console.log(
' [i] Account/default flows remove stale ANTHROPIC_* overrides before applying new setup.'
+18 -1
View File
@@ -219,7 +219,24 @@ export class ClaudeSettingsChecker implements IHealthChecker {
// Validate JSON
try {
const content = fs.readFileSync(settingsPath, 'utf8');
JSON.parse(content);
const settings = JSON.parse(content) as {
env?: Record<string, unknown>;
};
const baseUrl =
settings.env && typeof settings.env.ANTHROPIC_BASE_URL === 'string'
? settings.env.ANTHROPIC_BASE_URL
: '';
if (baseUrl.includes('/api/provider/codex')) {
spinner.warn();
console.log(` ${warn(settingsName.padEnd(22))} Codex CLIProxy bridge persisted`);
results.addCheck(
'Claude Settings',
'warning',
'Claude settings route Claude Code through the Codex CLIProxy translator',
'Run: ccs persist default --yes; use ccsxp or ccs codex --target codex for Codex'
);
return;
}
spinner.succeed();
console.log(` ${ok(settingsName.padEnd(22))} Valid`);
results.addCheck('Claude Settings', 'success');
+30 -1
View File
@@ -103,6 +103,30 @@ function describeProfile(profileName: string, result: ProfileDetectionResult): s
return 'Native Claude profile resolution.';
}
function resultUsesCodexProvider(result: ProfileDetectionResult): boolean {
if (result.type !== 'cliproxy') {
return false;
}
if (result.provider === 'codex') {
return true;
}
if (!result.isComposite || !result.compositeTiers) {
return false;
}
return Object.values(result.compositeTiers).some(
(tier) => tier.provider === 'codex' || tier.fallback?.provider === 'codex'
);
}
function createCodexClaudeExtensionError(profileName: string): Error {
return new Error(
`Profile "${profileName}" is a Codex CLIProxy profile. CCS does not persist Codex CLIProxy profiles into Claude Code or Claude Code Extension settings because that routes Claude traffic through the Codex translator. Use ccsxp or ccs codex --target codex for ChatGPT/Codex subscriptions. To clear stale Claude settings, run ccs persist default --yes.`
);
}
function createProfileOption(
profileName: string,
result: ProfileDetectionResult
@@ -140,7 +164,9 @@ export function listClaudeExtensionProfiles(): ClaudeExtensionProfileOption[] {
}
return deduped
.map((profileName) => createProfileOption(profileName, detector.detectProfileType(profileName)))
.map((profileName) => ({ profileName, result: detector.detectProfileType(profileName) }))
.filter(({ result }) => !resultUsesCodexProvider(result))
.map(({ profileName, result }) => createProfileOption(profileName, result))
.sort((left, right) => deduped.indexOf(left.name) - deduped.indexOf(right.name));
}
@@ -294,6 +320,9 @@ export async function resolveClaudeExtensionSetup(
): Promise<ClaudeExtensionSetup> {
const detector = new ProfileDetector();
const result = detector.detectProfileType(requestedProfile);
if (resultUsesCodexProvider(result)) {
throw createCodexClaudeExtensionError(requestedProfile);
}
const resolved = await resolveExtensionEnv(requestedProfile, result);
return {