@foxden-app/foxclaw 0.5.63 → 0.5.65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +13 -2
- package/CHANGELOG.md +22 -0
- package/dist/config.d.ts +14 -0
- package/dist/config.js +120 -0
- package/dist/controller/controller.js +22 -0
- package/dist/i18n.d.ts +10 -0
- package/dist/i18n.js +10 -0
- package/dist/main.js +8 -4
- package/package.json +1 -1
package/.env.example
CHANGED
|
@@ -27,8 +27,19 @@ CODEX_APP_SERVER_STATE_PATH=
|
|
|
27
27
|
CODEX_APP_SERVER_LOG_PATH=
|
|
28
28
|
CODEX_APP_SYNC_ON_OPEN=true
|
|
29
29
|
CODEX_APP_SYNC_ON_TURN_COMPLETE=false
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
# Optional OpenAI-compatible model providers for Codex app-server.
|
|
31
|
+
# Keys stay in environment variables; CODEX_API_PROVIDERS only references their names.
|
|
32
|
+
# Codex currently requires Responses-compatible endpoints. If your source URL is
|
|
33
|
+
# /v1/chat/completions, FoxClaw normalizes the base URL for display/injection but
|
|
34
|
+
# the upstream service still needs /v1/responses compatibility or a proxy shim.
|
|
35
|
+
# Compact form: id|url|ENV_KEY|model|display name
|
|
36
|
+
# CODEX_API_PROVIDERS=shop|https://example.com/v1|FOXCLAW_CODEX_API_KEY_SHOP|gpt-5.5|Shop Proxy
|
|
37
|
+
# CODEX_API_DEFAULT_PROVIDER=shop
|
|
38
|
+
# FOXCLAW_CODEX_API_KEY_SHOP=sk-...
|
|
39
|
+
# JSON form:
|
|
40
|
+
# CODEX_API_PROVIDERS=[{"id":"shop","baseUrl":"https://example.com/v1","apiKeyEnv":"FOXCLAW_CODEX_API_KEY_SHOP","model":"gpt-5.5","displayName":"Shop Proxy"}]
|
|
41
|
+
STORE_PATH=
|
|
42
|
+
LOG_LEVEL=info
|
|
32
43
|
DEFAULT_CWD=/absolute/path/to/workspace
|
|
33
44
|
DEFAULT_APPROVAL_POLICY=on-request
|
|
34
45
|
DEFAULT_SANDBOX_MODE=workspace-write
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable FoxClaw changes are listed here. Each release note is bilingual so GitHub Releases and the npm package are useful to both Chinese and English readers.
|
|
4
4
|
|
|
5
|
+
## 0.5.65 - 2026-06-29
|
|
6
|
+
|
|
7
|
+
### 中文
|
|
8
|
+
- 修复 Telegram 运行状态消息在网络/TLS 抖动下可能残留的问题:当 FoxClaw 编辑“正在运行命令”等 preview 消息失败并改发新状态消息时,旧 message id 现在会登记到最终清理列表。
|
|
9
|
+
- 保持“过程汇报”折叠消息的完整内容,不做自动裁剪;用户仍可通过 Telegram 展开、查看更多或弹窗查看全部过程。
|
|
10
|
+
|
|
11
|
+
### English
|
|
12
|
+
- Fixed stale Telegram runtime status messages after transient network/TLS edit failures. When FoxClaw fails to edit a "running command" preview and sends a replacement status message, the old message id is now tracked for final cleanup.
|
|
13
|
+
- Kept collapsed progress summaries complete rather than trimming them, preserving Telegram's expand, "show more", and full-view behavior.
|
|
14
|
+
|
|
15
|
+
## 0.5.64 - 2026-06-29
|
|
16
|
+
|
|
17
|
+
### 中文
|
|
18
|
+
- 新增 OpenAI-compatible API provider 配置支持:`CODEX_API_PROVIDERS` 可声明 provider id、base URL、key 环境变量、默认模型,并在启动 Codex app-server 时注入原生 `model_providers.*` 配置。
|
|
19
|
+
- 新增 `CODEX_API_DEFAULT_PROVIDER`,用于显式把 Codex 默认 `model_provider` 切到已配置的 API provider;未设置时只登记 provider,不会悄悄替换现有 ChatGPT/Codex 登录态。
|
|
20
|
+
- `/config` 现在展示 API provider 摘要、默认 provider、key 环境变量是否存在,并对 `/v1/chat/completions` 来源提示 Codex 当前需要 Responses-compatible 端点;API key 不会出现在面板或 Codex 配置 override 中。
|
|
21
|
+
|
|
22
|
+
### English
|
|
23
|
+
- Added OpenAI-compatible API provider configuration. `CODEX_API_PROVIDERS` can declare provider id, base URL, API-key environment variable, and default model, then FoxClaw injects native Codex `model_providers.*` app-server config.
|
|
24
|
+
- Added `CODEX_API_DEFAULT_PROVIDER` to explicitly switch Codex's default `model_provider` to a configured API provider. Without it, providers are registered but the existing ChatGPT/Codex auth flow is not silently replaced.
|
|
25
|
+
- `/config` now shows API provider summaries, the default provider, whether each key env var is present, and warns when the source URL was `/v1/chat/completions` because current Codex requires a Responses-compatible endpoint. API keys are not printed in panels or Codex config overrides.
|
|
26
|
+
|
|
5
27
|
## 0.5.63 - 2026-06-28
|
|
6
28
|
|
|
7
29
|
### 中文
|
package/dist/config.d.ts
CHANGED
|
@@ -31,6 +31,8 @@ export interface AppConfig {
|
|
|
31
31
|
codexAppServerLogPath: string;
|
|
32
32
|
codexAuthDir: string | null;
|
|
33
33
|
codexHome: string | null;
|
|
34
|
+
codexApiProviders: CodexApiProviderConfig[];
|
|
35
|
+
codexApiDefaultProvider: string | null;
|
|
34
36
|
codexAppSyncOnOpen: boolean;
|
|
35
37
|
codexAppSyncOnTurnComplete: boolean;
|
|
36
38
|
storePath: string;
|
|
@@ -79,6 +81,18 @@ export interface AppConfig {
|
|
|
79
81
|
voiceTextLimit: number;
|
|
80
82
|
voiceTtsTimeoutMs: number;
|
|
81
83
|
}
|
|
84
|
+
export interface CodexApiProviderConfig {
|
|
85
|
+
id: string;
|
|
86
|
+
name: string;
|
|
87
|
+
baseUrl: string;
|
|
88
|
+
apiKeyEnv: string;
|
|
89
|
+
model: string | null;
|
|
90
|
+
wireApi: 'responses';
|
|
91
|
+
sourceEndpoint: string | null;
|
|
92
|
+
chatCompletionsOnly: boolean;
|
|
93
|
+
}
|
|
82
94
|
export declare function loadConfig(): AppConfig;
|
|
95
|
+
export declare function buildCodexApiProviderOverrides(providers: readonly CodexApiProviderConfig[], defaultProviderId?: string | null): string[];
|
|
96
|
+
export declare function parseCodexApiProviders(raw: string | undefined): CodexApiProviderConfig[];
|
|
83
97
|
export declare function selectDefaultRuntimeBotToken(configuredTokens: string[], legacyToken: string | null): string | null;
|
|
84
98
|
export declare function ensureAppDirs(config: AppConfig): void;
|
package/dist/config.js
CHANGED
|
@@ -68,6 +68,8 @@ export function loadConfig() {
|
|
|
68
68
|
codexAppServerLogPath: process.env.CODEX_APP_SERVER_LOG_PATH || DEFAULT_CODEX_APP_SERVER_LOG_PATH,
|
|
69
69
|
codexAuthDir: process.env.CODEX_AUTH_DIR?.trim() || null,
|
|
70
70
|
codexHome: process.env.CODEX_HOME?.trim() || null,
|
|
71
|
+
codexApiProviders: parseCodexApiProviders(process.env.CODEX_API_PROVIDERS),
|
|
72
|
+
codexApiDefaultProvider: optionalSanitizedProviderId(process.env.CODEX_API_DEFAULT_PROVIDER),
|
|
71
73
|
codexAppSyncOnOpen: boolEnv('CODEX_APP_SYNC_ON_OPEN', true),
|
|
72
74
|
codexAppSyncOnTurnComplete: boolEnv('CODEX_APP_SYNC_ON_TURN_COMPLETE', false),
|
|
73
75
|
storePath: process.env.STORE_PATH || DEFAULT_STORE_PATH,
|
|
@@ -116,6 +118,124 @@ export function loadConfig() {
|
|
|
116
118
|
ensureAppDirs(config);
|
|
117
119
|
return config;
|
|
118
120
|
}
|
|
121
|
+
export function buildCodexApiProviderOverrides(providers, defaultProviderId = null) {
|
|
122
|
+
const overrides = [];
|
|
123
|
+
for (const provider of providers) {
|
|
124
|
+
overrides.push(`model_providers.${provider.id}=${tomlInlineTable({
|
|
125
|
+
name: provider.name,
|
|
126
|
+
base_url: provider.baseUrl,
|
|
127
|
+
env_key: provider.apiKeyEnv,
|
|
128
|
+
wire_api: provider.wireApi,
|
|
129
|
+
})}`);
|
|
130
|
+
}
|
|
131
|
+
if (defaultProviderId) {
|
|
132
|
+
const selected = providers.find(provider => provider.id === defaultProviderId);
|
|
133
|
+
if (!selected) {
|
|
134
|
+
throw new Error(`CODEX_API_DEFAULT_PROVIDER does not match any configured provider: ${defaultProviderId}`);
|
|
135
|
+
}
|
|
136
|
+
overrides.push(`model_provider=${tomlString(selected.id)}`);
|
|
137
|
+
if (selected.model) {
|
|
138
|
+
overrides.push(`model=${tomlString(selected.model)}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
return overrides;
|
|
142
|
+
}
|
|
143
|
+
export function parseCodexApiProviders(raw) {
|
|
144
|
+
if (!raw?.trim())
|
|
145
|
+
return [];
|
|
146
|
+
const trimmed = raw.trim();
|
|
147
|
+
if (trimmed.startsWith('[')) {
|
|
148
|
+
const parsed = JSON.parse(trimmed);
|
|
149
|
+
if (!Array.isArray(parsed)) {
|
|
150
|
+
throw new Error('CODEX_API_PROVIDERS JSON must be an array');
|
|
151
|
+
}
|
|
152
|
+
return parsed.map((entry, index) => normalizeCodexApiProvider(entry, index));
|
|
153
|
+
}
|
|
154
|
+
return trimmed
|
|
155
|
+
.split(',')
|
|
156
|
+
.map((entry) => entry.trim())
|
|
157
|
+
.filter(Boolean)
|
|
158
|
+
.map((entry, index) => {
|
|
159
|
+
const parts = entry.split('|').map((part) => part.trim());
|
|
160
|
+
if (parts.length < 3 || parts.length > 5) {
|
|
161
|
+
throw new Error('CODEX_API_PROVIDERS entries must use id|url|env_key[|model][|name]');
|
|
162
|
+
}
|
|
163
|
+
const [id, url, apiKeyEnv, model, name] = parts;
|
|
164
|
+
return normalizeCodexApiProvider({ id, url, apiKeyEnv, model, displayName: name }, index);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
function normalizeCodexApiProvider(entry, index) {
|
|
168
|
+
if (!entry || typeof entry !== 'object') {
|
|
169
|
+
throw new Error(`CODEX_API_PROVIDERS[${index}] must be an object`);
|
|
170
|
+
}
|
|
171
|
+
const record = entry;
|
|
172
|
+
const id = sanitizeCodexProviderId(stringField(record, ['id'], index));
|
|
173
|
+
const sourceEndpoint = optionalStringField(record, ['endpoint', 'api', 'url', 'baseUrl', 'base_url']);
|
|
174
|
+
if (!sourceEndpoint) {
|
|
175
|
+
throw new Error(`CODEX_API_PROVIDERS[${index}] is missing url/baseUrl`);
|
|
176
|
+
}
|
|
177
|
+
const normalized = normalizeOpenAiCompatibleBaseUrl(sourceEndpoint);
|
|
178
|
+
const apiKeyEnv = stringField(record, ['apiKeyEnv', 'api_key_env', 'envKey', 'env_key'], index);
|
|
179
|
+
if (!/^[A-Za-z_][A-Za-z0-9_]*$/.test(apiKeyEnv)) {
|
|
180
|
+
throw new Error(`CODEX_API_PROVIDERS[${index}] env key is not a valid environment variable name`);
|
|
181
|
+
}
|
|
182
|
+
const displayName = optionalStringField(record, ['displayName', 'display_name', 'label']) ?? id;
|
|
183
|
+
return {
|
|
184
|
+
id,
|
|
185
|
+
name: displayName,
|
|
186
|
+
baseUrl: normalized.baseUrl,
|
|
187
|
+
apiKeyEnv,
|
|
188
|
+
model: optionalStringField(record, ['model']),
|
|
189
|
+
wireApi: 'responses',
|
|
190
|
+
sourceEndpoint,
|
|
191
|
+
chatCompletionsOnly: normalized.chatCompletionsEndpoint,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
function normalizeOpenAiCompatibleBaseUrl(input) {
|
|
195
|
+
const trimmed = input.trim().replace(/\/+$/, '');
|
|
196
|
+
if (!trimmed) {
|
|
197
|
+
throw new Error('CODEX_API_PROVIDERS contains an empty URL');
|
|
198
|
+
}
|
|
199
|
+
const chatSuffix = '/chat/completions';
|
|
200
|
+
if (trimmed.endsWith(chatSuffix)) {
|
|
201
|
+
return { baseUrl: trimmed.slice(0, -chatSuffix.length), chatCompletionsEndpoint: true };
|
|
202
|
+
}
|
|
203
|
+
return { baseUrl: trimmed, chatCompletionsEndpoint: false };
|
|
204
|
+
}
|
|
205
|
+
function stringField(record, keys, index) {
|
|
206
|
+
const value = optionalStringField(record, keys);
|
|
207
|
+
if (!value) {
|
|
208
|
+
throw new Error(`CODEX_API_PROVIDERS[${index}] is missing ${keys[0]}`);
|
|
209
|
+
}
|
|
210
|
+
return value;
|
|
211
|
+
}
|
|
212
|
+
function optionalStringField(record, keys) {
|
|
213
|
+
for (const key of keys) {
|
|
214
|
+
const value = record[key];
|
|
215
|
+
if (typeof value === 'string' && value.trim()) {
|
|
216
|
+
return value.trim();
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return null;
|
|
220
|
+
}
|
|
221
|
+
function sanitizeCodexProviderId(value) {
|
|
222
|
+
const sanitized = value.trim().toLowerCase().replace(/[^a-z0-9_-]+/g, '-').replace(/^-+|-+$/g, '');
|
|
223
|
+
if (!sanitized) {
|
|
224
|
+
throw new Error('CODEX_API_PROVIDERS contains an invalid provider id');
|
|
225
|
+
}
|
|
226
|
+
return sanitized;
|
|
227
|
+
}
|
|
228
|
+
function optionalSanitizedProviderId(value) {
|
|
229
|
+
if (!value?.trim())
|
|
230
|
+
return null;
|
|
231
|
+
return sanitizeCodexProviderId(value);
|
|
232
|
+
}
|
|
233
|
+
function tomlInlineTable(values) {
|
|
234
|
+
return `{ ${Object.entries(values).map(([key, value]) => `${key} = ${tomlString(value)}`).join(', ')} }`;
|
|
235
|
+
}
|
|
236
|
+
function tomlString(value) {
|
|
237
|
+
return JSON.stringify(value);
|
|
238
|
+
}
|
|
119
239
|
export function selectDefaultRuntimeBotToken(configuredTokens, legacyToken) {
|
|
120
240
|
if (configuredTokens.length === 0 || !legacyToken)
|
|
121
241
|
return null;
|
|
@@ -8152,6 +8152,7 @@ export class BridgeSessionCore {
|
|
|
8152
8152
|
active.previewActive = false;
|
|
8153
8153
|
active.statusMessageText = null;
|
|
8154
8154
|
active.statusNeedsRebase = false;
|
|
8155
|
+
this.recordArchivedMessageId(active, active.previewMessageId);
|
|
8155
8156
|
this.store.removeActiveTurnPreview(active.turnId);
|
|
8156
8157
|
await this.ensureStatusMessage(active, text);
|
|
8157
8158
|
return;
|
|
@@ -9471,8 +9472,29 @@ function formatConfigMessage(locale, result, appConfig, authPoolStats) {
|
|
|
9471
9472
|
lines.push(t(locale, 'config_panel_ttl', { value: formatDurationMs(locale, appConfig.telegramPanelTtlMs) }));
|
|
9472
9473
|
lines.push(`TELEGRAM_PANEL_TTL_MS=${appConfig.telegramPanelTtlMs}`);
|
|
9473
9474
|
lines.push(formatCodexAuthPoolSummary(locale, authPoolStats));
|
|
9475
|
+
lines.push(...formatCodexApiProviderConfigLines(locale, appConfig));
|
|
9474
9476
|
return lines.join('\n');
|
|
9475
9477
|
}
|
|
9478
|
+
function formatCodexApiProviderConfigLines(locale, appConfig) {
|
|
9479
|
+
if (appConfig.codexApiProviders.length === 0) {
|
|
9480
|
+
return [t(locale, 'config_api_providers_none')];
|
|
9481
|
+
}
|
|
9482
|
+
const lines = [t(locale, 'config_api_providers_title')];
|
|
9483
|
+
lines.push(t(locale, 'config_api_default_provider', { value: appConfig.codexApiDefaultProvider ?? '-' }));
|
|
9484
|
+
for (const provider of appConfig.codexApiProviders) {
|
|
9485
|
+
lines.push(t(locale, 'config_api_provider_line', {
|
|
9486
|
+
id: provider.id,
|
|
9487
|
+
model: provider.model ?? '-',
|
|
9488
|
+
base: provider.baseUrl,
|
|
9489
|
+
env: provider.apiKeyEnv,
|
|
9490
|
+
key: process.env[provider.apiKeyEnv]?.trim() ? t(locale, 'yes') : t(locale, 'no'),
|
|
9491
|
+
}));
|
|
9492
|
+
if (provider.chatCompletionsOnly) {
|
|
9493
|
+
lines.push(t(locale, 'config_api_provider_chat_warning', { id: provider.id }));
|
|
9494
|
+
}
|
|
9495
|
+
}
|
|
9496
|
+
return lines;
|
|
9497
|
+
}
|
|
9476
9498
|
function formatCodexAuthPoolSummary(locale, stats) {
|
|
9477
9499
|
return t(locale, 'auth_pool_summary', {
|
|
9478
9500
|
total: stats.totalSeen,
|
package/dist/i18n.d.ts
CHANGED
|
@@ -623,6 +623,11 @@ declare const MESSAGES: {
|
|
|
623
623
|
readonly config_delete_tool_details_updated: "Delete operation details after final reply set to: {value}";
|
|
624
624
|
readonly config_delete_tool_details_usage: "Usage: /config delete_tool_details <on|off>";
|
|
625
625
|
readonly config_panel_ttl: "Interactive panel cleanup: {value}";
|
|
626
|
+
readonly config_api_providers_none: "API providers: none configured";
|
|
627
|
+
readonly config_api_providers_title: "API providers:";
|
|
628
|
+
readonly config_api_default_provider: "Default API provider: {value}";
|
|
629
|
+
readonly config_api_provider_line: "- {id}: model {model}, base {base}, key env {env} present {key}";
|
|
630
|
+
readonly config_api_provider_chat_warning: " {id}: source was /chat/completions; Codex requires a Responses-compatible endpoint.";
|
|
626
631
|
readonly config_env_update_failed: "Runtime setting changed, but updating {value} failed: {error}";
|
|
627
632
|
readonly requirements_title: "Config requirements";
|
|
628
633
|
readonly requirements_empty: "No config requirements are configured.";
|
|
@@ -1329,6 +1334,11 @@ declare const MESSAGES: {
|
|
|
1329
1334
|
readonly config_delete_tool_details_updated: "最终回复后删除操作明细已设置为:{value}";
|
|
1330
1335
|
readonly config_delete_tool_details_usage: "用法:/config delete_tool_details <on|off>";
|
|
1331
1336
|
readonly config_panel_ttl: "交互面板自动清理:{value}";
|
|
1337
|
+
readonly config_api_providers_none: "API Provider:未配置";
|
|
1338
|
+
readonly config_api_providers_title: "API Provider:";
|
|
1339
|
+
readonly config_api_default_provider: "默认 API Provider:{value}";
|
|
1340
|
+
readonly config_api_provider_line: "- {id}:模型 {model},base {base},key 环境变量 {env} 存在 {key}";
|
|
1341
|
+
readonly config_api_provider_chat_warning: " {id}:来源是 /chat/completions;Codex 需要兼容 Responses 的端点。";
|
|
1332
1342
|
readonly config_env_update_failed: "运行时设置已改变,但更新 {value} 失败:{error}";
|
|
1333
1343
|
readonly requirements_title: "配置要求";
|
|
1334
1344
|
readonly requirements_empty: "当前没有配置要求。";
|
package/dist/i18n.js
CHANGED
|
@@ -621,6 +621,11 @@ const MESSAGES = {
|
|
|
621
621
|
config_delete_tool_details_updated: 'Delete operation details after final reply set to: {value}',
|
|
622
622
|
config_delete_tool_details_usage: 'Usage: /config delete_tool_details <on|off>',
|
|
623
623
|
config_panel_ttl: 'Interactive panel cleanup: {value}',
|
|
624
|
+
config_api_providers_none: 'API providers: none configured',
|
|
625
|
+
config_api_providers_title: 'API providers:',
|
|
626
|
+
config_api_default_provider: 'Default API provider: {value}',
|
|
627
|
+
config_api_provider_line: '- {id}: model {model}, base {base}, key env {env} present {key}',
|
|
628
|
+
config_api_provider_chat_warning: ' {id}: source was /chat/completions; Codex requires a Responses-compatible endpoint.',
|
|
624
629
|
config_env_update_failed: 'Runtime setting changed, but updating {value} failed: {error}',
|
|
625
630
|
requirements_title: 'Config requirements',
|
|
626
631
|
requirements_empty: 'No config requirements are configured.',
|
|
@@ -1327,6 +1332,11 @@ const MESSAGES = {
|
|
|
1327
1332
|
config_delete_tool_details_updated: '最终回复后删除操作明细已设置为:{value}',
|
|
1328
1333
|
config_delete_tool_details_usage: '用法:/config delete_tool_details <on|off>',
|
|
1329
1334
|
config_panel_ttl: '交互面板自动清理:{value}',
|
|
1335
|
+
config_api_providers_none: 'API Provider:未配置',
|
|
1336
|
+
config_api_providers_title: 'API Provider:',
|
|
1337
|
+
config_api_default_provider: '默认 API Provider:{value}',
|
|
1338
|
+
config_api_provider_line: '- {id}:模型 {model},base {base},key 环境变量 {env} 存在 {key}',
|
|
1339
|
+
config_api_provider_chat_warning: ' {id}:来源是 /chat/completions;Codex 需要兼容 Responses 的端点。',
|
|
1330
1340
|
config_env_update_failed: '运行时设置已改变,但更新 {value} 失败:{error}',
|
|
1331
1341
|
requirements_title: '配置要求',
|
|
1332
1342
|
requirements_empty: '当前没有配置要求。',
|
package/dist/main.js
CHANGED
|
@@ -7,7 +7,7 @@ import process from 'node:process';
|
|
|
7
7
|
import { createInterface } from 'node:readline/promises';
|
|
8
8
|
import { spawnSync } from 'node:child_process';
|
|
9
9
|
import { fileURLToPath } from 'node:url';
|
|
10
|
-
import { APP_HOME, DEFAULT_CODEX_TELEGRAM_HOME, DEFAULT_ENV_PATH, DEFAULT_LOG_PATH, DEFAULT_STATUS_PATH, getLoadedEnvPath, loadConfig, loadEnv, } from './config.js';
|
|
10
|
+
import { APP_HOME, buildCodexApiProviderOverrides, DEFAULT_CODEX_TELEGRAM_HOME, DEFAULT_ENV_PATH, DEFAULT_LOG_PATH, DEFAULT_STATUS_PATH, getLoadedEnvPath, loadConfig, loadEnv, } from './config.js';
|
|
11
11
|
import { createAuthRefreshNotificationAggregator, } from './auth/notifications.js';
|
|
12
12
|
import { installBundledCodexSkills } from './codex_skills.js';
|
|
13
13
|
import { acquireProcessLock, LockHeldError } from './lock.js';
|
|
@@ -448,6 +448,7 @@ async function runServeCli() {
|
|
|
448
448
|
]);
|
|
449
449
|
const config = loadConfig();
|
|
450
450
|
const logger = new Logger(config.logLevel, config.logPath);
|
|
451
|
+
const codexApiProviderOverrides = buildCodexApiProviderOverrides(config.codexApiProviders, config.codexApiDefaultProvider);
|
|
451
452
|
const authNotificationAggregator = createAuthRefreshNotificationAggregator(logger);
|
|
452
453
|
attachIlinkRuntimeFromBridgeLogger(logger, config.wxIlinkRouteTag);
|
|
453
454
|
const processLock = acquireProcessLock(config.lockPath);
|
|
@@ -496,7 +497,10 @@ async function runServeCli() {
|
|
|
496
497
|
const childEnv = sharedDefaultRuntime
|
|
497
498
|
? (config.codexHome ? { CODEX_HOME: config.codexHome } : null)
|
|
498
499
|
: { CODEX_HOME: home };
|
|
499
|
-
const app = new CodexAppClient(runtimeConfig.codexCliBin, runtimeConfig.codexAppLaunchCmd, runtimeConfig.codexAppAutolaunch, runtimeConfig.codexAppServerStatePath, runtimeConfig.codexAppServerLogPath, logger, childEnv,
|
|
500
|
+
const app = new CodexAppClient(runtimeConfig.codexCliBin, runtimeConfig.codexAppLaunchCmd, runtimeConfig.codexAppAutolaunch, runtimeConfig.codexAppServerStatePath, runtimeConfig.codexAppServerLogPath, logger, childEnv, [
|
|
501
|
+
...codexApiProviderOverrides,
|
|
502
|
+
...(sharedDefaultRuntime ? [] : ['cli_auth_credentials_store="file"']),
|
|
503
|
+
]);
|
|
500
504
|
seeds.push({ id, home, authDir, sharedDefaultRuntime, config: runtimeConfig, bot, app });
|
|
501
505
|
}
|
|
502
506
|
let authSync = null;
|
|
@@ -670,7 +674,7 @@ async function runServeCli() {
|
|
|
670
674
|
runtimes.push({ ...seed, core, telegram: new TelegramChannelAdapter(core) });
|
|
671
675
|
}
|
|
672
676
|
if (config.wxEnabled) {
|
|
673
|
-
const weixinApp = new CodexAppClient(config.codexCliBin, config.codexAppLaunchCmd, config.codexAppAutolaunch, config.codexAppServerStatePath, config.codexAppServerLogPath, logger);
|
|
677
|
+
const weixinApp = new CodexAppClient(config.codexCliBin, config.codexAppLaunchCmd, config.codexAppAutolaunch, config.codexAppServerStatePath, config.codexAppServerLogPath, logger, null, codexApiProviderOverrides);
|
|
674
678
|
const outbound = new BridgeMessagingRouter(new TelegramMessagingPort(seeds[0].bot), new WeixinMessagingPort(store, (id) => loadWeixinAccount(config.weixinAccountsDir, id)));
|
|
675
679
|
activeWeixinCore = new BridgeSessionCore(config, store, logger, seeds[0].bot, weixinApp, outbound, selfUpdater, coordinator, false);
|
|
676
680
|
managedApps.push(weixinApp);
|
|
@@ -764,7 +768,7 @@ async function runServeCli() {
|
|
|
764
768
|
const singleCodexHome = config.codexHome ?? path.join(os.homedir(), '.codex');
|
|
765
769
|
installBundledCodexSkills(packageRoot, singleCodexHome);
|
|
766
770
|
const bot = new TelegramGateway(config.tgBotToken, config.tgAllowedUserId, config.tgAllowedChatId, config.telegramPollIntervalMs, store, logger);
|
|
767
|
-
const app = new CodexAppClient(config.codexCliBin, config.codexAppLaunchCmd, config.codexAppAutolaunch, config.codexAppServerStatePath, config.codexAppServerLogPath, logger);
|
|
771
|
+
const app = new CodexAppClient(config.codexCliBin, config.codexAppLaunchCmd, config.codexAppAutolaunch, config.codexAppServerStatePath, config.codexAppServerLogPath, logger, config.codexHome ? { CODEX_HOME: config.codexHome } : null, codexApiProviderOverrides);
|
|
768
772
|
const telegramMessaging = new TelegramMessagingPort(bot);
|
|
769
773
|
const weixinMessaging = config.wxEnabled
|
|
770
774
|
? new WeixinMessagingPort(store, (id) => loadWeixinAccount(config.weixinAccountsDir, id))
|