fix: address Codex OAuth review feedback

- Save CODEX_OAUTH_TOKEN marker config after OAuth to track install state
- Check marker config instead of OPENAI_API_KEY for install detection
- Avoid mutating input dict in CodexOAuthManager.save_token()
- Add CODEX_OAUTH_TOKEN to Codex env_vars whitelist in server config
- Prioritize named integration descriptions over env_vars fallback
This commit is contained in:
eureka928 2026-02-07 05:41:58 +01:00
parent 2601f197ed
commit 009e8e27ef
5 changed files with 92 additions and 22 deletions

View file

@ -250,6 +250,7 @@ class CodexOAuthManager:
True on success.
"""
path = cls._token_path()
token_data = token_data.copy()
try:
if "saved_at" not in token_data:
token_data["saved_at"] = int(time.time())

View file

@ -168,12 +168,11 @@ class ConfigInfo:
"env_vars": ["OPENAI_API_KEY"],
"toolkit": "rag_toolkit",
},
# Codex OAuth is a model-provider integration (obtains an
# OpenAI API key). After OAuth the key is stored via the
# Provider API, not here. The entry is kept only for
# install-state detection in the integration list UI.
# TODO: Codex is a model-provider integration, not a toolkit.
# Its OAuth entry point should move to the Model/Provider settings
# page. Keeping it here temporarily for install-state detection.
ConfigGroup.CODEX.value: {
"env_vars": [],
"env_vars": ["CODEX_OAUTH_TOKEN"],
"toolkit": None,
"type": "provider",
},

View file

@ -268,11 +268,45 @@ const ToolSelect = forwardRef<
}
};
const saveCodexMarkerConfig = async () => {
try {
const existingConfigs = await proxyFetchGet('/api/configs');
const existing = Array.isArray(existingConfigs)
? existingConfigs.find(
(c: any) =>
c.config_group?.toLowerCase() === 'codex' &&
c.config_name === 'CODEX_OAUTH_TOKEN'
)
: null;
const configPayload = {
config_group: 'Codex',
config_name: 'CODEX_OAUTH_TOKEN',
config_value: 'exists',
};
if (existing) {
await proxyFetchPut(
`/api/configs/${existing.id}`,
configPayload
);
} else {
await proxyFetchPost('/api/configs', configPayload);
}
} catch (configError) {
console.warn(
'Failed to persist Codex marker config',
configError
);
}
};
onInstall = async () => {
try {
const response = await fetchPost('/install/tool/codex');
if (response.success) {
await saveCodexAsProvider(response);
await saveCodexMarkerConfig();
const codexItem = {
id: 0,
key: key,
@ -295,6 +329,7 @@ const ToolSelect = forwardRef<
);
if (retryResponse.success) {
await saveCodexAsProvider(retryResponse);
await saveCodexMarkerConfig();
fetchIntegrationsData();
const codexItem = {
id: 0,
@ -342,16 +377,16 @@ const ToolSelect = forwardRef<
env_vars: value.env_vars,
toolkit: value.toolkit,
desc:
value.env_vars && value.env_vars.length > 0
? `${t('layout.environmental-variables-required')} ${value.env_vars.join(
', '
)}`
key.toLowerCase() === 'codex'
? t('layout.codex-integration')
: key.toLowerCase() === 'notion'
? t('layout.notion-workspace-integration')
: key.toLowerCase() === 'google calendar'
? t('layout.google-calendar-integration')
: key.toLowerCase() === 'codex'
? t('layout.codex-integration')
: value.env_vars && value.env_vars.length > 0
? `${t('layout.environmental-variables-required')} ${value.env_vars.join(
', '
)}`
: '',
onInstall,
};

View file

@ -98,15 +98,15 @@ export function useIntegrationManagement(items: IntegrationItem[]) {
);
map[item.key] = hasAccessToken;
} else if (item.key === 'Codex') {
// Codex: check if OPENAI_API_KEY config is present
const hasApiKey = configs.some(
// Codex: check if CODEX_OAUTH_TOKEN marker config is present
const hasOAuthToken = configs.some(
(c: any) =>
c.config_group?.toLowerCase() === 'codex' &&
c.config_name === 'OPENAI_API_KEY' &&
c.config_name === 'CODEX_OAUTH_TOKEN' &&
c.config_value &&
String(c.config_value).length > 0
);
map[item.key] = hasApiKey;
map[item.key] = hasOAuthToken;
} else {
// For other integrations, use config_group presence
const hasConfig = configs.some(

View file

@ -347,11 +347,11 @@ export default function SettingMCP() {
);
}
};
} else if (key.toLowerCase() === 'codex') {
// Codex OAuth obtains an OpenAI API key for model access.
// After a successful flow we save it as an OpenAI *Provider*
// (not a toolkit config) so it integrates with the model
// configuration system.
} else if (key.toLowerCase() === 'codex') {
const saveCodexAsProvider = async (installResponse: any) => {
if (!installResponse?.access_token) return;
try {
@ -391,12 +391,46 @@ export default function SettingMCP() {
}
};
const saveCodexMarkerConfig = async () => {
try {
const existingConfigs = await proxyFetchGet('/api/configs');
const existing = Array.isArray(existingConfigs)
? existingConfigs.find(
(c: any) =>
c.config_group?.toLowerCase() === 'codex' &&
c.config_name === 'CODEX_OAUTH_TOKEN'
)
: null;
const configPayload = {
config_group: 'Codex',
config_name: 'CODEX_OAUTH_TOKEN',
config_value: 'exists',
};
if (existing) {
await proxyFetchPut(
`/api/configs/${existing.id}`,
configPayload
);
} else {
await proxyFetchPost('/api/configs', configPayload);
}
} catch (configError) {
console.warn(
'Failed to persist Codex marker config',
configError
);
}
};
onInstall = async () => {
try {
const response = await fetchPost('/install/tool/codex');
if (response.success) {
toast.success(t('setting.codex-installed-successfully'));
await saveCodexAsProvider(response);
await saveCodexMarkerConfig();
fetchList();
setRefreshKey((prev) => prev + 1);
} else if (response.status === 'authorizing') {
@ -412,6 +446,7 @@ export default function SettingMCP() {
const finalize = await fetchPost('/install/tool/codex');
if (finalize?.success) {
await saveCodexAsProvider(finalize);
await saveCodexMarkerConfig();
toast.success(
t('setting.codex-installed-successfully')
);
@ -462,16 +497,16 @@ export default function SettingMCP() {
name: key,
env_vars: value.env_vars,
desc:
value.env_vars && value.env_vars.length > 0
? `${t(
'setting.environmental-variables-required'
)}: ${value.env_vars.join(', ')}`
key.toLowerCase() === 'codex'
? t('setting.codex-integration')
: key.toLowerCase() === 'notion'
? t('setting.notion-workspace-integration')
: key.toLowerCase() === 'google calendar'
? t('setting.google-calendar-integration')
: key.toLowerCase() === 'codex'
? t('setting.codex-integration')
: value.env_vars && value.env_vars.length > 0
? `${t(
'setting.environmental-variables-required'
)}: ${value.env_vars.join(', ')}`
: '',
onInstall,
};