coding-tool-x 3.4.10 → 3.4.12
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-0PgPv5qO.js → Analytics-Q_QFMM2p.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-pBGoYbCP.js → ConfigTemplates-Ca8C7VtV.js} +1 -1
- package/dist/web/assets/{Home-BRN882om.js → Home-ChIIT4Ew.js} +1 -1
- package/dist/web/assets/{PluginManager-am97Huts.js → PluginManager-C3w7p-sj.js} +1 -1
- package/dist/web/assets/{ProjectList-CXS9KJN1.js → ProjectList-BfJTDXDw.js} +1 -1
- package/dist/web/assets/{SessionList-BZyrzH7J.js → SessionList-a3EoL0hZ.js} +1 -1
- package/dist/web/assets/{SkillManager-p1CI0tYa.js → SkillManager-CohW5iqS.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-CUPvLoba.js → WorkspaceManager-DxaKsaJK.js} +1 -1
- package/dist/web/assets/index-B02wDWNC.css +1 -0
- package/dist/web/assets/index-EMrm1wk-.js +2 -0
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/src/commands/toggle-proxy.js +1 -1
- package/src/server/api/claude-hooks.js +23 -536
- package/src/server/api/codex-proxy.js +1 -1
- package/src/server/api/opencode-proxy.js +92 -7
- package/src/server/index.js +2 -1
- package/src/server/services/codex-channels.js +1 -1
- package/src/server/services/gemini-channels.js +52 -31
- package/src/server/services/native-oauth-adapters.js +152 -5
- package/src/server/services/notification-hooks.js +579 -5
- package/src/server/services/opencode-channels.js +11 -2
- package/dist/web/assets/index-B4Wl3JfR.js +0 -2
- package/dist/web/assets/index-Bgt_oqoE.css +0 -1
|
@@ -13,9 +13,16 @@ const {
|
|
|
13
13
|
restoreSettings,
|
|
14
14
|
deleteBackup,
|
|
15
15
|
isProxyConfig,
|
|
16
|
-
getCurrentProxyPort
|
|
16
|
+
getCurrentProxyPort,
|
|
17
|
+
readConfig,
|
|
18
|
+
selectConfigPath
|
|
17
19
|
} = require('../services/opencode-settings-manager');
|
|
18
|
-
const {
|
|
20
|
+
const {
|
|
21
|
+
getChannels,
|
|
22
|
+
getEnabledChannels,
|
|
23
|
+
applyChannelToSettings,
|
|
24
|
+
getEffectiveApiKeyCandidates
|
|
25
|
+
} = require('../services/opencode-channels');
|
|
19
26
|
const { getSchedulerState } = require('../services/channel-scheduler');
|
|
20
27
|
const { PATHS, ensureStorageDirMigrated } = require('../../config/paths');
|
|
21
28
|
const fs = require('fs');
|
|
@@ -60,6 +67,83 @@ function resolveActiveChannel(channels, activeChannelId = null) {
|
|
|
60
67
|
|| null;
|
|
61
68
|
}
|
|
62
69
|
|
|
70
|
+
function sanitizeManagedProviderId(value) {
|
|
71
|
+
return String(value || '')
|
|
72
|
+
.toLowerCase()
|
|
73
|
+
.replace(/[^a-z0-9-]/g, '-')
|
|
74
|
+
.replace(/-+/g, '-')
|
|
75
|
+
.replace(/^-|-$/g, '')
|
|
76
|
+
|| 'channel';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function getCurrentProviderIdFromConfig(config) {
|
|
80
|
+
const modelRef = String(config?.model || '').trim();
|
|
81
|
+
if (modelRef.includes('/')) {
|
|
82
|
+
return modelRef.split('/')[0].trim();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const providerIds = config?.provider && typeof config.provider === 'object'
|
|
86
|
+
? Object.keys(config.provider).filter(Boolean)
|
|
87
|
+
: [];
|
|
88
|
+
|
|
89
|
+
return providerIds.length === 1 ? providerIds[0] : '';
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function getManagedProviderIdsForChannel(channel) {
|
|
93
|
+
const providerKey = String(channel?.providerKey || '').trim();
|
|
94
|
+
const fallbackName = String(channel?.name || '').trim();
|
|
95
|
+
const base = providerKey || fallbackName || 'ctx-channel';
|
|
96
|
+
return new Set([
|
|
97
|
+
providerKey,
|
|
98
|
+
sanitizeManagedProviderId(base)
|
|
99
|
+
].filter(Boolean));
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function findActiveChannelFromNativeConfig(channels = []) {
|
|
103
|
+
if (!Array.isArray(channels) || channels.length === 0) {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
const configPath = selectConfigPath();
|
|
109
|
+
if (!configPath || !fs.existsSync(configPath)) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
const config = readConfig(configPath);
|
|
114
|
+
const providerId = getCurrentProviderIdFromConfig(config);
|
|
115
|
+
const provider = providerId && config?.provider && typeof config.provider === 'object'
|
|
116
|
+
? config.provider[providerId]
|
|
117
|
+
: null;
|
|
118
|
+
const baseUrl = String(provider?.options?.baseURL || '').trim();
|
|
119
|
+
const apiKey = String(provider?.options?.apiKey || '').trim();
|
|
120
|
+
|
|
121
|
+
if (providerId) {
|
|
122
|
+
const providerMatched = channels.find((channel) => getManagedProviderIdsForChannel(channel).has(providerId));
|
|
123
|
+
if (providerMatched) {
|
|
124
|
+
return providerMatched;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
if (baseUrl && apiKey) {
|
|
129
|
+
const exactMatched = channels.find((channel) => (
|
|
130
|
+
channel.baseUrl === baseUrl && getEffectiveApiKeyCandidates(channel).includes(apiKey)
|
|
131
|
+
));
|
|
132
|
+
if (exactMatched) {
|
|
133
|
+
return exactMatched;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (baseUrl) {
|
|
138
|
+
return channels.find((channel) => channel.baseUrl === baseUrl) || null;
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.warn('[OpenCode Proxy] Failed to infer native active channel:', error.message);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
return null;
|
|
145
|
+
}
|
|
146
|
+
|
|
63
147
|
// 保存激活渠道ID
|
|
64
148
|
function saveActiveChannelId(channelId) {
|
|
65
149
|
ensureStorageDirMigrated();
|
|
@@ -127,14 +211,15 @@ router.post('/start', async (req, res) => {
|
|
|
127
211
|
try {
|
|
128
212
|
// 1. 获取当前启用的渠道
|
|
129
213
|
const enabledChannels = getEnabledChannels();
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
if (!currentChannel) {
|
|
214
|
+
if (enabledChannels.length === 0) {
|
|
133
215
|
return res.status(400).json({
|
|
134
216
|
error: 'No enabled OpenCode channel found. Please create and enable a channel first.'
|
|
135
217
|
});
|
|
136
218
|
}
|
|
137
219
|
|
|
220
|
+
const { channels: allChannels } = getChannels();
|
|
221
|
+
const currentChannel = findActiveChannelFromNativeConfig(allChannels) || enabledChannels[0];
|
|
222
|
+
|
|
138
223
|
// 2. 保存当前激活渠道ID
|
|
139
224
|
saveActiveChannelId(currentChannel.id);
|
|
140
225
|
console.log(`[OpenCode Proxy] Saved active channel: ${currentChannel.name} (${currentChannel.id})`);
|
|
@@ -193,13 +278,13 @@ router.post('/start', async (req, res) => {
|
|
|
193
278
|
};
|
|
194
279
|
});
|
|
195
280
|
|
|
196
|
-
const
|
|
281
|
+
const primaryProxyChannel = enabledChannels[0] || currentChannel;
|
|
282
|
+
const activeModel = primaryProxyChannel?.model || primaryProxyChannel?.speedTestModel || null;
|
|
197
283
|
setProxyConfig(proxyResult.port, { channels: channelPayloads, model: activeModel });
|
|
198
284
|
|
|
199
285
|
// 5. 广播状态更新
|
|
200
286
|
const { broadcastProxyState } = require('../websocket-server');
|
|
201
287
|
const updatedStatus = getOpenCodeProxyStatus();
|
|
202
|
-
const { channels: allChannels } = getChannels();
|
|
203
288
|
broadcastProxyState('opencode', updatedStatus, currentChannel, allChannels);
|
|
204
289
|
|
|
205
290
|
res.json({
|
package/src/server/index.js
CHANGED
|
@@ -199,11 +199,12 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
|
|
|
199
199
|
app.use('/api/env', require('./api/env'));
|
|
200
200
|
app.use('/api/skills', require('./api/skills'));
|
|
201
201
|
const claudeHooks = require('./api/claude-hooks');
|
|
202
|
+
const notificationHooks = require('./services/notification-hooks');
|
|
202
203
|
app.use('/api/claude/hooks', claudeHooks);
|
|
203
204
|
app.use('/api/hooks', require('./api/hooks'));
|
|
204
205
|
|
|
205
206
|
// 初始化 Claude hooks 默认配置(自动开启任务完成通知)
|
|
206
|
-
|
|
207
|
+
notificationHooks.initDefaultHooks();
|
|
207
208
|
|
|
208
209
|
// Claude Code 专有功能 API
|
|
209
210
|
app.use('/api/commands', require('./api/commands'));
|
|
@@ -73,7 +73,7 @@ function writeAnnotatedCodexConfig(configPath, config, comments = []) {
|
|
|
73
73
|
function pruneManagedProviders(existingProviders, currentProviderKey, allChannels) {
|
|
74
74
|
const knownKeys = new Set(allChannels.map(ch => ch.providerKey).filter(Boolean));
|
|
75
75
|
for (const key of Object.keys(existingProviders)) {
|
|
76
|
-
if (key !== currentProviderKey &&
|
|
76
|
+
if (key === 'cc-proxy' || (key !== currentProviderKey && knownKeys.has(key))) {
|
|
77
77
|
delete existingProviders[key];
|
|
78
78
|
}
|
|
79
79
|
}
|
|
@@ -34,6 +34,43 @@ function getChannelsFilePath() {
|
|
|
34
34
|
return PATHS.channels.gemini;
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
function readExistingGeminiEnv() {
|
|
38
|
+
const envPath = path.join(getGeminiDir(), '.env');
|
|
39
|
+
if (!fs.existsSync(envPath)) {
|
|
40
|
+
return {};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const env = {};
|
|
44
|
+
try {
|
|
45
|
+
const content = fs.readFileSync(envPath, 'utf8');
|
|
46
|
+
content.split('\n').forEach((line) => {
|
|
47
|
+
const trimmed = line.trim();
|
|
48
|
+
if (!trimmed || trimmed.startsWith('#')) return;
|
|
49
|
+
|
|
50
|
+
const match = trimmed.match(/^([^=]+)=(.*)$/);
|
|
51
|
+
if (match) {
|
|
52
|
+
env[match[1].trim()] = match[2].trim();
|
|
53
|
+
}
|
|
54
|
+
});
|
|
55
|
+
} catch (err) {
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return env;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function writeGeminiEnv(env = {}) {
|
|
63
|
+
const envPath = path.join(getGeminiDir(), '.env');
|
|
64
|
+
const content = Object.entries(env)
|
|
65
|
+
.map(([key, value]) => `${key}=${value}`)
|
|
66
|
+
.join('\n');
|
|
67
|
+
|
|
68
|
+
fs.writeFileSync(envPath, content ? `${content}\n` : '', 'utf8');
|
|
69
|
+
if (process.platform !== 'win32') {
|
|
70
|
+
fs.chmodSync(envPath, 0o600);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
|
|
37
74
|
// 检查是否在代理模式
|
|
38
75
|
function isProxyConfig() {
|
|
39
76
|
const envPath = path.join(getGeminiDir(), '.env');
|
|
@@ -295,21 +332,12 @@ function applyChannelToSettings(channelId, channels = null) {
|
|
|
295
332
|
fs.mkdirSync(geminiDir, { recursive: true });
|
|
296
333
|
}
|
|
297
334
|
|
|
298
|
-
const
|
|
299
|
-
|
|
300
|
-
// 构建 .env 内容
|
|
335
|
+
const env = readExistingGeminiEnv();
|
|
301
336
|
const effectiveApiKey = getEffectiveApiKey(channel) || '';
|
|
302
|
-
|
|
303
|
-
GEMINI_API_KEY
|
|
304
|
-
GEMINI_MODEL
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
fs.writeFileSync(envPath, envContent, 'utf8');
|
|
308
|
-
|
|
309
|
-
// 设置 .env 文件权限为 600 (仅所有者可读写)
|
|
310
|
-
if (process.platform !== 'win32') {
|
|
311
|
-
fs.chmodSync(envPath, 0o600);
|
|
312
|
-
}
|
|
337
|
+
env.GOOGLE_GEMINI_BASE_URL = channel.baseUrl;
|
|
338
|
+
env.GEMINI_API_KEY = effectiveApiKey;
|
|
339
|
+
env.GEMINI_MODEL = channel.model;
|
|
340
|
+
writeGeminiEnv(env);
|
|
313
341
|
|
|
314
342
|
// 确保 settings.json 存在并配置正确的认证模式
|
|
315
343
|
const settingsPath = path.join(geminiDir, 'settings.json');
|
|
@@ -364,32 +392,25 @@ function writeGeminiConfigForMultiChannel(allChannels) {
|
|
|
364
392
|
fs.mkdirSync(geminiDir, { recursive: true });
|
|
365
393
|
}
|
|
366
394
|
|
|
367
|
-
const envPath = path.join(geminiDir, '.env');
|
|
368
|
-
|
|
369
395
|
// 获取第一个启用的渠道作为默认配置
|
|
370
396
|
const enabledChannels = allChannels.filter(c => c.enabled !== false);
|
|
371
397
|
const defaultChannel = enabledChannels[0] || allChannels[0];
|
|
372
398
|
|
|
399
|
+
const env = readExistingGeminiEnv();
|
|
400
|
+
|
|
373
401
|
if (!defaultChannel) {
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
402
|
+
delete env.GOOGLE_GEMINI_BASE_URL;
|
|
403
|
+
delete env.GEMINI_API_KEY;
|
|
404
|
+
delete env.GEMINI_MODEL;
|
|
405
|
+
writeGeminiEnv(env);
|
|
377
406
|
return;
|
|
378
407
|
}
|
|
379
408
|
|
|
380
|
-
// 构建 .env 内容
|
|
381
409
|
const effectiveApiKey = getEffectiveApiKey(defaultChannel) || '';
|
|
382
|
-
|
|
383
|
-
GEMINI_API_KEY
|
|
384
|
-
GEMINI_MODEL
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
fs.writeFileSync(envPath, envContent, 'utf8');
|
|
388
|
-
|
|
389
|
-
// 设置 .env 文件权限为 600 (仅所有者可读写)
|
|
390
|
-
if (process.platform !== 'win32') {
|
|
391
|
-
fs.chmodSync(envPath, 0o600);
|
|
392
|
-
}
|
|
410
|
+
env.GOOGLE_GEMINI_BASE_URL = defaultChannel.baseUrl;
|
|
411
|
+
env.GEMINI_API_KEY = effectiveApiKey;
|
|
412
|
+
env.GEMINI_MODEL = defaultChannel.model;
|
|
413
|
+
writeGeminiEnv(env);
|
|
393
414
|
|
|
394
415
|
// 确保 settings.json 存在并配置正确的认证模式
|
|
395
416
|
const settingsPath = path.join(geminiDir, 'settings.json');
|
|
@@ -723,18 +723,21 @@ function clearOpenCodeOAuth() {
|
|
|
723
723
|
return;
|
|
724
724
|
}
|
|
725
725
|
|
|
726
|
+
const removedProviderIds = [];
|
|
726
727
|
Object.keys(payload).forEach((providerId) => {
|
|
727
728
|
if (payload[providerId]?.type === 'oauth') {
|
|
729
|
+
removedProviderIds.push(providerId);
|
|
728
730
|
delete payload[providerId];
|
|
729
731
|
}
|
|
730
732
|
});
|
|
731
733
|
|
|
732
734
|
if (Object.keys(payload).length === 0) {
|
|
733
735
|
removeFileIfExists(NATIVE_PATHS.opencode.auth);
|
|
734
|
-
|
|
736
|
+
} else {
|
|
737
|
+
writeJsonFile(NATIVE_PATHS.opencode.auth, payload);
|
|
735
738
|
}
|
|
736
739
|
|
|
737
|
-
|
|
740
|
+
syncOpenCodeConfigAfterOAuthRemoval(removedProviderIds);
|
|
738
741
|
}
|
|
739
742
|
|
|
740
743
|
function disableOpenCodeOAuthCredential(credential = {}) {
|
|
@@ -745,6 +748,7 @@ function disableOpenCodeOAuthCredential(credential = {}) {
|
|
|
745
748
|
return;
|
|
746
749
|
}
|
|
747
750
|
|
|
751
|
+
const removedProviderIds = [];
|
|
748
752
|
Object.keys(payload).forEach((key) => {
|
|
749
753
|
const target = payload[key];
|
|
750
754
|
if (!target || target.type !== 'oauth') {
|
|
@@ -754,16 +758,18 @@ function disableOpenCodeOAuthCredential(credential = {}) {
|
|
|
754
758
|
const providerMatched = providerId && key === providerId;
|
|
755
759
|
const tokenMatched = accessToken && String(target.access || '').trim() === accessToken;
|
|
756
760
|
if (providerMatched || tokenMatched) {
|
|
761
|
+
removedProviderIds.push(key);
|
|
757
762
|
delete payload[key];
|
|
758
763
|
}
|
|
759
764
|
});
|
|
760
765
|
|
|
761
766
|
if (Object.keys(payload).length === 0) {
|
|
762
767
|
removeFileIfExists(NATIVE_PATHS.opencode.auth);
|
|
763
|
-
|
|
768
|
+
} else {
|
|
769
|
+
writeJsonFile(NATIVE_PATHS.opencode.auth, payload);
|
|
764
770
|
}
|
|
765
771
|
|
|
766
|
-
|
|
772
|
+
syncOpenCodeConfigAfterOAuthRemoval(removedProviderIds);
|
|
767
773
|
}
|
|
768
774
|
|
|
769
775
|
function isManagedOpenCodeProvider(provider) {
|
|
@@ -780,6 +786,44 @@ function isManagedOpenCodeProvider(provider) {
|
|
|
780
786
|
return apiKey === 'PROXY_KEY' && (baseUrl.includes('127.0.0.1') || baseUrl.includes('localhost'));
|
|
781
787
|
}
|
|
782
788
|
|
|
789
|
+
function isProxyBackedOpenCodeProvider(provider) {
|
|
790
|
+
if (!provider || typeof provider !== 'object') {
|
|
791
|
+
return false;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const apiKey = String(provider?.options?.apiKey || '').trim();
|
|
795
|
+
const baseUrl = String(provider?.options?.baseURL || '').trim();
|
|
796
|
+
return apiKey === 'PROXY_KEY' && (baseUrl.includes('127.0.0.1') || baseUrl.includes('localhost'));
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
function isMeaningfulOpenCodeProvider(provider) {
|
|
800
|
+
if (!provider || typeof provider !== 'object') {
|
|
801
|
+
return false;
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
if (provider.__ctx_managed__ === true) {
|
|
805
|
+
return true;
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
if (typeof provider.npm === 'string' && provider.npm.trim()) {
|
|
809
|
+
return true;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
if (typeof provider.name === 'string' && provider.name.trim()) {
|
|
813
|
+
return true;
|
|
814
|
+
}
|
|
815
|
+
|
|
816
|
+
if (provider.options && typeof provider.options === 'object' && Object.keys(provider.options).length > 0) {
|
|
817
|
+
return true;
|
|
818
|
+
}
|
|
819
|
+
|
|
820
|
+
if (provider.models && typeof provider.models === 'object' && Object.keys(provider.models).length > 0) {
|
|
821
|
+
return true;
|
|
822
|
+
}
|
|
823
|
+
|
|
824
|
+
return false;
|
|
825
|
+
}
|
|
826
|
+
|
|
783
827
|
function clearOpenCodeManagedModelSelection(config) {
|
|
784
828
|
const modelRef = String(config?.model || '').trim();
|
|
785
829
|
if (!modelRef || !modelRef.includes('/')) {
|
|
@@ -797,6 +841,107 @@ function clearOpenCodeManagedModelSelection(config) {
|
|
|
797
841
|
}
|
|
798
842
|
}
|
|
799
843
|
|
|
844
|
+
function getConfiguredOpenCodeProviderId(config) {
|
|
845
|
+
const modelRef = String(config?.model || '').trim();
|
|
846
|
+
if (modelRef.includes('/')) {
|
|
847
|
+
return modelRef.split('/')[0].trim();
|
|
848
|
+
}
|
|
849
|
+
|
|
850
|
+
const providerIds = config?.provider && typeof config.provider === 'object'
|
|
851
|
+
? Object.keys(config.provider).filter(Boolean)
|
|
852
|
+
: [];
|
|
853
|
+
return providerIds.length === 1 ? providerIds[0] : '';
|
|
854
|
+
}
|
|
855
|
+
|
|
856
|
+
function buildOpenCodeModelRef(providerId, provider) {
|
|
857
|
+
if (!providerId || !provider || typeof provider !== 'object') {
|
|
858
|
+
return '';
|
|
859
|
+
}
|
|
860
|
+
|
|
861
|
+
const modelIds = provider.models && typeof provider.models === 'object'
|
|
862
|
+
? Object.keys(provider.models).filter(Boolean)
|
|
863
|
+
: [];
|
|
864
|
+
|
|
865
|
+
if (modelIds.length === 0) {
|
|
866
|
+
return '';
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
return `${providerId}/${modelIds[0]}`;
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
function pickFallbackOpenCodeModel(config, excludedProviderIds = new Set()) {
|
|
873
|
+
const providers = config?.provider && typeof config.provider === 'object'
|
|
874
|
+
? Object.entries(config.provider)
|
|
875
|
+
: [];
|
|
876
|
+
|
|
877
|
+
for (const [providerId, provider] of providers) {
|
|
878
|
+
if (excludedProviderIds.has(providerId)) {
|
|
879
|
+
continue;
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
const modelRef = buildOpenCodeModelRef(providerId, provider);
|
|
883
|
+
if (modelRef) {
|
|
884
|
+
return modelRef;
|
|
885
|
+
}
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
return '';
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
function syncOpenCodeConfigAfterOAuthRemoval(removedProviderIds = []) {
|
|
892
|
+
const removedIds = new Set((removedProviderIds || []).filter(Boolean));
|
|
893
|
+
if (removedIds.size === 0) {
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
|
|
897
|
+
const configPath = opencodeSettingsManager.selectConfigPath();
|
|
898
|
+
if (!configPath || !fs.existsSync(configPath)) {
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
let config = {};
|
|
903
|
+
try {
|
|
904
|
+
config = opencodeSettingsManager.readConfig(configPath);
|
|
905
|
+
} catch {
|
|
906
|
+
return;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
config = config && typeof config === 'object' ? config : {};
|
|
910
|
+
config.provider = config.provider && typeof config.provider === 'object' ? config.provider : {};
|
|
911
|
+
|
|
912
|
+
let changed = false;
|
|
913
|
+
for (const providerId of removedIds) {
|
|
914
|
+
const provider = config.provider[providerId];
|
|
915
|
+
if (provider && !isMeaningfulOpenCodeProvider(provider)) {
|
|
916
|
+
delete config.provider[providerId];
|
|
917
|
+
changed = true;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
|
|
921
|
+
const activeProviderId = getConfiguredOpenCodeProviderId(config);
|
|
922
|
+
if (activeProviderId && removedIds.has(activeProviderId)) {
|
|
923
|
+
const activeProvider = config.provider[activeProviderId];
|
|
924
|
+
if (!isMeaningfulOpenCodeProvider(activeProvider)) {
|
|
925
|
+
const fallbackModel = pickFallbackOpenCodeModel(config, removedIds);
|
|
926
|
+
if (fallbackModel) {
|
|
927
|
+
config.model = fallbackModel;
|
|
928
|
+
} else {
|
|
929
|
+
delete config.model;
|
|
930
|
+
}
|
|
931
|
+
changed = true;
|
|
932
|
+
}
|
|
933
|
+
}
|
|
934
|
+
|
|
935
|
+
if (Object.keys(config.provider).length === 0) {
|
|
936
|
+
delete config.provider;
|
|
937
|
+
changed = true;
|
|
938
|
+
}
|
|
939
|
+
|
|
940
|
+
if (changed) {
|
|
941
|
+
opencodeSettingsManager.writeConfig(configPath, config);
|
|
942
|
+
}
|
|
943
|
+
}
|
|
944
|
+
|
|
800
945
|
function applyOpenCodeOAuth(credential) {
|
|
801
946
|
const providerId = String(credential.providerId || 'openai').trim() || 'openai';
|
|
802
947
|
const payload = readJsonFile(NATIVE_PATHS.opencode.auth, {});
|
|
@@ -840,7 +985,9 @@ function inspectOpenCodeState() {
|
|
|
840
985
|
const providers = config?.provider && typeof config.provider === 'object'
|
|
841
986
|
? Object.values(config.provider)
|
|
842
987
|
: [];
|
|
843
|
-
channelConfigured = providers.some(provider =>
|
|
988
|
+
channelConfigured = providers.some(provider => (
|
|
989
|
+
isMeaningfulOpenCodeProvider(provider) && !isProxyBackedOpenCodeProvider(provider)
|
|
990
|
+
));
|
|
844
991
|
} catch {
|
|
845
992
|
channelConfigured = false;
|
|
846
993
|
}
|