Skip to content

Commit 43d443f

Browse files
committed
feat(codex): add OpenAI-compatible transform option
Add a UI toggle for providers to route through the builtin OpenAI bridge.\n\nWhen enabled, the provider base_url points to /bridge/openai/<provider>/v1 and Codex auth token is fixed to 'codexmate'; upstream base URL and API key are stored in ~/.codex/codexmate-openai-bridge.json.
1 parent e688552 commit 43d443f

6 files changed

Lines changed: 568 additions & 8 deletions

File tree

cli.js

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,10 @@ const {
8383
const {
8484
createBuiltinClaudeProxyRuntimeController
8585
} = require('./cli/claude-proxy');
86+
const {
87+
createOpenaiBridgeHttpHandler,
88+
upsertOpenaiBridgeProvider
89+
} = require('./cli/openai-bridge');
8690
const {
8791
createOpenclawConfigController
8892
} = require('./cli/openclaw-config');
@@ -160,6 +164,7 @@ const CURRENT_MODELS_FILE = path.join(CONFIG_DIR, 'provider-current-models.json'
160164
const INIT_MARK_FILE = path.join(CONFIG_DIR, 'codexmate-init.json');
161165
const BUILTIN_PROXY_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-proxy.json');
162166
const BUILTIN_CLAUDE_PROXY_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-claude-proxy.json');
167+
const OPENAI_BRIDGE_SETTINGS_FILE = path.join(CONFIG_DIR, 'codexmate-openai-bridge.json');
163168
const CODEX_SESSIONS_DIR = path.join(CONFIG_DIR, 'sessions');
164169
const SESSION_TRASH_DIR = path.join(CONFIG_DIR, 'codexmate-session-trash');
165170
const SESSION_TRASH_FILES_DIR = path.join(SESSION_TRASH_DIR, 'files');
@@ -261,6 +266,14 @@ const CLI_INSTALL_TARGETS = Object.freeze([
261266
const HTTP_KEEP_ALIVE_AGENT = new http.Agent({ keepAlive: true });
262267
const HTTPS_KEEP_ALIVE_AGENT = new https.Agent({ keepAlive: true });
263268

269+
const openaiBridgeHandler = createOpenaiBridgeHttpHandler({
270+
settingsFile: OPENAI_BRIDGE_SETTINGS_FILE,
271+
expectedToken: 'codexmate',
272+
maxBodySize: MAX_API_BODY_SIZE,
273+
httpAgent: HTTP_KEEP_ALIVE_AGENT,
274+
httpsAgent: HTTPS_KEEP_ALIVE_AGENT
275+
});
276+
264277
function resolveWebPort() {
265278
const raw = process.env.CODEXMATE_PORT;
266279
if (!raw) return DEFAULT_WEB_PORT;
@@ -1824,6 +1837,7 @@ function addProviderToConfig(params = {}) {
18241837
const name = typeof params.name === 'string' ? params.name.trim() : '';
18251838
const url = typeof params.url === 'string' ? params.url.trim() : '';
18261839
const key = typeof params.key === 'string' ? params.key.trim() : '';
1840+
const useTransform = !!params.useTransform;
18271841
const allowManaged = !!params.allowManaged;
18281842
const normalizedUrl = normalizeBaseUrl(url);
18291843

@@ -1876,15 +1890,35 @@ function addProviderToConfig(params = {}) {
18761890

18771891
const lineEnding = content.includes('\r\n') ? '\r\n' : '\n';
18781892
const safeName = escapeTomlBasicString(name);
1879-
const safeUrl = escapeTomlBasicString(normalizedUrl);
1880-
const safeKey = escapeTomlBasicString(key);
1893+
let baseUrlForConfig = normalizedUrl;
1894+
let authKeyForConfig = key;
1895+
const extraLines = [];
1896+
1897+
if (useTransform) {
1898+
const saveRes = upsertOpenaiBridgeProvider(OPENAI_BRIDGE_SETTINGS_FILE, name, normalizedUrl, key);
1899+
if (saveRes && saveRes.error) {
1900+
return { error: String(saveRes.error) };
1901+
}
1902+
const port = resolveWebPort();
1903+
// 通过 URL 构造避免出现重复 /(例如 /bridge/openai//v1)
1904+
baseUrlForConfig = new URL(
1905+
`/bridge/openai/${encodeURIComponent(name)}/v1`,
1906+
`http://${DEFAULT_WEB_OPEN_HOST}:${port}`
1907+
).toString().replace(/\/+$/g, '');
1908+
authKeyForConfig = 'codexmate';
1909+
extraLines.push(`codexmate_bridge = "openai"`);
1910+
}
1911+
1912+
const safeUrl = escapeTomlBasicString(baseUrlForConfig);
1913+
const safeKey = escapeTomlBasicString(authKeyForConfig);
18811914
const block = [
18821915
buildModelProviderTableHeader(name),
18831916
`name = "${safeName}"`,
18841917
`base_url = "${safeUrl}"`,
18851918
`wire_api = "responses"`,
18861919
`requires_openai_auth = false`,
18871920
`preferred_auth_method = "${safeKey}"`,
1921+
...extraLines,
18881922
`request_max_retries = 4`,
18891923
`stream_max_retries = 10`,
18901924
`stream_idle_timeout_ms = 300000`
@@ -8163,6 +8197,9 @@ function createWebServer({ htmlPath, assetsDir, webDir, host, port, openBrowser
81638197

81648198
const server = http.createServer((req, res) => {
81658199
const requestPath = (req.url || '/').split('?')[0];
8200+
if (typeof openaiBridgeHandler === 'function' && openaiBridgeHandler(req, res)) {
8201+
return;
8202+
}
81668203
if (requestPath === '/api/import-skills-zip') {
81678204
void handleImportSkillsZipUpload(req, res);
81688205
return;

0 commit comments

Comments
 (0)