coding-tool-x 3.4.0 → 3.4.1
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/dist/web/assets/{Analytics-DEjfL5Jx.js → Analytics-CbGxotgz.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-DkRL_-tf.js → ConfigTemplates-oP6nrFEb.js} +1 -1
- package/dist/web/assets/{Home-CF-L640I.js → Home-DMntmEvh.js} +1 -1
- package/dist/web/assets/{PluginManager-BzNYTdNB.js → PluginManager-BUC_c7nH.js} +1 -1
- package/dist/web/assets/{ProjectList-C0-JgHMM.js → ProjectList-CW8J49n7.js} +1 -1
- package/dist/web/assets/{SessionList-CkZUdX5N.js → SessionList-7lYnF92v.js} +1 -1
- package/dist/web/assets/{SkillManager-Cak0-4d4.js → SkillManager-Cs08216i.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-CGDJzwEr.js → WorkspaceManager-CY-oGtyB.js} +1 -1
- package/dist/web/assets/{index-Dz7v9OM0.css → index-5qy5NMIP.css} +1 -1
- package/dist/web/assets/index-ClCqKpvX.js +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +6 -2
- package/src/server/codex-proxy-server.js +4 -92
- package/src/server/gemini-proxy-server.js +5 -28
- package/src/server/opencode-proxy-server.js +3 -93
- package/src/server/proxy-server.js +2 -57
- package/src/server/services/base/base-channel-service.js +247 -0
- package/src/server/services/base/proxy-utils.js +152 -0
- package/src/server/services/channel-health.js +30 -19
- package/src/server/services/channels.js +125 -293
- package/src/server/services/codex-channels.js +148 -513
- package/src/server/services/codex-env-manager.js +49 -19
- package/src/server/services/gemini-channels.js +2 -7
- package/src/server/services/oauth-credentials-service.js +12 -2
- package/src/server/services/opencode-channels.js +7 -9
- package/src/server/services/repo-scanner-base.js +1 -0
- package/dist/web/assets/index-D_WItvHE.js +0 -2
|
@@ -77,6 +77,22 @@ function upsertManagedBlock(content, snippet) {
|
|
|
77
77
|
return `${stripped.trimEnd()}\n\n${snippet}\n`;
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
+
function buildExportCommand(envFilePath, homeDir) {
|
|
81
|
+
return `. "${buildHomeRelativeShellPath(envFilePath, homeDir)}"`;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function buildPosixEnvFileContent(nextValues) {
|
|
85
|
+
const keys = Object.keys(nextValues).sort();
|
|
86
|
+
if (!keys.length) {
|
|
87
|
+
return '';
|
|
88
|
+
}
|
|
89
|
+
return [
|
|
90
|
+
'# Managed by Coding-Tool',
|
|
91
|
+
...keys.map((key) => `export ${key}=${shellQuote(nextValues[key])}`),
|
|
92
|
+
''
|
|
93
|
+
].join('\n');
|
|
94
|
+
}
|
|
95
|
+
|
|
80
96
|
function readJsonFile(filePath, fallbackValue) {
|
|
81
97
|
try {
|
|
82
98
|
if (!fs.existsSync(filePath)) {
|
|
@@ -181,29 +197,43 @@ function syncPosixEnvironment(nextValues, previousState, options) {
|
|
|
181
197
|
const {
|
|
182
198
|
runtime,
|
|
183
199
|
homeDir,
|
|
200
|
+
envFilePath,
|
|
184
201
|
stateFilePath,
|
|
185
202
|
shellEnv,
|
|
186
203
|
execSync
|
|
187
204
|
} = options;
|
|
188
205
|
const nextKeys = Object.keys(nextValues).sort();
|
|
189
206
|
let changed = false;
|
|
207
|
+
const { preferred, candidates } = getPosixProfileCandidates(homeDir, shellEnv);
|
|
208
|
+
const sourceSnippet = buildSourceSnippet(envFilePath, homeDir);
|
|
209
|
+
const sourceCommand = buildExportCommand(envFilePath, homeDir);
|
|
190
210
|
|
|
191
211
|
// 清理旧版本遗留的 shell profile 注入(迁移兼容)
|
|
192
212
|
const previousProfiles = Array.isArray(previousState.profiles) ? previousState.profiles : [];
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
213
|
+
const cleanupTargets = new Set([
|
|
214
|
+
...previousProfiles,
|
|
215
|
+
...candidates.filter((filePath) => fs.existsSync(filePath))
|
|
216
|
+
]);
|
|
217
|
+
if (!nextKeys.length) {
|
|
218
|
+
cleanupTargets.add(preferred);
|
|
219
|
+
}
|
|
220
|
+
for (const profilePath of cleanupTargets) {
|
|
221
|
+
if (!fs.existsSync(profilePath)) continue;
|
|
222
|
+
const currentContent = fs.readFileSync(profilePath, 'utf8');
|
|
223
|
+
if (!currentContent.includes(PROFILE_MARKER_START)) continue;
|
|
224
|
+
const nextContent = stripManagedBlock(currentContent);
|
|
225
|
+
const finalContent = nextContent ? `${nextContent}\n` : '';
|
|
226
|
+
changed = writeTextFileIfChanged(profilePath, finalContent) || changed;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (nextKeys.length > 0) {
|
|
230
|
+
changed = writeTextFileIfChanged(envFilePath, buildPosixEnvFileContent(nextValues)) || changed;
|
|
231
|
+
const currentProfileContent = fs.existsSync(preferred) ? fs.readFileSync(preferred, 'utf8') : '';
|
|
232
|
+
const nextProfileContent = upsertManagedBlock(currentProfileContent, sourceSnippet);
|
|
233
|
+
changed = writeTextFileIfChanged(preferred, nextProfileContent) || changed;
|
|
234
|
+
} else if (fs.existsSync(envFilePath)) {
|
|
235
|
+
fs.unlinkSync(envFilePath);
|
|
236
|
+
changed = true;
|
|
207
237
|
}
|
|
208
238
|
|
|
209
239
|
// macOS:用 launchctl 写入全局环境变量,新开终端/进程即生效
|
|
@@ -225,7 +255,7 @@ function syncPosixEnvironment(nextValues, previousState, options) {
|
|
|
225
255
|
writeJsonFile(stateFilePath, {
|
|
226
256
|
version: 1,
|
|
227
257
|
values: nextValues,
|
|
228
|
-
profiles: []
|
|
258
|
+
profiles: [preferred]
|
|
229
259
|
});
|
|
230
260
|
} else if (fs.existsSync(stateFilePath)) {
|
|
231
261
|
fs.unlinkSync(stateFilePath);
|
|
@@ -235,10 +265,10 @@ function syncPosixEnvironment(nextValues, previousState, options) {
|
|
|
235
265
|
changed,
|
|
236
266
|
reloadRequired: changed,
|
|
237
267
|
isFirstTime: Object.keys(previousState.values || {}).length === 0 && nextKeys.length > 0,
|
|
238
|
-
sourceCommand: null,
|
|
239
|
-
shellConfigPath: null,
|
|
240
|
-
shellConfigPaths: [],
|
|
241
|
-
envFilePath: null,
|
|
268
|
+
sourceCommand: nextKeys.length > 0 ? sourceCommand : null,
|
|
269
|
+
shellConfigPath: nextKeys.length > 0 ? preferred : null,
|
|
270
|
+
shellConfigPaths: nextKeys.length > 0 ? [preferred] : [],
|
|
271
|
+
envFilePath: nextKeys.length > 0 ? envFilePath : null,
|
|
242
272
|
managedKeys: nextKeys
|
|
243
273
|
};
|
|
244
274
|
}
|
|
@@ -3,6 +3,7 @@ const path = require('path');
|
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const { PATHS, NATIVE_PATHS } = require('../../config/paths');
|
|
5
5
|
const { clearNativeOAuth } = require('./native-oauth-adapters');
|
|
6
|
+
const { normalizeGatewaySourceType } = require('./base/proxy-utils');
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
* Gemini 渠道管理服务(多渠道架构)
|
|
@@ -17,13 +18,7 @@ const { clearNativeOAuth } = require('./native-oauth-adapters');
|
|
|
17
18
|
* - 使用 weight 和 maxConcurrency 控制负载均衡
|
|
18
19
|
*/
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
const normalized = String(value || '').trim().toLowerCase();
|
|
22
|
-
if (normalized === 'claude') return 'claude';
|
|
23
|
-
if (normalized === 'codex') return 'codex';
|
|
24
|
-
if (normalized === 'gemini') return 'gemini';
|
|
25
|
-
return fallback;
|
|
26
|
-
}
|
|
21
|
+
// normalizeGatewaySourceType imported from base/proxy-utils
|
|
27
22
|
|
|
28
23
|
// 获取 Gemini 配置目录
|
|
29
24
|
function getGeminiDir() {
|
|
@@ -380,12 +380,21 @@ function stableFingerprintValue(tool, metadata) {
|
|
|
380
380
|
return stableId;
|
|
381
381
|
}
|
|
382
382
|
|
|
383
|
+
function resolveFingerprintValue(tool, metadata, options = {}) {
|
|
384
|
+
if (options.fingerprintMode === 'primary-token') {
|
|
385
|
+
return metadata.primaryToken
|
|
386
|
+
|| metadata.accessToken
|
|
387
|
+
|| stableFingerprintValue(tool, metadata);
|
|
388
|
+
}
|
|
389
|
+
return stableFingerprintValue(tool, metadata);
|
|
390
|
+
}
|
|
391
|
+
|
|
383
392
|
function upsertCredential(tool, metadata, options = {}) {
|
|
384
393
|
const store = readStore();
|
|
385
394
|
const toolStore = getToolStore(store, tool);
|
|
386
395
|
const now = Date.now();
|
|
387
396
|
const primaryToken = metadata.primaryToken || metadata.accessToken || '';
|
|
388
|
-
const fingerprint = fingerprintFor(tool,
|
|
397
|
+
const fingerprint = fingerprintFor(tool, resolveFingerprintValue(tool, metadata, options));
|
|
389
398
|
const existingIndex = toolStore.credentials.findIndex((item) => item.fingerprint === fingerprint);
|
|
390
399
|
const existing = existingIndex >= 0 ? toolStore.credentials[existingIndex] : null;
|
|
391
400
|
|
|
@@ -456,7 +465,8 @@ function syncLocalCredential(tool) {
|
|
|
456
465
|
}
|
|
457
466
|
|
|
458
467
|
const credentials = nativeCredentials.map((metadata) => upsertCredential(tool, metadata, {
|
|
459
|
-
source: 'synced-local'
|
|
468
|
+
source: 'synced-local',
|
|
469
|
+
fingerprintMode: 'primary-token'
|
|
460
470
|
}));
|
|
461
471
|
|
|
462
472
|
return {
|
|
@@ -4,18 +4,15 @@ const crypto = require('crypto');
|
|
|
4
4
|
const { PATHS } = require('../../config/paths');
|
|
5
5
|
const { clearNativeOAuth } = require('./native-oauth-adapters');
|
|
6
6
|
const { setChannelConfig } = require('./opencode-settings-manager');
|
|
7
|
+
const { normalizeGatewaySourceType } = require('./base/proxy-utils');
|
|
7
8
|
|
|
8
9
|
/**
|
|
9
10
|
* OpenCode 渠道管理服务
|
|
10
11
|
* 存储位置: ~/.cc-tool/opencode-channels.json
|
|
11
12
|
*/
|
|
12
13
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (normalized === 'claude') return 'claude';
|
|
16
|
-
if (normalized === 'gemini') return 'gemini';
|
|
17
|
-
return 'codex';
|
|
18
|
-
}
|
|
14
|
+
// normalizeGatewaySourceType imported from base/proxy-utils
|
|
15
|
+
// OpenCode default fallback is 'codex'
|
|
19
16
|
|
|
20
17
|
function normalizeApiKey(value) {
|
|
21
18
|
if (typeof value !== 'string') return '';
|
|
@@ -80,7 +77,7 @@ function loadChannels() {
|
|
|
80
77
|
modelRedirects: ch.modelRedirects || [],
|
|
81
78
|
speedTestModel: ch.speedTestModel || null,
|
|
82
79
|
wireApi: ch.wireApi || 'openai', // OpenCode 默认使用 OpenAI 兼容格式
|
|
83
|
-
gatewaySourceType: normalizeGatewaySourceType(ch.gatewaySourceType),
|
|
80
|
+
gatewaySourceType: normalizeGatewaySourceType(ch.gatewaySourceType, 'codex'),
|
|
84
81
|
allowedModels: ch.allowedModels || []
|
|
85
82
|
};
|
|
86
83
|
normalized.providerKey = deriveProviderKey(normalized);
|
|
@@ -132,7 +129,7 @@ function createChannel(name, baseUrl, apiKey, extraConfig = {}) {
|
|
|
132
129
|
modelRedirects: extraConfig.modelRedirects || [],
|
|
133
130
|
speedTestModel: extraConfig.speedTestModel || null,
|
|
134
131
|
model: extraConfig.model || null,
|
|
135
|
-
gatewaySourceType: normalizeGatewaySourceType(extraConfig.gatewaySourceType),
|
|
132
|
+
gatewaySourceType: normalizeGatewaySourceType(extraConfig.gatewaySourceType, 'codex'),
|
|
136
133
|
providerKey: extraConfig.providerKey || null,
|
|
137
134
|
presetId: extraConfig.presetId || null,
|
|
138
135
|
websiteUrl: extraConfig.websiteUrl || '',
|
|
@@ -169,7 +166,8 @@ function updateChannel(channelId, updates) {
|
|
|
169
166
|
gatewaySourceType: normalizeGatewaySourceType(
|
|
170
167
|
updates.gatewaySourceType !== undefined
|
|
171
168
|
? updates.gatewaySourceType
|
|
172
|
-
: oldChannel.gatewaySourceType
|
|
169
|
+
: oldChannel.gatewaySourceType,
|
|
170
|
+
'codex'
|
|
173
171
|
),
|
|
174
172
|
updatedAt: Date.now()
|
|
175
173
|
};
|