coding-tool-x 3.4.5 → 3.4.7
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-DFWyPf5C.js → Analytics-0PgPv5qO.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BFE7hmKd.js → ConfigTemplates-pBGoYbCP.js} +1 -1
- package/dist/web/assets/{Home-DZUuCrxk.js → Home-BRN882om.js} +1 -1
- package/dist/web/assets/{PluginManager-WyGY2BQN.js → PluginManager-am97Huts.js} +1 -1
- package/dist/web/assets/{ProjectList-CBc0QawN.js → ProjectList-CXS9KJN1.js} +1 -1
- package/dist/web/assets/{SessionList-CdPR7QLq.js → SessionList-BZyrzH7J.js} +1 -1
- package/dist/web/assets/{SkillManager-B5-DxQOS.js → SkillManager-p1CI0tYa.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-C7yqFjpi.js → WorkspaceManager-CUPvLoba.js} +1 -1
- package/dist/web/assets/index-B4Wl3JfR.js +2 -0
- package/dist/web/assets/{index-C1pzEgmj.css → index-Bgt_oqoE.css} +1 -1
- package/dist/web/index.html +2 -2
- package/package.json +1 -1
- package/src/reset-config.js +9 -1
- package/src/server/api/codex-channels.js +26 -0
- package/src/server/api/oauth-credentials.js +23 -1
- package/src/server/api/opencode-proxy.js +0 -2
- package/src/server/api/proxy.js +21 -23
- package/src/server/services/channels.js +13 -1
- package/src/server/services/native-oauth-adapters.js +94 -10
- package/src/server/services/oauth-credentials-service.js +44 -2
- package/src/server/services/opencode-channels.js +0 -2
- package/src/server/services/settings-manager.js +9 -1
- package/dist/web/assets/index-BDsmoSfO.js +0 -2
package/dist/web/index.html
CHANGED
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
<link rel="icon" href="/favicon.ico">
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
7
7
|
<title>CC-TOOL - ClaudeCode增强工作助手</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-B4Wl3JfR.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/markdown-DyTJGI4N.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vue-vendor-3bf-fPGP.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/vendors-CKPV1OAU.js">
|
|
12
12
|
<link rel="modulepreload" crossorigin href="/assets/naive-ui-Bdxp09n2.js">
|
|
13
13
|
<link rel="modulepreload" crossorigin href="/assets/icons-B5Pl4lrD.js">
|
|
14
14
|
<link rel="stylesheet" crossorigin href="/assets/markdown-BfC0goYb.css">
|
|
15
|
-
<link rel="stylesheet" crossorigin href="/assets/index-
|
|
15
|
+
<link rel="stylesheet" crossorigin href="/assets/index-Bgt_oqoE.css">
|
|
16
16
|
</head>
|
|
17
17
|
<body>
|
|
18
18
|
<div id="app"></div>
|
package/package.json
CHANGED
package/src/reset-config.js
CHANGED
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const { PATHS, NATIVE_PATHS, ensureStorageDirMigrated } = require('./config/paths');
|
|
3
|
+
const { isWindowsLikePlatform } = require('./utils/home-dir');
|
|
4
|
+
|
|
5
|
+
function buildEchoCommand(value) {
|
|
6
|
+
if (isWindowsLikePlatform(process.platform, process.env)) {
|
|
7
|
+
return `cmd /c echo ${value}`;
|
|
8
|
+
}
|
|
9
|
+
return `echo '${value}'`;
|
|
10
|
+
}
|
|
3
11
|
|
|
4
12
|
// 恢复配置到默认状态
|
|
5
13
|
async function resetConfig() {
|
|
@@ -56,7 +64,7 @@ async function resetConfig() {
|
|
|
56
64
|
if (!settings.env) settings.env = {};
|
|
57
65
|
settings.env.ANTHROPIC_BASE_URL = activeChannel.baseUrl;
|
|
58
66
|
settings.env.ANTHROPIC_API_KEY = activeChannel.apiKey;
|
|
59
|
-
settings.apiKeyHelper =
|
|
67
|
+
settings.apiKeyHelper = buildEchoCommand(activeChannel.apiKey);
|
|
60
68
|
|
|
61
69
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
62
70
|
console.log(`[OK] 已恢复到渠道: ${activeChannel.name}`);
|
|
@@ -25,11 +25,26 @@ const { deleteBackup } = require('../services/codex-settings-manager');
|
|
|
25
25
|
const { PATHS } = require('../../config/paths');
|
|
26
26
|
const { getDefaultSpeedTestModelByToolType } = require('../../config/model-metadata');
|
|
27
27
|
const CODEX_GATEWAY_SOURCE_TYPE = 'codex';
|
|
28
|
+
const CODEX_PROVIDER_KEY_PATTERN = /^[a-z0-9_-]+$/i;
|
|
28
29
|
|
|
29
30
|
function getDefaultCodexModel() {
|
|
30
31
|
return getDefaultSpeedTestModelByToolType('codex');
|
|
31
32
|
}
|
|
32
33
|
|
|
34
|
+
function validateCodexProviderKey(value) {
|
|
35
|
+
const normalized = String(value || '').trim();
|
|
36
|
+
if (!normalized) {
|
|
37
|
+
return 'Missing required fields: providerKey';
|
|
38
|
+
}
|
|
39
|
+
if (!CODEX_PROVIDER_KEY_PATTERN.test(normalized)) {
|
|
40
|
+
return 'Invalid providerKey: only letters, numbers, underscores, and hyphens are allowed';
|
|
41
|
+
}
|
|
42
|
+
if (normalized.toLowerCase() === 'openai') {
|
|
43
|
+
return 'Invalid providerKey: "openai" is reserved for the built-in OpenAI provider';
|
|
44
|
+
}
|
|
45
|
+
return '';
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
module.exports = (config) => {
|
|
34
49
|
/**
|
|
35
50
|
* GET /api/codex/channels
|
|
@@ -137,6 +152,11 @@ module.exports = (config) => {
|
|
|
137
152
|
return res.status(400).json({ error: 'Missing required fields: apiKey' });
|
|
138
153
|
}
|
|
139
154
|
|
|
155
|
+
const providerKeyError = validateCodexProviderKey(providerKey);
|
|
156
|
+
if (providerKeyError) {
|
|
157
|
+
return res.status(400).json({ error: providerKeyError });
|
|
158
|
+
}
|
|
159
|
+
|
|
140
160
|
// wireApi 固定为 'responses' (OpenAI Responses API 格式)
|
|
141
161
|
const channel = createChannel(name, providerKey, baseUrl, apiKey, 'responses', {
|
|
142
162
|
websiteUrl,
|
|
@@ -168,6 +188,12 @@ module.exports = (config) => {
|
|
|
168
188
|
|
|
169
189
|
const { channelId } = req.params;
|
|
170
190
|
const updates = req.body;
|
|
191
|
+
if (Object.prototype.hasOwnProperty.call(updates, 'providerKey')) {
|
|
192
|
+
const providerKeyError = validateCodexProviderKey(updates.providerKey);
|
|
193
|
+
if (providerKeyError) {
|
|
194
|
+
return res.status(400).json({ error: providerKeyError });
|
|
195
|
+
}
|
|
196
|
+
}
|
|
171
197
|
|
|
172
198
|
const channel = updateChannel(channelId, updates);
|
|
173
199
|
// 清除该渠道的模型重定向日志缓存,使下次请求时重新打印
|
|
@@ -9,6 +9,7 @@ const {
|
|
|
9
9
|
setDefaultCredential,
|
|
10
10
|
deleteCredential,
|
|
11
11
|
applyStoredCredential,
|
|
12
|
+
disableStoredCredential,
|
|
12
13
|
clearNativeOAuthState,
|
|
13
14
|
fetchCredentialUsage
|
|
14
15
|
} = require('../services/oauth-credentials-service');
|
|
@@ -117,10 +118,31 @@ router.post('/:tool/:credentialId/apply', async (req, res) => {
|
|
|
117
118
|
assertTool(tool);
|
|
118
119
|
const result = await applyStoredCredential(tool, credentialId);
|
|
119
120
|
broadcastToolProxyState(tool);
|
|
121
|
+
const message = tool === 'opencode'
|
|
122
|
+
? 'opencode 已应用 OAuth 凭证,并保留现有 API providers'
|
|
123
|
+
: `${tool} 已切换到 OAuth 凭证控制`;
|
|
120
124
|
res.json({
|
|
121
125
|
tool,
|
|
122
126
|
...result,
|
|
123
|
-
message
|
|
127
|
+
message
|
|
128
|
+
});
|
|
129
|
+
} catch (error) {
|
|
130
|
+
res.status(error.statusCode || 500).json({ error: error.message });
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
router.post('/:tool/:credentialId/disable-native', (req, res) => {
|
|
135
|
+
try {
|
|
136
|
+
const { tool, credentialId } = req.params;
|
|
137
|
+
assertTool(tool);
|
|
138
|
+
const result = disableStoredCredential(tool, credentialId);
|
|
139
|
+
broadcastToolProxyState(tool);
|
|
140
|
+
res.json({
|
|
141
|
+
tool,
|
|
142
|
+
...result,
|
|
143
|
+
message: tool === 'opencode'
|
|
144
|
+
? 'opencode OAuth provider 已关闭'
|
|
145
|
+
: `${tool} 本机 OAuth 已关闭`
|
|
124
146
|
});
|
|
125
147
|
} catch (error) {
|
|
126
148
|
res.status(error.statusCode || 500).json({ error: error.message });
|
|
@@ -16,7 +16,6 @@ const {
|
|
|
16
16
|
getCurrentProxyPort
|
|
17
17
|
} = require('../services/opencode-settings-manager');
|
|
18
18
|
const { getChannels, getEnabledChannels, applyChannelToSettings } = require('../services/opencode-channels');
|
|
19
|
-
const { clearNativeOAuth } = require('../services/native-oauth-adapters');
|
|
20
19
|
const { getSchedulerState } = require('../services/channel-scheduler');
|
|
21
20
|
const { PATHS, ensureStorageDirMigrated } = require('../../config/paths');
|
|
22
21
|
const fs = require('fs');
|
|
@@ -195,7 +194,6 @@ router.post('/start', async (req, res) => {
|
|
|
195
194
|
});
|
|
196
195
|
|
|
197
196
|
const activeModel = currentChannel.model || currentChannel.speedTestModel || null;
|
|
198
|
-
clearNativeOAuth('opencode');
|
|
199
197
|
setProxyConfig(proxyResult.port, { channels: channelPayloads, model: activeModel });
|
|
200
198
|
|
|
201
199
|
// 5. 广播状态更新
|
package/src/server/api/proxy.js
CHANGED
|
@@ -107,34 +107,32 @@ function findActiveChannelFromSettings() {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
if (!baseUrl || !apiKey || baseUrl.includes('127.0.0.1')) {
|
|
111
|
-
console.log('[Proxy] Invalid settings: empty baseUrl/apiKey or localhost detected');
|
|
112
|
-
return null;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
110
|
const channels = getAllChannels();
|
|
116
111
|
|
|
117
112
|
// Level 1: Exact match (baseUrl + apiKey)
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
113
|
+
if (baseUrl && apiKey && !baseUrl.includes('127.0.0.1')) {
|
|
114
|
+
let matchingChannel = channels.find(ch =>
|
|
115
|
+
ch.baseUrl === baseUrl && ch.apiKey === apiKey
|
|
116
|
+
);
|
|
117
|
+
if (matchingChannel) {
|
|
118
|
+
console.log(`[Proxy] Level 1 - Exact match: ${matchingChannel.name}`);
|
|
119
|
+
return matchingChannel;
|
|
120
|
+
}
|
|
126
121
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
122
|
+
// Level 2: Match by baseUrl only (when apiKey differs)
|
|
123
|
+
matchingChannel = channels.find(ch => ch.baseUrl === baseUrl);
|
|
124
|
+
if (matchingChannel) {
|
|
125
|
+
console.log(`[Proxy] Level 2 - Matched by baseUrl only: ${matchingChannel.name}`);
|
|
126
|
+
return matchingChannel;
|
|
127
|
+
}
|
|
128
|
+
} else {
|
|
129
|
+
console.log('[Proxy] settings.json has no valid baseUrl/apiKey, falling back to channel list');
|
|
132
130
|
}
|
|
133
131
|
|
|
134
132
|
// Level 3: Use active-channel.json for last known active channel
|
|
135
133
|
const activeChannelId = loadActiveChannelId();
|
|
136
134
|
if (activeChannelId) {
|
|
137
|
-
matchingChannel = channels.find(ch => ch.id === activeChannelId);
|
|
135
|
+
const matchingChannel = channels.find(ch => ch.id === activeChannelId);
|
|
138
136
|
if (matchingChannel) {
|
|
139
137
|
console.log(`[Proxy] Level 3 - Using last active channel: ${matchingChannel.name}`);
|
|
140
138
|
return matchingChannel;
|
|
@@ -142,10 +140,10 @@ function findActiveChannelFromSettings() {
|
|
|
142
140
|
}
|
|
143
141
|
|
|
144
142
|
// Level 4: Return first enabled channel as last resort
|
|
145
|
-
|
|
146
|
-
if (
|
|
147
|
-
console.log(`[Proxy] Level 4 - Using first enabled channel: ${
|
|
148
|
-
return
|
|
143
|
+
const fallbackChannel = channels.find(ch => ch.enabled !== false);
|
|
144
|
+
if (fallbackChannel) {
|
|
145
|
+
console.log(`[Proxy] Level 4 - Using first enabled channel: ${fallbackChannel.name}`);
|
|
146
|
+
return fallbackChannel;
|
|
149
147
|
}
|
|
150
148
|
|
|
151
149
|
console.log('[Proxy] No matching channel found after all fallback levels');
|
|
@@ -4,6 +4,7 @@ const BaseChannelService = require('./base/base-channel-service');
|
|
|
4
4
|
const { isProxyConfig } = require('./settings-manager');
|
|
5
5
|
const { PATHS, NATIVE_PATHS } = require('../../config/paths');
|
|
6
6
|
const { clearNativeOAuth } = require('./native-oauth-adapters');
|
|
7
|
+
const { isWindowsLikePlatform } = require('../../utils/home-dir');
|
|
7
8
|
|
|
8
9
|
// ── Claude 特有工具函数 ──
|
|
9
10
|
|
|
@@ -51,7 +52,10 @@ function extractApiKeyFromHelper(apiKeyHelper) {
|
|
|
51
52
|
}
|
|
52
53
|
|
|
53
54
|
function buildApiKeyHelperCommand() {
|
|
54
|
-
|
|
55
|
+
if (isWindowsLikePlatform(process.platform, process.env)) {
|
|
56
|
+
return 'cmd /c echo ctx-managed';
|
|
57
|
+
}
|
|
58
|
+
return "echo 'ctx-managed'";
|
|
55
59
|
}
|
|
56
60
|
|
|
57
61
|
// ── Claude 原生设置写入 ──
|
|
@@ -105,6 +109,10 @@ function updateClaudeSettingsWithModelConfig(channel) {
|
|
|
105
109
|
delete settings.env.NO_PROXY;
|
|
106
110
|
}
|
|
107
111
|
|
|
112
|
+
if (settings.env && Object.keys(settings.env).length === 0) {
|
|
113
|
+
delete settings.env;
|
|
114
|
+
}
|
|
115
|
+
|
|
108
116
|
settings.apiKeyHelper = buildApiKeyHelperCommand();
|
|
109
117
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
110
118
|
}
|
|
@@ -133,6 +141,10 @@ function updateClaudeSettings(baseUrl, apiKey) {
|
|
|
133
141
|
settings.env.ANTHROPIC_API_KEY = apiKey;
|
|
134
142
|
}
|
|
135
143
|
|
|
144
|
+
if (settings.env && Object.keys(settings.env).length === 0) {
|
|
145
|
+
delete settings.env;
|
|
146
|
+
}
|
|
147
|
+
|
|
136
148
|
settings.apiKeyHelper = buildApiKeyHelperCommand();
|
|
137
149
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), 'utf8');
|
|
138
150
|
}
|
|
@@ -219,7 +219,7 @@ function inspectClaudeState() {
|
|
|
219
219
|
|
|
220
220
|
return {
|
|
221
221
|
tool: 'claude',
|
|
222
|
-
mode: proxyStatus.running ? 'proxy' : (
|
|
222
|
+
mode: proxyStatus.running ? 'proxy' : (channelConfigured ? 'channel' : (nativeOAuth ? 'oauth' : 'idle')),
|
|
223
223
|
proxyRunning: proxyStatus.running,
|
|
224
224
|
oauthPresent: Boolean(nativeOAuth),
|
|
225
225
|
channelConfigured,
|
|
@@ -395,7 +395,7 @@ function inspectCodexState() {
|
|
|
395
395
|
|
|
396
396
|
return {
|
|
397
397
|
tool: 'codex',
|
|
398
|
-
mode: proxyStatus.running ? 'proxy' : (
|
|
398
|
+
mode: proxyStatus.running ? 'proxy' : (channelConfigured ? 'channel' : (nativeOAuth ? 'oauth' : 'idle')),
|
|
399
399
|
proxyRunning: proxyStatus.running,
|
|
400
400
|
oauthPresent: Boolean(nativeOAuth),
|
|
401
401
|
channelConfigured,
|
|
@@ -619,7 +619,7 @@ function inspectGeminiState() {
|
|
|
619
619
|
|
|
620
620
|
return {
|
|
621
621
|
tool: 'gemini',
|
|
622
|
-
mode: proxyStatus.running ? 'proxy' : (
|
|
622
|
+
mode: proxyStatus.running ? 'proxy' : (channelConfigured ? 'channel' : (nativeOAuth ? 'oauth' : 'idle')),
|
|
623
623
|
proxyRunning: proxyStatus.running,
|
|
624
624
|
oauthPresent: Boolean(nativeOAuth),
|
|
625
625
|
channelConfigured,
|
|
@@ -737,10 +737,67 @@ function clearOpenCodeOAuth() {
|
|
|
737
737
|
writeJsonFile(NATIVE_PATHS.opencode.auth, payload);
|
|
738
738
|
}
|
|
739
739
|
|
|
740
|
-
function
|
|
741
|
-
|
|
742
|
-
|
|
740
|
+
function disableOpenCodeOAuthCredential(credential = {}) {
|
|
741
|
+
const providerId = String(credential.providerId || '').trim();
|
|
742
|
+
const accessToken = String(credential.accessToken || credential.primaryToken || '').trim();
|
|
743
|
+
const payload = readJsonFile(NATIVE_PATHS.opencode.auth, {});
|
|
744
|
+
if (!payload || typeof payload !== 'object') {
|
|
745
|
+
return;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
Object.keys(payload).forEach((key) => {
|
|
749
|
+
const target = payload[key];
|
|
750
|
+
if (!target || target.type !== 'oauth') {
|
|
751
|
+
return;
|
|
752
|
+
}
|
|
753
|
+
|
|
754
|
+
const providerMatched = providerId && key === providerId;
|
|
755
|
+
const tokenMatched = accessToken && String(target.access || '').trim() === accessToken;
|
|
756
|
+
if (providerMatched || tokenMatched) {
|
|
757
|
+
delete payload[key];
|
|
758
|
+
}
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
if (Object.keys(payload).length === 0) {
|
|
762
|
+
removeFileIfExists(NATIVE_PATHS.opencode.auth);
|
|
763
|
+
return;
|
|
764
|
+
}
|
|
765
|
+
|
|
766
|
+
writeJsonFile(NATIVE_PATHS.opencode.auth, payload);
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
function isManagedOpenCodeProvider(provider) {
|
|
770
|
+
if (!provider || typeof provider !== 'object') {
|
|
771
|
+
return false;
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
if (provider.__ctx_managed__ === true) {
|
|
775
|
+
return true;
|
|
776
|
+
}
|
|
777
|
+
|
|
778
|
+
const apiKey = String(provider?.options?.apiKey || '').trim();
|
|
779
|
+
const baseUrl = String(provider?.options?.baseURL || '').trim();
|
|
780
|
+
return apiKey === 'PROXY_KEY' && (baseUrl.includes('127.0.0.1') || baseUrl.includes('localhost'));
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
function clearOpenCodeManagedModelSelection(config) {
|
|
784
|
+
const modelRef = String(config?.model || '').trim();
|
|
785
|
+
if (!modelRef || !modelRef.includes('/')) {
|
|
786
|
+
return;
|
|
787
|
+
}
|
|
788
|
+
|
|
789
|
+
const providerId = modelRef.split('/')[0].trim();
|
|
790
|
+
if (!providerId) {
|
|
791
|
+
return;
|
|
792
|
+
}
|
|
793
|
+
|
|
794
|
+
const provider = config?.provider?.[providerId];
|
|
795
|
+
if (isManagedOpenCodeProvider(provider)) {
|
|
796
|
+
delete config.model;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
743
799
|
|
|
800
|
+
function applyOpenCodeOAuth(credential) {
|
|
744
801
|
const providerId = String(credential.providerId || 'openai').trim() || 'openai';
|
|
745
802
|
const payload = readJsonFile(NATIVE_PATHS.opencode.auth, {});
|
|
746
803
|
payload[providerId] = {
|
|
@@ -758,9 +815,12 @@ function applyOpenCodeOAuth(credential) {
|
|
|
758
815
|
? opencodeSettingsManager.readConfig(configPath)
|
|
759
816
|
: {};
|
|
760
817
|
config.provider = config.provider && typeof config.provider === 'object' ? config.provider : {};
|
|
761
|
-
|
|
762
|
-
config.provider[providerId]
|
|
763
|
-
|
|
818
|
+
config.provider[providerId] = config.provider[providerId] && typeof config.provider[providerId] === 'object'
|
|
819
|
+
? config.provider[providerId]
|
|
820
|
+
: {};
|
|
821
|
+
// Preserve existing ctx-managed API providers for OpenCode coexistence, but
|
|
822
|
+
// drop the active managed selection so OAuth-backed providers become available.
|
|
823
|
+
clearOpenCodeManagedModelSelection(config);
|
|
764
824
|
opencodeSettingsManager.writeConfig(configPath, config);
|
|
765
825
|
|
|
766
826
|
return { storage: 'auth-file' };
|
|
@@ -787,7 +847,11 @@ function inspectOpenCodeState() {
|
|
|
787
847
|
|
|
788
848
|
return {
|
|
789
849
|
tool: 'opencode',
|
|
790
|
-
mode: proxyStatus.running
|
|
850
|
+
mode: proxyStatus.running
|
|
851
|
+
? 'proxy'
|
|
852
|
+
: (nativeOAuth && channelConfigured
|
|
853
|
+
? 'mixed'
|
|
854
|
+
: (nativeOAuth ? 'oauth' : (channelConfigured ? 'channel' : 'idle'))),
|
|
791
855
|
proxyRunning: proxyStatus.running,
|
|
792
856
|
oauthPresent: Boolean(nativeOAuth),
|
|
793
857
|
channelConfigured,
|
|
@@ -865,6 +929,25 @@ function clearNativeOAuth(tool) {
|
|
|
865
929
|
}
|
|
866
930
|
}
|
|
867
931
|
|
|
932
|
+
function disableNativeOAuthCredential(tool, credential = {}) {
|
|
933
|
+
switch (tool) {
|
|
934
|
+
case 'claude':
|
|
935
|
+
clearClaudeOAuth();
|
|
936
|
+
return;
|
|
937
|
+
case 'codex':
|
|
938
|
+
clearCodexOAuth();
|
|
939
|
+
return;
|
|
940
|
+
case 'gemini':
|
|
941
|
+
clearGeminiOAuth();
|
|
942
|
+
return;
|
|
943
|
+
case 'opencode':
|
|
944
|
+
disableOpenCodeOAuthCredential(credential);
|
|
945
|
+
return;
|
|
946
|
+
default:
|
|
947
|
+
throw new Error(`Unsupported OAuth tool: ${tool}`);
|
|
948
|
+
}
|
|
949
|
+
}
|
|
950
|
+
|
|
868
951
|
function applyOAuthCredential(tool, credential) {
|
|
869
952
|
switch (tool) {
|
|
870
953
|
case 'claude':
|
|
@@ -887,5 +970,6 @@ module.exports = {
|
|
|
887
970
|
readNativeOAuth,
|
|
888
971
|
readAllNativeOAuth,
|
|
889
972
|
clearNativeOAuth,
|
|
973
|
+
disableNativeOAuthCredential,
|
|
890
974
|
applyOAuthCredential
|
|
891
975
|
};
|
|
@@ -12,6 +12,7 @@ const {
|
|
|
12
12
|
inspectTool,
|
|
13
13
|
readAllNativeOAuth,
|
|
14
14
|
clearNativeOAuth,
|
|
15
|
+
disableNativeOAuthCredential,
|
|
15
16
|
applyOAuthCredential
|
|
16
17
|
} = require('./native-oauth-adapters');
|
|
17
18
|
const { maskToken, decodeJwtPayload, removeFileIfExists } = require('./oauth-utils');
|
|
@@ -300,6 +301,23 @@ function sanitizeCredential(entry, defaultCredentialId) {
|
|
|
300
301
|
};
|
|
301
302
|
}
|
|
302
303
|
|
|
304
|
+
function sanitizeNativeCredential(entry = {}) {
|
|
305
|
+
const primaryToken = entry.primaryToken
|
|
306
|
+
|| entry.accessToken
|
|
307
|
+
|| entry.token
|
|
308
|
+
|| '';
|
|
309
|
+
|
|
310
|
+
return {
|
|
311
|
+
providerId: entry.providerId || '',
|
|
312
|
+
accountId: entry.accountId || '',
|
|
313
|
+
accountEmail: entry.accountEmail || '',
|
|
314
|
+
expiresAt: entry.expiresAt || null,
|
|
315
|
+
lastRefresh: entry.lastRefresh || null,
|
|
316
|
+
storage: entry.storage || '',
|
|
317
|
+
tokenPreview: maskToken(primaryToken)
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
|
|
303
321
|
function sanitizeToolSummary(tool, toolStore) {
|
|
304
322
|
const credentials = (toolStore.credentials || [])
|
|
305
323
|
.map((entry) => sanitizeCredential(entry, toolStore.defaultCredentialId))
|
|
@@ -309,11 +327,16 @@ function sanitizeToolSummary(tool, toolStore) {
|
|
|
309
327
|
if (aTime !== bTime) return bTime - aTime;
|
|
310
328
|
return (b.createdAt || 0) - (a.createdAt || 0);
|
|
311
329
|
});
|
|
330
|
+
const nativeState = inspectTool(tool);
|
|
331
|
+
const nativeCredentials = readAllNativeOAuth(tool).map((entry) => sanitizeNativeCredential(entry));
|
|
312
332
|
return {
|
|
313
333
|
tool,
|
|
314
334
|
defaultCredentialId: toolStore.defaultCredentialId || null,
|
|
315
335
|
credentials,
|
|
316
|
-
nativeState:
|
|
336
|
+
nativeState: {
|
|
337
|
+
...nativeState,
|
|
338
|
+
nativeCredentials
|
|
339
|
+
}
|
|
317
340
|
};
|
|
318
341
|
}
|
|
319
342
|
|
|
@@ -612,7 +635,9 @@ async function applyStoredCredential(tool, credentialId) {
|
|
|
612
635
|
const entry = findStoredCredential(tool, credentialId);
|
|
613
636
|
const proxyStopped = await stopProxyIfRunning(tool);
|
|
614
637
|
cleanupManagedArtifacts(tool);
|
|
615
|
-
|
|
638
|
+
if (tool !== 'opencode') {
|
|
639
|
+
disableAllChannelsForTool(tool);
|
|
640
|
+
}
|
|
616
641
|
applyOAuthCredential(tool, entry.secrets);
|
|
617
642
|
|
|
618
643
|
// 记录最近使用时间
|
|
@@ -631,6 +656,22 @@ async function applyStoredCredential(tool, credentialId) {
|
|
|
631
656
|
};
|
|
632
657
|
}
|
|
633
658
|
|
|
659
|
+
function disableStoredCredential(tool, credentialId) {
|
|
660
|
+
assertSupportedTool(tool);
|
|
661
|
+
const entry = findStoredCredential(tool, credentialId);
|
|
662
|
+
disableNativeOAuthCredential(tool, {
|
|
663
|
+
...(entry.secrets || {}),
|
|
664
|
+
providerId: entry.providerId || entry.secrets?.providerId || '',
|
|
665
|
+
accountId: entry.accountId || entry.secrets?.accountId || ''
|
|
666
|
+
});
|
|
667
|
+
|
|
668
|
+
return {
|
|
669
|
+
credential: sanitizeCredential(entry, readStore().tools[tool]?.defaultCredentialId || null),
|
|
670
|
+
toolSummary: getToolSummary(tool),
|
|
671
|
+
nativeState: inspectTool(tool)
|
|
672
|
+
};
|
|
673
|
+
}
|
|
674
|
+
|
|
634
675
|
function clearNativeOAuthState(tool) {
|
|
635
676
|
assertSupportedTool(tool);
|
|
636
677
|
clearNativeOAuth(tool);
|
|
@@ -791,6 +832,7 @@ module.exports = {
|
|
|
791
832
|
setDefaultCredential,
|
|
792
833
|
deleteCredential,
|
|
793
834
|
applyStoredCredential,
|
|
835
|
+
disableStoredCredential,
|
|
794
836
|
clearNativeOAuthState,
|
|
795
837
|
fetchCredentialUsage
|
|
796
838
|
};
|
|
@@ -2,7 +2,6 @@ const fs = require('fs');
|
|
|
2
2
|
const path = require('path');
|
|
3
3
|
const crypto = require('crypto');
|
|
4
4
|
const { PATHS } = require('../../config/paths');
|
|
5
|
-
const { clearNativeOAuth } = require('./native-oauth-adapters');
|
|
6
5
|
const { setChannelConfig } = require('./opencode-settings-manager');
|
|
7
6
|
const { normalizeGatewaySourceType } = require('./base/proxy-utils');
|
|
8
7
|
|
|
@@ -251,7 +250,6 @@ function applyChannelToSettings(channelId) {
|
|
|
251
250
|
});
|
|
252
251
|
saveChannels(data);
|
|
253
252
|
|
|
254
|
-
clearNativeOAuth('opencode');
|
|
255
253
|
setChannelConfig(channel);
|
|
256
254
|
|
|
257
255
|
return channel;
|
|
@@ -1,5 +1,13 @@
|
|
|
1
1
|
const fs = require('fs');
|
|
2
2
|
const { NATIVE_PATHS } = require('../../config/paths');
|
|
3
|
+
const { isWindowsLikePlatform } = require('../../utils/home-dir');
|
|
4
|
+
|
|
5
|
+
function buildEchoCommand(value) {
|
|
6
|
+
if (isWindowsLikePlatform(process.platform, process.env)) {
|
|
7
|
+
return `cmd /c echo ${value}`;
|
|
8
|
+
}
|
|
9
|
+
return `echo '${value}'`;
|
|
10
|
+
}
|
|
3
11
|
|
|
4
12
|
// Claude Code 配置文件路径
|
|
5
13
|
function getSettingsPath() {
|
|
@@ -113,7 +121,7 @@ function setProxyConfig(proxyPort) {
|
|
|
113
121
|
// 修改为代理配置(使用 Claude Code 的标准格式)
|
|
114
122
|
settings.env.ANTHROPIC_BASE_URL = `http://127.0.0.1:${proxyPort}`;
|
|
115
123
|
settings.env.ANTHROPIC_API_KEY = 'PROXY_KEY';
|
|
116
|
-
settings.apiKeyHelper =
|
|
124
|
+
settings.apiKeyHelper = buildEchoCommand('PROXY_KEY');
|
|
117
125
|
|
|
118
126
|
// 写入
|
|
119
127
|
writeSettings(settings);
|