aicodeswitch 3.9.3 → 4.0.0
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/README.md +37 -36
- package/UPGRADE.md +5 -3
- package/bin/restore.js +126 -22
- package/bin/start.js +29 -41
- package/bin/stop.js +3 -3
- package/bin/utils/config-helpers.js +198 -0
- package/dist/server/config-managed-fields.js +69 -0
- package/dist/server/config-merge.js +260 -0
- package/dist/server/database-factory.js +11 -181
- package/dist/server/fs-database.js +211 -31
- package/dist/server/main.js +380 -241
- package/dist/server/original-config-reader.js +154 -88
- package/dist/server/proxy-server.js +993 -385
- package/dist/server/rules-status-service.js +285 -54
- package/dist/server/transformers/chunk-collector.js +26 -4
- package/dist/server/transformers/streaming.js +2334 -280
- package/dist/server/transformers/transformers.js +1765 -0
- package/dist/ui/assets/index-GQBwe1Rm.js +514 -0
- package/dist/ui/index.html +1 -1
- package/package.json +4 -8
- package/schema/claude.schema.md +15 -14
- package/schema/deepseek-chat.schema.md +799 -0
- package/schema/{openai.schema.md → openai-chat-completions.schema.md} +9 -1083
- package/schema/openai-responses.schema.md +226196 -0
- package/schema/stream.md +2592 -0
- package/dist/server/database.js +0 -1609
- package/dist/server/migrate-to-fs.js +0 -353
- package/dist/server/transformers/claude-openai.js +0 -868
- package/dist/server/transformers/gemini.js +0 -625
- package/dist/ui/assets/index-DNtgPQMm.js +0 -511
package/dist/server/main.js
CHANGED
|
@@ -30,6 +30,8 @@ const websocket_service_1 = require("./websocket-service");
|
|
|
30
30
|
const rules_status_service_1 = require("./rules-status-service");
|
|
31
31
|
const type_migration_1 = require("./type-migration");
|
|
32
32
|
const config_metadata_1 = require("./config-metadata");
|
|
33
|
+
const config_merge_1 = require("./config-merge");
|
|
34
|
+
const config_managed_fields_1 = require("./config-managed-fields");
|
|
33
35
|
const config_1 = require("./config");
|
|
34
36
|
const appDir = path_1.default.join(os_1.default.homedir(), '.aicodeswitch');
|
|
35
37
|
const legacyDataDir = path_1.default.join(appDir, 'data');
|
|
@@ -104,7 +106,13 @@ const asyncHandler = (handler) => (req, res, next) => {
|
|
|
104
106
|
next(err);
|
|
105
107
|
});
|
|
106
108
|
};
|
|
107
|
-
const
|
|
109
|
+
const VALID_CLAUDE_EFFORT_LEVELS = ['low', 'medium', 'high', 'max'];
|
|
110
|
+
const DEFAULT_CLAUDE_EFFORT_LEVEL = 'medium';
|
|
111
|
+
const isClaudeEffortLevel = (value) => {
|
|
112
|
+
return typeof value === 'string' && VALID_CLAUDE_EFFORT_LEVELS.includes(value);
|
|
113
|
+
};
|
|
114
|
+
const writeClaudeConfig = (dbManager_1, enableAgentTeams_1, enableBypassPermissionsSupport_1, effortLevel_1, defaultModel_1, ...args_1) => __awaiter(void 0, [dbManager_1, enableAgentTeams_1, enableBypassPermissionsSupport_1, effortLevel_1, defaultModel_1, ...args_1], void 0, function* (dbManager, enableAgentTeams, enableBypassPermissionsSupport, effortLevel, defaultModel, options = {}) {
|
|
115
|
+
var _a;
|
|
108
116
|
try {
|
|
109
117
|
const homeDir = os_1.default.homedir();
|
|
110
118
|
const port = process.env.PORT ? parseInt(process.env.PORT, 10) : 4567;
|
|
@@ -116,29 +124,44 @@ const writeClaudeConfig = (dbManager, enableAgentTeams, enableBypassPermissionsS
|
|
|
116
124
|
const claudeJsonBakPath = path_1.default.join(homeDir, '.claude.json.aicodeswitch_backup');
|
|
117
125
|
// 使用新的配置状态检测来判断是否可以写入
|
|
118
126
|
const configStatus = (0, config_metadata_1.checkClaudeConfigStatus)();
|
|
127
|
+
const isRuntimeRefresh = options.allowOverwriteRefresh === true && configStatus.isOverwritten;
|
|
119
128
|
// 只有当当前配置已经是代理配置时,才拒绝写入
|
|
120
|
-
if (configStatus.isOverwritten) {
|
|
129
|
+
if (configStatus.isOverwritten && !isRuntimeRefresh) {
|
|
121
130
|
console.error('Claude config has already been overwritten. Please restore the original config first.');
|
|
122
131
|
return false;
|
|
123
132
|
}
|
|
124
133
|
// 如果 .aicodeswitch_backup 文件不存在,才进行备份(避免覆盖已有备份)
|
|
125
|
-
let originalSettingsHash =
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
//
|
|
131
|
-
fs_1.default.
|
|
134
|
+
let originalSettingsHash = isRuntimeRefresh
|
|
135
|
+
? (_a = configStatus.metadata) === null || _a === void 0 ? void 0 : _a.originalHash
|
|
136
|
+
: undefined;
|
|
137
|
+
if (!isRuntimeRefresh) {
|
|
138
|
+
if (!fs_1.default.existsSync(claudeSettingsBakPath)) {
|
|
139
|
+
// 计算原始配置文件的 hash(如果存在)
|
|
140
|
+
if (fs_1.default.existsSync(claudeSettingsPath)) {
|
|
141
|
+
originalSettingsHash = (0, crypto_1.createHash)('sha256').update(fs_1.default.readFileSync(claudeSettingsPath, 'utf-8')).digest('hex');
|
|
142
|
+
// 备份当前配置文件
|
|
143
|
+
fs_1.default.renameSync(claudeSettingsPath, claudeSettingsBakPath);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else {
|
|
147
|
+
// .aicodeswitch_backup 已存在,直接使用现有的备份文件
|
|
148
|
+
console.log('Backup file already exists, skipping backup step');
|
|
132
149
|
}
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
// .aicodeswitch_backup 已存在,直接使用现有的备份文件
|
|
136
|
-
console.log('Backup file already exists, skipping backup step');
|
|
137
150
|
}
|
|
138
151
|
if (!fs_1.default.existsSync(claudeDir)) {
|
|
139
152
|
fs_1.default.mkdirSync(claudeDir, { recursive: true });
|
|
140
153
|
}
|
|
141
|
-
//
|
|
154
|
+
// 读取当前配置(如果存在),保留工具运行时写入的内容
|
|
155
|
+
let currentSettings = {};
|
|
156
|
+
if (fs_1.default.existsSync(claudeSettingsPath)) {
|
|
157
|
+
try {
|
|
158
|
+
currentSettings = JSON.parse(fs_1.default.readFileSync(claudeSettingsPath, 'utf-8'));
|
|
159
|
+
}
|
|
160
|
+
catch (error) {
|
|
161
|
+
console.warn('Failed to parse current settings.json, using empty object:', error);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
// 构建代理配置
|
|
142
165
|
const claudeSettingsEnv = {
|
|
143
166
|
ANTHROPIC_AUTH_TOKEN: config.apiKey || "api_key",
|
|
144
167
|
ANTHROPIC_API_KEY: "",
|
|
@@ -150,32 +173,56 @@ const writeClaudeConfig = (dbManager, enableAgentTeams, enableBypassPermissionsS
|
|
|
150
173
|
if (enableAgentTeams) {
|
|
151
174
|
claudeSettingsEnv.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
|
|
152
175
|
}
|
|
153
|
-
const
|
|
176
|
+
const proxySettings = {
|
|
154
177
|
env: claudeSettingsEnv
|
|
155
178
|
};
|
|
156
179
|
// 如果开启对bypassPermissions的支持,添加对应的配置项
|
|
157
180
|
if (enableBypassPermissionsSupport) {
|
|
158
|
-
|
|
181
|
+
proxySettings.permissions = {
|
|
159
182
|
defaultMode: "bypassPermissions"
|
|
160
183
|
};
|
|
161
|
-
|
|
184
|
+
proxySettings.skipDangerousModePermissionPrompt = true;
|
|
185
|
+
}
|
|
186
|
+
// 如果设置了 effortLevel,添加对应的配置项
|
|
187
|
+
if (effortLevel && isClaudeEffortLevel(effortLevel)) {
|
|
188
|
+
proxySettings.effortLevel = effortLevel;
|
|
189
|
+
}
|
|
190
|
+
// 如果设置了默认模型,添加对应的配置项
|
|
191
|
+
if (defaultModel && typeof defaultModel === 'string' && defaultModel.trim()) {
|
|
192
|
+
proxySettings.model = defaultModel.trim();
|
|
162
193
|
}
|
|
163
|
-
|
|
194
|
+
// 使用智能合并:将代理配置的管理字段写入,保留当前配置的非管理字段
|
|
195
|
+
const mergedSettings = (0, config_merge_1.mergeJsonConfig)(proxySettings, currentSettings, config_managed_fields_1.CLAUDE_SETTINGS_MANAGED_FIELDS);
|
|
196
|
+
// 原子性写入合并后的配置
|
|
197
|
+
(0, config_merge_1.atomicWriteFile)(claudeSettingsPath, JSON.stringify(mergedSettings, null, 2));
|
|
164
198
|
// Claude Code .claude.json
|
|
165
199
|
const claudeJsonPath = path_1.default.join(homeDir, '.claude.json');
|
|
166
|
-
//
|
|
167
|
-
let
|
|
200
|
+
// 读取当前配置(如果存在),保留工具运行时写入的内容
|
|
201
|
+
let currentClaudeJson = {};
|
|
168
202
|
if (fs_1.default.existsSync(claudeJsonPath)) {
|
|
169
|
-
|
|
203
|
+
try {
|
|
204
|
+
currentClaudeJson = JSON.parse(fs_1.default.readFileSync(claudeJsonPath, 'utf-8'));
|
|
205
|
+
}
|
|
206
|
+
catch (error) {
|
|
207
|
+
console.warn('Failed to parse current .claude.json, using empty object:', error);
|
|
208
|
+
}
|
|
170
209
|
}
|
|
171
210
|
// 然后处理备份
|
|
172
|
-
if (!
|
|
173
|
-
if (fs_1.default.existsSync(
|
|
174
|
-
fs_1.default.
|
|
211
|
+
if (!isRuntimeRefresh) {
|
|
212
|
+
if (!fs_1.default.existsSync(claudeJsonBakPath)) {
|
|
213
|
+
if (fs_1.default.existsSync(claudeJsonPath)) {
|
|
214
|
+
fs_1.default.renameSync(claudeJsonPath, claudeJsonBakPath);
|
|
215
|
+
}
|
|
175
216
|
}
|
|
176
217
|
}
|
|
177
|
-
|
|
178
|
-
|
|
218
|
+
// 构建代理配置
|
|
219
|
+
const proxyClaudeJson = {
|
|
220
|
+
hasCompletedOnboarding: true
|
|
221
|
+
};
|
|
222
|
+
// 使用智能合并
|
|
223
|
+
const mergedClaudeJson = (0, config_merge_1.mergeJsonConfig)(proxyClaudeJson, currentClaudeJson, config_managed_fields_1.CLAUDE_JSON_MANAGED_FIELDS);
|
|
224
|
+
// 原子性写入合并后的配置
|
|
225
|
+
(0, config_merge_1.atomicWriteFile)(claudeJsonPath, JSON.stringify(mergedClaudeJson, null, 2));
|
|
179
226
|
// 保存元数据
|
|
180
227
|
const currentSettingsHash = (0, crypto_1.createHash)('sha256').update(fs_1.default.readFileSync(claudeSettingsPath, 'utf-8')).digest('hex');
|
|
181
228
|
const metadata = {
|
|
@@ -203,125 +250,13 @@ const writeClaudeConfig = (dbManager, enableAgentTeams, enableBypassPermissionsS
|
|
|
203
250
|
return false;
|
|
204
251
|
}
|
|
205
252
|
});
|
|
206
|
-
|
|
207
|
-
* 更新Claude Code配置中的Agent Teams设置
|
|
208
|
-
* 此函数假设配置文件已经被代理覆盖,直接修改环境变量而不重新备份
|
|
209
|
-
*/
|
|
210
|
-
const updateClaudeAgentTeamsConfig = (enableAgentTeams) => __awaiter(void 0, void 0, void 0, function* () {
|
|
211
|
-
try {
|
|
212
|
-
const homeDir = os_1.default.homedir();
|
|
213
|
-
const claudeSettingsPath = path_1.default.join(homeDir, '.claude/settings.json');
|
|
214
|
-
// 检查配置文件是否存在
|
|
215
|
-
if (!fs_1.default.existsSync(claudeSettingsPath)) {
|
|
216
|
-
console.error('Claude settings.json does not exist');
|
|
217
|
-
return false;
|
|
218
|
-
}
|
|
219
|
-
// 读取当前配置
|
|
220
|
-
const currentContent = fs_1.default.readFileSync(claudeSettingsPath, 'utf-8');
|
|
221
|
-
const currentConfig = JSON.parse(currentContent);
|
|
222
|
-
// 检查是否是代理配置
|
|
223
|
-
const configStatus = (0, config_metadata_1.checkClaudeConfigStatus)();
|
|
224
|
-
if (!configStatus.isOverwritten) {
|
|
225
|
-
console.error('Claude config is not overwritten by proxy. Please activate a route first.');
|
|
226
|
-
return false;
|
|
227
|
-
}
|
|
228
|
-
// 更新或删除Agent Teams环境变量
|
|
229
|
-
if (enableAgentTeams) {
|
|
230
|
-
currentConfig.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS = "1";
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
delete currentConfig.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
234
|
-
}
|
|
235
|
-
// 写入更新后的配置
|
|
236
|
-
fs_1.default.writeFileSync(claudeSettingsPath, JSON.stringify(currentConfig, null, 2));
|
|
237
|
-
// 更新元数据中的当前配置hash
|
|
238
|
-
const metadata = (0, config_metadata_1.loadMetadata)('claude');
|
|
239
|
-
if (metadata && metadata.files[0]) {
|
|
240
|
-
metadata.files[0].currentHash = (0, crypto_1.createHash)('sha256')
|
|
241
|
-
.update(fs_1.default.readFileSync(claudeSettingsPath, 'utf-8'))
|
|
242
|
-
.digest('hex');
|
|
243
|
-
metadata.timestamp = Date.now();
|
|
244
|
-
(0, config_metadata_1.saveMetadata)(metadata);
|
|
245
|
-
}
|
|
246
|
-
return true;
|
|
247
|
-
}
|
|
248
|
-
catch (error) {
|
|
249
|
-
console.error('Failed to update Claude Agent Teams config:', error);
|
|
250
|
-
return false;
|
|
251
|
-
}
|
|
252
|
-
});
|
|
253
|
-
/**
|
|
254
|
-
* 更新Claude Code配置中的bypassPermissions支持设置
|
|
255
|
-
* 此函数假设配置文件已经被代理覆盖,直接修改配置而不重新备份
|
|
256
|
-
*/
|
|
257
|
-
const updateClaudeBypassPermissionsSupportConfig = (enableBypassPermissionsSupport) => __awaiter(void 0, void 0, void 0, function* () {
|
|
258
|
-
try {
|
|
259
|
-
const homeDir = os_1.default.homedir();
|
|
260
|
-
const claudeSettingsPath = path_1.default.join(homeDir, '.claude/settings.json');
|
|
261
|
-
// 检查配置文件是否存在
|
|
262
|
-
if (!fs_1.default.existsSync(claudeSettingsPath)) {
|
|
263
|
-
console.error('Claude settings.json does not exist');
|
|
264
|
-
return false;
|
|
265
|
-
}
|
|
266
|
-
// 读取当前配置
|
|
267
|
-
const currentContent = fs_1.default.readFileSync(claudeSettingsPath, 'utf-8');
|
|
268
|
-
const currentConfig = JSON.parse(currentContent);
|
|
269
|
-
// 检查是否是代理配置
|
|
270
|
-
const configStatus = (0, config_metadata_1.checkClaudeConfigStatus)();
|
|
271
|
-
if (!configStatus.isOverwritten) {
|
|
272
|
-
console.error('Claude config is not overwritten by proxy. Please activate a route first.');
|
|
273
|
-
return false;
|
|
274
|
-
}
|
|
275
|
-
// 更新或删除bypassPermissions支持配置项
|
|
276
|
-
if (enableBypassPermissionsSupport) {
|
|
277
|
-
currentConfig.permissions = {
|
|
278
|
-
defaultMode: "bypassPermissions"
|
|
279
|
-
};
|
|
280
|
-
currentConfig.skipDangerousModePermissionPrompt = true;
|
|
281
|
-
}
|
|
282
|
-
else {
|
|
283
|
-
delete currentConfig.permissions;
|
|
284
|
-
delete currentConfig.skipDangerousModePermissionPrompt;
|
|
285
|
-
}
|
|
286
|
-
// 写入更新后的配置
|
|
287
|
-
fs_1.default.writeFileSync(claudeSettingsPath, JSON.stringify(currentConfig, null, 2));
|
|
288
|
-
// 更新元数据中的当前配置hash
|
|
289
|
-
const metadata = (0, config_metadata_1.loadMetadata)('claude');
|
|
290
|
-
if (metadata && metadata.files[0]) {
|
|
291
|
-
metadata.files[0].currentHash = (0, crypto_1.createHash)('sha256')
|
|
292
|
-
.update(fs_1.default.readFileSync(claudeSettingsPath, 'utf-8'))
|
|
293
|
-
.digest('hex');
|
|
294
|
-
metadata.timestamp = Date.now();
|
|
295
|
-
(0, config_metadata_1.saveMetadata)(metadata);
|
|
296
|
-
}
|
|
297
|
-
return true;
|
|
298
|
-
}
|
|
299
|
-
catch (error) {
|
|
300
|
-
console.error('Failed to update Claude bypassPermissions support config:', error);
|
|
301
|
-
return false;
|
|
302
|
-
}
|
|
303
|
-
});
|
|
304
|
-
const VALID_CODEX_REASONING_EFFORTS = ['low', 'medium', 'high'];
|
|
253
|
+
const VALID_CODEX_REASONING_EFFORTS = ['low', 'medium', 'high', 'xhigh'];
|
|
305
254
|
const DEFAULT_CODEX_REASONING_EFFORT = 'high';
|
|
306
255
|
const isCodexReasoningEffort = (value) => {
|
|
307
256
|
return typeof value === 'string' && VALID_CODEX_REASONING_EFFORTS.includes(value);
|
|
308
257
|
};
|
|
309
|
-
const
|
|
310
|
-
|
|
311
|
-
return `model_provider = "aicodeswitch"
|
|
312
|
-
model = "gpt-5.1-codex"
|
|
313
|
-
model_reasoning_effort = "${modelReasoningEffort}"
|
|
314
|
-
disable_response_storage = true
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
[model_providers.aicodeswitch]
|
|
318
|
-
name = "aicodeswitch"
|
|
319
|
-
base_url = "http://${host}:${localPort}/codex"
|
|
320
|
-
wire_api = "responses"
|
|
321
|
-
requires_openai_auth = true
|
|
322
|
-
`;
|
|
323
|
-
};
|
|
324
|
-
const writeCodexConfig = (dbManager_1, ...args_1) => __awaiter(void 0, [dbManager_1, ...args_1], void 0, function* (dbManager, modelReasoningEffort = DEFAULT_CODEX_REASONING_EFFORT) {
|
|
258
|
+
const writeCodexConfig = (dbManager_1, ...args_1) => __awaiter(void 0, [dbManager_1, ...args_1], void 0, function* (dbManager, modelReasoningEffort = DEFAULT_CODEX_REASONING_EFFORT, codexDefaultModel, options = {}) {
|
|
259
|
+
var _a;
|
|
325
260
|
try {
|
|
326
261
|
const homeDir = os_1.default.homedir();
|
|
327
262
|
const config = dbManager.getConfig();
|
|
@@ -332,41 +267,92 @@ const writeCodexConfig = (dbManager_1, ...args_1) => __awaiter(void 0, [dbManage
|
|
|
332
267
|
const codexAuthBakPath = path_1.default.join(codexDir, 'auth.json.aicodeswitch_backup');
|
|
333
268
|
// 使用新的配置状态检测来判断是否可以写入
|
|
334
269
|
const configStatus = (0, config_metadata_1.checkCodexConfigStatus)();
|
|
270
|
+
const isRuntimeRefresh = options.allowOverwriteRefresh === true && configStatus.isOverwritten;
|
|
335
271
|
// 只有当当前配置已经是代理配置时,才拒绝写入
|
|
336
|
-
if (configStatus.isOverwritten) {
|
|
272
|
+
if (configStatus.isOverwritten && !isRuntimeRefresh) {
|
|
337
273
|
console.error('Codex config has already been overwritten. Please restore the original config first.');
|
|
338
274
|
return false;
|
|
339
275
|
}
|
|
340
276
|
// 如果 .aicodeswitch_backup 文件不存在,才进行备份(避免覆盖已有备份)
|
|
341
|
-
let originalConfigHash =
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
//
|
|
347
|
-
fs_1.default.
|
|
277
|
+
let originalConfigHash = isRuntimeRefresh
|
|
278
|
+
? (_a = configStatus.metadata) === null || _a === void 0 ? void 0 : _a.originalHash
|
|
279
|
+
: undefined;
|
|
280
|
+
if (!isRuntimeRefresh) {
|
|
281
|
+
if (!fs_1.default.existsSync(codexConfigBakPath)) {
|
|
282
|
+
// 计算原始配置文件的 hash(如果存在)
|
|
283
|
+
if (fs_1.default.existsSync(codexConfigPath)) {
|
|
284
|
+
originalConfigHash = (0, crypto_1.createHash)('sha256').update(fs_1.default.readFileSync(codexConfigPath, 'utf-8')).digest('hex');
|
|
285
|
+
// 备份当前配置文件
|
|
286
|
+
fs_1.default.renameSync(codexConfigPath, codexConfigBakPath);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
// .aicodeswitch_backup 已存在,直接使用现有的备份文件
|
|
291
|
+
console.log('Backup file already exists, skipping backup step');
|
|
348
292
|
}
|
|
349
|
-
}
|
|
350
|
-
else {
|
|
351
|
-
// .aicodeswitch_backup 已存在,直接使用现有的备份文件
|
|
352
|
-
console.log('Backup file already exists, skipping backup step');
|
|
353
293
|
}
|
|
354
294
|
if (!fs_1.default.existsSync(codexDir)) {
|
|
355
295
|
fs_1.default.mkdirSync(codexDir, { recursive: true });
|
|
356
296
|
}
|
|
357
|
-
|
|
297
|
+
// 读取当前配置(如果存在),保留工具运行时写入的内容
|
|
298
|
+
let currentConfig = {};
|
|
299
|
+
if (fs_1.default.existsSync(codexConfigPath)) {
|
|
300
|
+
try {
|
|
301
|
+
currentConfig = (0, config_merge_1.parseToml)(fs_1.default.readFileSync(codexConfigPath, 'utf-8'));
|
|
302
|
+
}
|
|
303
|
+
catch (error) {
|
|
304
|
+
console.warn('Failed to parse current config.toml, using empty object:', error);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
// 构建代理配置
|
|
308
|
+
const proxyConfig = {
|
|
309
|
+
model_provider: "aicodeswitch",
|
|
310
|
+
model: codexDefaultModel || "gpt-5.3-codex", // 使用配置的默认模型,否则使用默认值
|
|
311
|
+
model_reasoning_effort: modelReasoningEffort,
|
|
312
|
+
disable_response_storage: true,
|
|
313
|
+
preferred_auth_method: "apikey",
|
|
314
|
+
requires_openai_auth: true,
|
|
315
|
+
enableRouteSelection: true,
|
|
316
|
+
model_providers: {
|
|
317
|
+
aicodeswitch: {
|
|
318
|
+
name: "aicodeswitch",
|
|
319
|
+
base_url: `http://${host}:${process.env.PORT ? parseInt(process.env.PORT, 10) : 4567}/codex`,
|
|
320
|
+
wire_api: "responses"
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
};
|
|
324
|
+
// 使用智能合并
|
|
325
|
+
const mergedConfig = (0, config_merge_1.mergeTomlConfig)(proxyConfig, currentConfig, config_managed_fields_1.CODEX_CONFIG_MANAGED_FIELDS);
|
|
326
|
+
// 原子性写入合并后的配置
|
|
327
|
+
(0, config_merge_1.atomicWriteFile)(codexConfigPath, (0, config_merge_1.stringifyToml)(mergedConfig));
|
|
358
328
|
// Codex auth.json
|
|
359
329
|
const codexAuthPath = path_1.default.join(codexDir, 'auth.json');
|
|
330
|
+
// 读取当前配置(如果存在),保留工具运行时写入的内容
|
|
331
|
+
let currentAuth = {};
|
|
332
|
+
if (fs_1.default.existsSync(codexAuthPath)) {
|
|
333
|
+
try {
|
|
334
|
+
currentAuth = JSON.parse(fs_1.default.readFileSync(codexAuthPath, 'utf-8'));
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
console.warn('Failed to parse current auth.json, using empty object:', error);
|
|
338
|
+
}
|
|
339
|
+
}
|
|
360
340
|
// 同样处理 auth.json 的备份
|
|
361
|
-
if (!
|
|
362
|
-
if (fs_1.default.existsSync(
|
|
363
|
-
fs_1.default.
|
|
341
|
+
if (!isRuntimeRefresh) {
|
|
342
|
+
if (!fs_1.default.existsSync(codexAuthBakPath)) {
|
|
343
|
+
if (fs_1.default.existsSync(codexAuthPath)) {
|
|
344
|
+
fs_1.default.renameSync(codexAuthPath, codexAuthBakPath);
|
|
345
|
+
}
|
|
364
346
|
}
|
|
365
347
|
}
|
|
366
|
-
|
|
348
|
+
// 构建代理配置
|
|
349
|
+
const proxyAuth = {
|
|
367
350
|
OPENAI_API_KEY: config.apiKey || "api_key"
|
|
368
351
|
};
|
|
369
|
-
|
|
352
|
+
// 使用智能合并
|
|
353
|
+
const mergedAuth = (0, config_merge_1.mergeJsonConfig)(proxyAuth, currentAuth, config_managed_fields_1.CODEX_AUTH_MANAGED_FIELDS);
|
|
354
|
+
// 原子性写入合并后的配置
|
|
355
|
+
(0, config_merge_1.atomicWriteFile)(codexAuthPath, JSON.stringify(mergedAuth, null, 2));
|
|
370
356
|
// 保存元数据
|
|
371
357
|
const currentConfigHash = (0, crypto_1.createHash)('sha256').update(fs_1.default.readFileSync(codexConfigPath, 'utf-8')).digest('hex');
|
|
372
358
|
const metadata = {
|
|
@@ -394,60 +380,62 @@ const writeCodexConfig = (dbManager_1, ...args_1) => __awaiter(void 0, [dbManage
|
|
|
394
380
|
return false;
|
|
395
381
|
}
|
|
396
382
|
});
|
|
397
|
-
const updateCodexReasoningEffortConfig = (modelReasoningEffort) => __awaiter(void 0, void 0, void 0, function* () {
|
|
398
|
-
try {
|
|
399
|
-
const homeDir = os_1.default.homedir();
|
|
400
|
-
const codexConfigPath = path_1.default.join(homeDir, '.codex/config.toml');
|
|
401
|
-
if (!fs_1.default.existsSync(codexConfigPath)) {
|
|
402
|
-
console.error('Codex config.toml does not exist');
|
|
403
|
-
return false;
|
|
404
|
-
}
|
|
405
|
-
const configStatus = (0, config_metadata_1.checkCodexConfigStatus)();
|
|
406
|
-
if (!configStatus.isOverwritten) {
|
|
407
|
-
console.error('Codex config is not overwritten by proxy. Please activate a route first.');
|
|
408
|
-
return false;
|
|
409
|
-
}
|
|
410
|
-
fs_1.default.writeFileSync(codexConfigPath, buildCodexConfigToml(modelReasoningEffort));
|
|
411
|
-
const metadata = (0, config_metadata_1.loadMetadata)('codex');
|
|
412
|
-
if (metadata && metadata.files[0]) {
|
|
413
|
-
metadata.files[0].currentHash = (0, crypto_1.createHash)('sha256')
|
|
414
|
-
.update(fs_1.default.readFileSync(codexConfigPath, 'utf-8'))
|
|
415
|
-
.digest('hex');
|
|
416
|
-
metadata.timestamp = Date.now();
|
|
417
|
-
(0, config_metadata_1.saveMetadata)(metadata);
|
|
418
|
-
}
|
|
419
|
-
return true;
|
|
420
|
-
}
|
|
421
|
-
catch (error) {
|
|
422
|
-
console.error('Failed to update Codex reasoning effort config:', error);
|
|
423
|
-
return false;
|
|
424
|
-
}
|
|
425
|
-
});
|
|
426
383
|
const restoreClaudeConfig = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
427
384
|
try {
|
|
428
385
|
const homeDir = os_1.default.homedir();
|
|
386
|
+
let restoredAnyFile = false;
|
|
429
387
|
// Restore Claude Code settings.json
|
|
430
388
|
const claudeDir = path_1.default.join(homeDir, '.claude');
|
|
431
389
|
const claudeSettingsPath = path_1.default.join(claudeDir, 'settings.json');
|
|
432
390
|
const claudeSettingsBakPath = path_1.default.join(claudeDir, 'settings.json.aicodeswitch_backup');
|
|
433
391
|
if (fs_1.default.existsSync(claudeSettingsBakPath)) {
|
|
392
|
+
// 读取备份配置
|
|
393
|
+
const backupSettings = JSON.parse(fs_1.default.readFileSync(claudeSettingsBakPath, 'utf-8'));
|
|
394
|
+
// 读取当前配置(可能包含工具运行时写入的新内容)
|
|
395
|
+
let currentSettings = {};
|
|
434
396
|
if (fs_1.default.existsSync(claudeSettingsPath)) {
|
|
435
|
-
|
|
397
|
+
try {
|
|
398
|
+
currentSettings = JSON.parse(fs_1.default.readFileSync(claudeSettingsPath, 'utf-8'));
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
console.warn('Failed to parse current settings.json during restore, using empty object:', error);
|
|
402
|
+
}
|
|
436
403
|
}
|
|
437
|
-
|
|
404
|
+
// 生成合并后的配置(备份作为基础,合并当前的非管理字段)
|
|
405
|
+
const mergedSettings = (0, config_merge_1.mergeJsonConfig)(backupSettings, currentSettings, config_managed_fields_1.CLAUDE_SETTINGS_MANAGED_FIELDS);
|
|
406
|
+
// 原子性写入合并后的配置
|
|
407
|
+
(0, config_merge_1.atomicWriteFile)(claudeSettingsPath, JSON.stringify(mergedSettings, null, 2));
|
|
408
|
+
// 删除备份文件
|
|
409
|
+
fs_1.default.unlinkSync(claudeSettingsBakPath);
|
|
410
|
+
restoredAnyFile = true;
|
|
438
411
|
}
|
|
439
412
|
// Restore Claude Code .claude.json
|
|
440
413
|
const claudeJsonPath = path_1.default.join(homeDir, '.claude.json');
|
|
441
414
|
const claudeJsonBakPath = path_1.default.join(homeDir, '.claude.json.aicodeswitch_backup');
|
|
442
415
|
if (fs_1.default.existsSync(claudeJsonBakPath)) {
|
|
416
|
+
// 读取备份配置
|
|
417
|
+
const backupClaudeJson = JSON.parse(fs_1.default.readFileSync(claudeJsonBakPath, 'utf-8'));
|
|
418
|
+
// 读取当前配置(可能包含工具运行时写入的新内容)
|
|
419
|
+
let currentClaudeJson = {};
|
|
443
420
|
if (fs_1.default.existsSync(claudeJsonPath)) {
|
|
444
|
-
|
|
421
|
+
try {
|
|
422
|
+
currentClaudeJson = JSON.parse(fs_1.default.readFileSync(claudeJsonPath, 'utf-8'));
|
|
423
|
+
}
|
|
424
|
+
catch (error) {
|
|
425
|
+
console.warn('Failed to parse current .claude.json during restore, using empty object:', error);
|
|
426
|
+
}
|
|
445
427
|
}
|
|
446
|
-
|
|
428
|
+
// 生成合并后的配置
|
|
429
|
+
const mergedClaudeJson = (0, config_merge_1.mergeJsonConfig)(backupClaudeJson, currentClaudeJson, config_managed_fields_1.CLAUDE_JSON_MANAGED_FIELDS);
|
|
430
|
+
// 原子性写入合并后的配置
|
|
431
|
+
(0, config_merge_1.atomicWriteFile)(claudeJsonPath, JSON.stringify(mergedClaudeJson, null, 2));
|
|
432
|
+
// 删除备份文件
|
|
433
|
+
fs_1.default.unlinkSync(claudeJsonBakPath);
|
|
434
|
+
restoredAnyFile = true;
|
|
447
435
|
}
|
|
448
436
|
// 删除元数据
|
|
449
437
|
(0, config_metadata_1.deleteMetadata)('claude');
|
|
450
|
-
return
|
|
438
|
+
return restoredAnyFile;
|
|
451
439
|
}
|
|
452
440
|
catch (error) {
|
|
453
441
|
console.error('Failed to restore Claude config files:', error);
|
|
@@ -457,28 +445,59 @@ const restoreClaudeConfig = () => __awaiter(void 0, void 0, void 0, function* ()
|
|
|
457
445
|
const restoreCodexConfig = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
458
446
|
try {
|
|
459
447
|
const homeDir = os_1.default.homedir();
|
|
448
|
+
let restoredAnyFile = false;
|
|
460
449
|
// Restore Codex config.toml
|
|
461
450
|
const codexDir = path_1.default.join(homeDir, '.codex');
|
|
462
451
|
const codexConfigPath = path_1.default.join(codexDir, 'config.toml');
|
|
463
452
|
const codexConfigBakPath = path_1.default.join(codexDir, 'config.toml.aicodeswitch_backup');
|
|
464
453
|
if (fs_1.default.existsSync(codexConfigBakPath)) {
|
|
454
|
+
// 读取备份配置
|
|
455
|
+
const backupConfig = (0, config_merge_1.parseToml)(fs_1.default.readFileSync(codexConfigBakPath, 'utf-8'));
|
|
456
|
+
// 读取当前配置(可能包含工具运行时写入的新内容)
|
|
457
|
+
let currentConfig = {};
|
|
465
458
|
if (fs_1.default.existsSync(codexConfigPath)) {
|
|
466
|
-
|
|
459
|
+
try {
|
|
460
|
+
currentConfig = (0, config_merge_1.parseToml)(fs_1.default.readFileSync(codexConfigPath, 'utf-8'));
|
|
461
|
+
}
|
|
462
|
+
catch (error) {
|
|
463
|
+
console.warn('Failed to parse current config.toml during restore, using empty object:', error);
|
|
464
|
+
}
|
|
467
465
|
}
|
|
468
|
-
|
|
466
|
+
// 生成合并后的配置(备份作为基础,合并当前的非管理字段)
|
|
467
|
+
const mergedConfig = (0, config_merge_1.mergeTomlConfig)(backupConfig, currentConfig, config_managed_fields_1.CODEX_CONFIG_MANAGED_FIELDS);
|
|
468
|
+
// 原子性写入合并后的配置
|
|
469
|
+
(0, config_merge_1.atomicWriteFile)(codexConfigPath, (0, config_merge_1.stringifyToml)(mergedConfig));
|
|
470
|
+
// 删除备份文件
|
|
471
|
+
fs_1.default.unlinkSync(codexConfigBakPath);
|
|
472
|
+
restoredAnyFile = true;
|
|
469
473
|
}
|
|
470
474
|
// Restore Codex auth.json
|
|
471
475
|
const codexAuthPath = path_1.default.join(codexDir, 'auth.json');
|
|
472
476
|
const codexAuthBakPath = path_1.default.join(codexDir, 'auth.json.aicodeswitch_backup');
|
|
473
477
|
if (fs_1.default.existsSync(codexAuthBakPath)) {
|
|
478
|
+
// 读取备份配置
|
|
479
|
+
const backupAuth = JSON.parse(fs_1.default.readFileSync(codexAuthBakPath, 'utf-8'));
|
|
480
|
+
// 读取当前配置(可能包含工具运行时写入的新内容)
|
|
481
|
+
let currentAuth = {};
|
|
474
482
|
if (fs_1.default.existsSync(codexAuthPath)) {
|
|
475
|
-
|
|
483
|
+
try {
|
|
484
|
+
currentAuth = JSON.parse(fs_1.default.readFileSync(codexAuthPath, 'utf-8'));
|
|
485
|
+
}
|
|
486
|
+
catch (error) {
|
|
487
|
+
console.warn('Failed to parse current auth.json during restore, using empty object:', error);
|
|
488
|
+
}
|
|
476
489
|
}
|
|
477
|
-
|
|
490
|
+
// 生成合并后的配置
|
|
491
|
+
const mergedAuth = (0, config_merge_1.mergeJsonConfig)(backupAuth, currentAuth, config_managed_fields_1.CODEX_AUTH_MANAGED_FIELDS);
|
|
492
|
+
// 原子性写入合并后的配置
|
|
493
|
+
(0, config_merge_1.atomicWriteFile)(codexAuthPath, JSON.stringify(mergedAuth, null, 2));
|
|
494
|
+
// 删除备份文件
|
|
495
|
+
fs_1.default.unlinkSync(codexAuthBakPath);
|
|
496
|
+
restoredAnyFile = true;
|
|
478
497
|
}
|
|
479
498
|
// 删除元数据
|
|
480
499
|
(0, config_metadata_1.deleteMetadata)('codex');
|
|
481
|
-
return
|
|
500
|
+
return restoredAnyFile;
|
|
482
501
|
}
|
|
483
502
|
catch (error) {
|
|
484
503
|
console.error('Failed to restore Codex config files:', error);
|
|
@@ -513,6 +532,33 @@ const checkCodexBackupExists = () => {
|
|
|
513
532
|
return false;
|
|
514
533
|
}
|
|
515
534
|
};
|
|
535
|
+
const syncConfigsOnServerStartup = (dbManager) => __awaiter(void 0, void 0, void 0, function* () {
|
|
536
|
+
const config = dbManager.getConfig();
|
|
537
|
+
// 服务启动即执行写入,参数来源改为全局配置
|
|
538
|
+
const claudeEffortLevel = isClaudeEffortLevel(config.claudeEffortLevel)
|
|
539
|
+
? config.claudeEffortLevel
|
|
540
|
+
: DEFAULT_CLAUDE_EFFORT_LEVEL;
|
|
541
|
+
const claudeWritten = yield writeClaudeConfig(dbManager, config.enableAgentTeams, config.enableBypassPermissionsSupport, claudeEffortLevel, config.claudeDefaultModel);
|
|
542
|
+
console.log(`[Startup Config Sync] Claude Code config ${claudeWritten ? 'written' : 'skipped'}`);
|
|
543
|
+
const modelReasoningEffort = isCodexReasoningEffort(config.codexModelReasoningEffort)
|
|
544
|
+
? config.codexModelReasoningEffort
|
|
545
|
+
: DEFAULT_CODEX_REASONING_EFFORT;
|
|
546
|
+
const codexWritten = yield writeCodexConfig(dbManager, modelReasoningEffort, config.codexDefaultModel);
|
|
547
|
+
console.log(`[Startup Config Sync] Codex config ${codexWritten ? 'written' : 'skipped'}`);
|
|
548
|
+
});
|
|
549
|
+
const syncConfigsOnGlobalConfigUpdate = (dbManager) => __awaiter(void 0, void 0, void 0, function* () {
|
|
550
|
+
const config = dbManager.getConfig();
|
|
551
|
+
const claudeEffortLevel = isClaudeEffortLevel(config.claudeEffortLevel)
|
|
552
|
+
? config.claudeEffortLevel
|
|
553
|
+
: DEFAULT_CLAUDE_EFFORT_LEVEL;
|
|
554
|
+
const claudeUpdated = yield writeClaudeConfig(dbManager, config.enableAgentTeams, config.enableBypassPermissionsSupport, claudeEffortLevel, config.claudeDefaultModel, { allowOverwriteRefresh: true });
|
|
555
|
+
console.log(`[Config Update Sync] Claude Code config ${claudeUpdated ? 'written' : 'skipped'}`);
|
|
556
|
+
const modelReasoningEffort = isCodexReasoningEffort(config.codexModelReasoningEffort)
|
|
557
|
+
? config.codexModelReasoningEffort
|
|
558
|
+
: DEFAULT_CODEX_REASONING_EFFORT;
|
|
559
|
+
const codexUpdated = yield writeCodexConfig(dbManager, modelReasoningEffort, config.codexDefaultModel, { allowOverwriteRefresh: true });
|
|
560
|
+
console.log(`[Config Update Sync] Codex config ${codexUpdated ? 'written' : 'skipped'}`);
|
|
561
|
+
});
|
|
516
562
|
const getCentralSkillsDir = () => {
|
|
517
563
|
return path_1.default.join(os_1.default.homedir(), '.aicodeswitch', 'skills');
|
|
518
564
|
};
|
|
@@ -902,8 +948,12 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
902
948
|
}
|
|
903
949
|
});
|
|
904
950
|
app.get('/api/vendors', (_req, res) => res.json(dbManager.getVendors()));
|
|
905
|
-
app.post('/api/vendors', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
906
|
-
|
|
951
|
+
app.post('/api/vendors', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
952
|
+
res.json(yield dbManager.createVendor(req.body));
|
|
953
|
+
})));
|
|
954
|
+
app.put('/api/vendors/:id', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
955
|
+
res.json(yield dbManager.updateVendor(req.params.id, req.body));
|
|
956
|
+
})));
|
|
907
957
|
app.delete('/api/vendors/:id', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
908
958
|
try {
|
|
909
959
|
const result = yield dbManager.deleteVendor(req.params.id);
|
|
@@ -925,7 +975,14 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
925
975
|
console.log('[创建服务] 创建结果:', JSON.stringify(result, null, 2));
|
|
926
976
|
res.json(result);
|
|
927
977
|
})));
|
|
928
|
-
app.put('/api/services/:id', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
978
|
+
app.put('/api/services/:id', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
979
|
+
const existingService = dbManager.getAPIService(req.params.id);
|
|
980
|
+
if (!existingService) {
|
|
981
|
+
res.status(404).json({ error: '服务不存在' });
|
|
982
|
+
return;
|
|
983
|
+
}
|
|
984
|
+
res.json(yield dbManager.updateAPIService(req.params.id, req.body));
|
|
985
|
+
})));
|
|
929
986
|
app.delete('/api/services/:id', (req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
930
987
|
console.log('[删除服务] 请求 ID:', req.params.id);
|
|
931
988
|
try {
|
|
@@ -970,26 +1027,8 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
970
1027
|
})));
|
|
971
1028
|
// 批量停用所有激活的路由(用于应用关闭时清理)
|
|
972
1029
|
app.post('/api/routes/deactivate-all', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
973
|
-
console.log('[Deactivate All Routes] Starting
|
|
974
|
-
//
|
|
975
|
-
try {
|
|
976
|
-
console.log('[Deactivate All Routes] Restoring Claude Code config...');
|
|
977
|
-
const claudeRestored = yield restoreClaudeConfig();
|
|
978
|
-
console.log(`[Deactivate All Routes] Claude Code config ${claudeRestored ? 'restored' : 'was not modified'}`);
|
|
979
|
-
}
|
|
980
|
-
catch (error) {
|
|
981
|
-
console.error('[Deactivate All Routes] Failed to restore Claude config:', error);
|
|
982
|
-
}
|
|
983
|
-
// 步骤2:恢复 Codex 配置文件
|
|
984
|
-
try {
|
|
985
|
-
console.log('[Deactivate All Routes] Restoring Codex config...');
|
|
986
|
-
const codexRestored = yield restoreCodexConfig();
|
|
987
|
-
console.log(`[Deactivate All Routes] Codex config ${codexRestored ? 'restored' : 'was not modified'}`);
|
|
988
|
-
}
|
|
989
|
-
catch (error) {
|
|
990
|
-
console.error('[Deactivate All Routes] Failed to restore Codex config:', error);
|
|
991
|
-
}
|
|
992
|
-
// 步骤3:停用所有激活的路由
|
|
1030
|
+
console.log('[Deactivate All Routes] Starting route deactivation...');
|
|
1031
|
+
// 仅停用路由,不再在路由接口内处理配置文件恢复
|
|
993
1032
|
console.log('[Deactivate All Routes] Deactivating all active routes...');
|
|
994
1033
|
const deactivatedCount = yield dbManager.deactivateAllRoutes();
|
|
995
1034
|
if (deactivatedCount > 0) {
|
|
@@ -1000,7 +1039,7 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
1000
1039
|
else {
|
|
1001
1040
|
console.log('[Deactivate All Routes] No active routes to deactivate');
|
|
1002
1041
|
}
|
|
1003
|
-
console.log('[Deactivate All Routes]
|
|
1042
|
+
console.log('[Deactivate All Routes] Route deactivation completed');
|
|
1004
1043
|
res.json({
|
|
1005
1044
|
success: true,
|
|
1006
1045
|
deactivatedCount
|
|
@@ -1043,6 +1082,28 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
1043
1082
|
res.status(500).json({ error: 'Failed to clear blacklist' });
|
|
1044
1083
|
}
|
|
1045
1084
|
})));
|
|
1085
|
+
// 清除规则的错误状态(广播 idle 状态给所有客户端)
|
|
1086
|
+
app.post('/api/rules/:id/clear-status', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1087
|
+
const { id } = req.params;
|
|
1088
|
+
const rule = dbManager.getRule(id);
|
|
1089
|
+
if (!rule) {
|
|
1090
|
+
res.status(404).json({ error: 'Rule not found' });
|
|
1091
|
+
return;
|
|
1092
|
+
}
|
|
1093
|
+
// 找到该规则所属的路由
|
|
1094
|
+
const routes = dbManager.getRoutes();
|
|
1095
|
+
const route = routes.find(r => {
|
|
1096
|
+
const rules = dbManager.getRules(r.id);
|
|
1097
|
+
return rules.some(r => r.id === id);
|
|
1098
|
+
});
|
|
1099
|
+
if (!route) {
|
|
1100
|
+
res.status(404).json({ error: 'Route not found' });
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
// 广播 idle 状态给所有客户端
|
|
1104
|
+
rules_status_service_1.rulesStatusBroadcaster.markRuleIdle(route.id, id);
|
|
1105
|
+
res.json({ success: true });
|
|
1106
|
+
})));
|
|
1046
1107
|
// 获取规则的黑名单状态
|
|
1047
1108
|
app.get('/api/rules/:routeId/blacklist-status', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1048
1109
|
const { routeId } = req.params;
|
|
@@ -1132,8 +1193,10 @@ const registerRoutes = (dbManager, proxyServer) => {
|
|
|
1132
1193
|
const config = req.body;
|
|
1133
1194
|
const result = yield dbManager.updateConfig(config);
|
|
1134
1195
|
if (result) {
|
|
1135
|
-
|
|
1136
|
-
|
|
1196
|
+
const latestConfig = dbManager.getConfig();
|
|
1197
|
+
yield proxyServer.updateConfig(latestConfig);
|
|
1198
|
+
updateProxyConfig(latestConfig);
|
|
1199
|
+
yield syncConfigsOnGlobalConfigUpdate(dbManager);
|
|
1137
1200
|
}
|
|
1138
1201
|
res.json(result);
|
|
1139
1202
|
})));
|
|
@@ -1474,29 +1537,53 @@ ${instruction}
|
|
|
1474
1537
|
res.json({ success: true });
|
|
1475
1538
|
})));
|
|
1476
1539
|
app.post('/api/write-config/claude', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1477
|
-
const
|
|
1478
|
-
const
|
|
1479
|
-
const
|
|
1540
|
+
const appConfig = dbManager.getConfig();
|
|
1541
|
+
const requestedEnableAgentTeams = req.body.enableAgentTeams;
|
|
1542
|
+
const requestedBypass = req.body.enableBypassPermissionsSupport;
|
|
1543
|
+
const enableAgentTeams = typeof requestedEnableAgentTeams === 'boolean'
|
|
1544
|
+
? requestedEnableAgentTeams
|
|
1545
|
+
: appConfig.enableAgentTeams;
|
|
1546
|
+
const enableBypassPermissionsSupport = typeof requestedBypass === 'boolean'
|
|
1547
|
+
? requestedBypass
|
|
1548
|
+
: appConfig.enableBypassPermissionsSupport;
|
|
1549
|
+
const result = yield writeClaudeConfig(dbManager, enableAgentTeams, enableBypassPermissionsSupport, undefined, appConfig.claudeDefaultModel);
|
|
1480
1550
|
res.json(result);
|
|
1481
1551
|
})));
|
|
1482
1552
|
app.post('/api/write-config/codex', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1553
|
+
const appConfig = dbManager.getConfig();
|
|
1483
1554
|
const requestedEffort = req.body.modelReasoningEffort;
|
|
1484
1555
|
const modelReasoningEffort = isCodexReasoningEffort(requestedEffort)
|
|
1485
1556
|
? requestedEffort
|
|
1486
|
-
:
|
|
1487
|
-
|
|
1557
|
+
: isCodexReasoningEffort(appConfig.codexModelReasoningEffort)
|
|
1558
|
+
? appConfig.codexModelReasoningEffort
|
|
1559
|
+
: DEFAULT_CODEX_REASONING_EFFORT;
|
|
1560
|
+
const result = yield writeCodexConfig(dbManager, modelReasoningEffort, appConfig.codexDefaultModel);
|
|
1488
1561
|
res.json(result);
|
|
1489
1562
|
})));
|
|
1490
|
-
//
|
|
1563
|
+
// 兼容接口:更新全局 Agent Teams 配置
|
|
1491
1564
|
app.post('/api/update-claude-agent-teams', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1492
1565
|
const { enableAgentTeams } = req.body;
|
|
1493
|
-
const
|
|
1566
|
+
const current = dbManager.getConfig();
|
|
1567
|
+
const result = yield dbManager.updateConfig(Object.assign(Object.assign({}, current), { enableAgentTeams: !!enableAgentTeams }));
|
|
1568
|
+
if (result) {
|
|
1569
|
+
const latestConfig = dbManager.getConfig();
|
|
1570
|
+
yield proxyServer.updateConfig(latestConfig);
|
|
1571
|
+
updateProxyConfig(latestConfig);
|
|
1572
|
+
yield syncConfigsOnGlobalConfigUpdate(dbManager);
|
|
1573
|
+
}
|
|
1494
1574
|
res.json(result);
|
|
1495
1575
|
})));
|
|
1496
|
-
//
|
|
1576
|
+
// 兼容接口:更新全局 bypassPermissions 支持配置
|
|
1497
1577
|
app.post('/api/update-claude-bypass-permissions-support', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1498
1578
|
const { enableBypassPermissionsSupport } = req.body;
|
|
1499
|
-
const
|
|
1579
|
+
const current = dbManager.getConfig();
|
|
1580
|
+
const result = yield dbManager.updateConfig(Object.assign(Object.assign({}, current), { enableBypassPermissionsSupport: !!enableBypassPermissionsSupport }));
|
|
1581
|
+
if (result) {
|
|
1582
|
+
const latestConfig = dbManager.getConfig();
|
|
1583
|
+
yield proxyServer.updateConfig(latestConfig);
|
|
1584
|
+
updateProxyConfig(latestConfig);
|
|
1585
|
+
yield syncConfigsOnGlobalConfigUpdate(dbManager);
|
|
1586
|
+
}
|
|
1500
1587
|
res.json(result);
|
|
1501
1588
|
})));
|
|
1502
1589
|
app.post('/api/update-codex-reasoning-effort', asyncHandler((req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -1505,7 +1592,14 @@ ${instruction}
|
|
|
1505
1592
|
res.status(400).json({ error: 'Invalid modelReasoningEffort' });
|
|
1506
1593
|
return;
|
|
1507
1594
|
}
|
|
1508
|
-
const
|
|
1595
|
+
const current = dbManager.getConfig();
|
|
1596
|
+
const result = yield dbManager.updateConfig(Object.assign(Object.assign({}, current), { codexModelReasoningEffort: requestedEffort }));
|
|
1597
|
+
if (result) {
|
|
1598
|
+
const latestConfig = dbManager.getConfig();
|
|
1599
|
+
yield proxyServer.updateConfig(latestConfig);
|
|
1600
|
+
updateProxyConfig(latestConfig);
|
|
1601
|
+
yield syncConfigsOnGlobalConfigUpdate(dbManager);
|
|
1602
|
+
}
|
|
1509
1603
|
res.json(result);
|
|
1510
1604
|
})));
|
|
1511
1605
|
app.post('/api/restore-config/claude', asyncHandler((_req, res) => __awaiter(void 0, void 0, void 0, function* () {
|
|
@@ -1815,6 +1909,15 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
1815
1909
|
console.log('[Server] Initializing database...');
|
|
1816
1910
|
const dbManager = yield database_factory_1.DatabaseFactory.createAuto(dataDir, legacyDataDir);
|
|
1817
1911
|
console.log('[Server] Database initialized successfully');
|
|
1912
|
+
// 服务启动时自动同步配置文件(适用于 CLI 和 dev:server)
|
|
1913
|
+
console.log('[Server] Syncing tool configs with global settings...');
|
|
1914
|
+
try {
|
|
1915
|
+
yield syncConfigsOnServerStartup(dbManager);
|
|
1916
|
+
console.log('[Server] Tool config sync completed');
|
|
1917
|
+
}
|
|
1918
|
+
catch (error) {
|
|
1919
|
+
console.error('[Server] Tool config sync failed:', error);
|
|
1920
|
+
}
|
|
1818
1921
|
const proxyServer = new proxy_server_1.ProxyServer(dbManager, app);
|
|
1819
1922
|
// Initialize proxy server and register proxy routes last
|
|
1820
1923
|
proxyServer.initialize();
|
|
@@ -1845,6 +1948,12 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
1845
1948
|
const toolInstallWss = (0, websocket_service_1.createToolInstallationWSServer)();
|
|
1846
1949
|
// 创建 WebSocket 服务器用于规则状态
|
|
1847
1950
|
const rulesStatusWss = (0, rules_status_service_1.createRulesStatusWSServer)();
|
|
1951
|
+
// 设置黑名单检查函数,用于在规则状态同步时检查黑名单是否已过期
|
|
1952
|
+
rules_status_service_1.rulesStatusBroadcaster.setBlacklistChecker((serviceId, routeId, contentType) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1953
|
+
// 检查服务��否在黑名单中
|
|
1954
|
+
const isBlacklisted = yield dbManager.isServiceBlacklisted(serviceId, routeId, contentType);
|
|
1955
|
+
return isBlacklisted;
|
|
1956
|
+
}));
|
|
1848
1957
|
// 将 WebSocket 服务器附加到 HTTP 服务器
|
|
1849
1958
|
server.on('upgrade', (request, socket, head) => {
|
|
1850
1959
|
if (request.url === '/api/tools/install') {
|
|
@@ -1863,16 +1972,46 @@ const start = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
1863
1972
|
});
|
|
1864
1973
|
console.log(`WebSocket server for tool installation attached to ws://${host}:${port}/api/tools/install`);
|
|
1865
1974
|
console.log(`WebSocket server for rules status attached to ws://${host}:${port}/api/rules/status`);
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1975
|
+
let isShuttingDown = false;
|
|
1976
|
+
let shutdownPromise = null;
|
|
1977
|
+
const shutdown = (signal) => __awaiter(void 0, void 0, void 0, function* () {
|
|
1978
|
+
if (isShuttingDown) {
|
|
1979
|
+
return shutdownPromise !== null && shutdownPromise !== void 0 ? shutdownPromise : Promise.resolve();
|
|
1980
|
+
}
|
|
1981
|
+
isShuttingDown = true;
|
|
1982
|
+
shutdownPromise = (() => __awaiter(void 0, void 0, void 0, function* () {
|
|
1983
|
+
console.log(`[Server] Received ${signal}, shutting down...`);
|
|
1984
|
+
// 服务终止前恢复配置文件(适用于 aicos stop 与 Ctrl+C)
|
|
1985
|
+
try {
|
|
1986
|
+
const claudeRestored = yield restoreClaudeConfig();
|
|
1987
|
+
console.log(`[Shutdown ...] Claude Code config ${claudeRestored ? 'restored' : 'was not modified'}`);
|
|
1988
|
+
}
|
|
1989
|
+
catch (error) {
|
|
1990
|
+
console.error('[Shutdown ...] Failed to restore Claude config:', error);
|
|
1991
|
+
}
|
|
1992
|
+
try {
|
|
1993
|
+
const codexRestored = yield restoreCodexConfig();
|
|
1994
|
+
console.log(`[Shutdown ...] Codex config ${codexRestored ? 'restored' : 'was not modified'}`);
|
|
1995
|
+
}
|
|
1996
|
+
catch (error) {
|
|
1997
|
+
console.error('[Shutdown ...] Failed to restore Codex config:', error);
|
|
1998
|
+
}
|
|
1999
|
+
dbManager.close();
|
|
2000
|
+
yield Promise.race([
|
|
2001
|
+
new Promise((resolve) => {
|
|
2002
|
+
server.close(() => resolve());
|
|
2003
|
+
}),
|
|
2004
|
+
new Promise((resolve) => {
|
|
2005
|
+
setTimeout(resolve, 5000);
|
|
2006
|
+
})
|
|
2007
|
+
]);
|
|
1870
2008
|
console.log('Server stopped.');
|
|
1871
2009
|
process.exit(0);
|
|
1872
|
-
});
|
|
2010
|
+
}))();
|
|
2011
|
+
return shutdownPromise;
|
|
1873
2012
|
});
|
|
1874
|
-
process.on('SIGINT', shutdown);
|
|
1875
|
-
process.on('SIGTERM', shutdown);
|
|
2013
|
+
process.on('SIGINT', () => { void shutdown('SIGINT'); });
|
|
2014
|
+
process.on('SIGTERM', () => { void shutdown('SIGTERM'); });
|
|
1876
2015
|
});
|
|
1877
2016
|
// 全局未捕获异常处理 - 防止服务崩溃
|
|
1878
2017
|
process.on('uncaughtException', (error) => {
|