@adversity/coding-tool-x 3.1.0 → 3.1.2
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/CHANGELOG.md +39 -18
- package/README.md +8 -8
- package/dist/web/assets/ConfigTemplates-Bidwfdf2.css +1 -0
- package/dist/web/assets/ConfigTemplates-DvcbKKdS.js +1 -0
- package/dist/web/assets/Home-BJKPCBuk.css +1 -0
- package/dist/web/assets/Home-Cw-F_Wnu.js +1 -0
- package/dist/web/assets/PluginManager-ROyoZ-6m.css +1 -0
- package/dist/web/assets/PluginManager-jy_4GVxI.js +1 -0
- package/dist/web/assets/ProjectList-C1fQb9OW.css +1 -0
- package/dist/web/assets/ProjectList-Df1-NcNr.js +1 -0
- package/dist/web/assets/SessionList-BGJWyneI.css +1 -0
- package/dist/web/assets/SessionList-UWcZtC2r.js +1 -0
- package/dist/web/assets/SkillManager-D7pd-d_P.css +1 -0
- package/dist/web/assets/SkillManager-IRdseMKB.js +1 -0
- package/dist/web/assets/Terminal-BasTyDut.js +1 -0
- package/dist/web/assets/Terminal-DGNJeVtc.css +1 -0
- package/dist/web/assets/WorkspaceManager-CrwgQgmP.css +1 -0
- package/dist/web/assets/WorkspaceManager-D-D2kK1V.js +1 -0
- package/dist/web/assets/icons-kcfLIMBB.js +1 -0
- package/dist/web/assets/index-CoB3zF0K.css +1 -0
- package/dist/web/assets/index-CryrSLv8.js +2 -0
- package/dist/web/assets/markdown-BfC0goYb.css +10 -0
- package/dist/web/assets/markdown-C9MYpaSi.js +1 -0
- package/dist/web/assets/naive-ui-CSrLusZZ.js +1 -0
- package/dist/web/assets/{vendors-D2HHw_aW.js → vendors-CO3Upi1d.js} +2 -2
- package/dist/web/assets/vue-vendor-DqyWIXEb.js +45 -0
- package/dist/web/assets/xterm-6GBZ9nXN.css +32 -0
- package/dist/web/assets/xterm-BJzAjXCH.js +13 -0
- package/dist/web/index.html +8 -6
- package/package.json +4 -2
- package/src/commands/channels.js +48 -1
- package/src/commands/cli-type.js +4 -2
- package/src/commands/daemon.js +81 -12
- package/src/commands/doctor.js +10 -9
- package/src/commands/list.js +1 -1
- package/src/commands/logs.js +6 -4
- package/src/commands/port-config.js +24 -4
- package/src/commands/proxy-control.js +12 -6
- package/src/commands/search.js +1 -1
- package/src/commands/security.js +3 -2
- package/src/commands/stats.js +226 -52
- package/src/commands/switch.js +1 -1
- package/src/commands/toggle-proxy.js +31 -6
- package/src/commands/update.js +97 -0
- package/src/commands/workspace.js +1 -1
- package/src/config/default.js +41 -2
- package/src/config/loader.js +74 -8
- package/src/config/model-metadata.js +415 -0
- package/src/config/model-pricing.js +23 -93
- package/src/config/paths.js +105 -33
- package/src/index.js +64 -3
- package/src/plugins/constants.js +3 -2
- package/src/plugins/plugin-api.js +1 -1
- package/src/reset-config.js +4 -2
- package/src/server/api/agents.js +57 -14
- package/src/server/api/channels.js +112 -33
- package/src/server/api/codex-channels.js +111 -18
- package/src/server/api/codex-proxy.js +14 -8
- package/src/server/api/commands.js +71 -18
- package/src/server/api/config-export.js +0 -6
- package/src/server/api/config-registry.js +11 -3
- package/src/server/api/config.js +376 -5
- package/src/server/api/convert.js +133 -0
- package/src/server/api/dashboard.js +22 -6
- package/src/server/api/gemini-channels.js +107 -18
- package/src/server/api/gemini-proxy.js +14 -8
- package/src/server/api/gemini-sessions.js +1 -1
- package/src/server/api/health-check.js +4 -3
- package/src/server/api/mcp.js +3 -3
- package/src/server/api/opencode-channels.js +497 -0
- package/src/server/api/opencode-projects.js +99 -0
- package/src/server/api/opencode-proxy.js +207 -0
- package/src/server/api/opencode-sessions.js +345 -0
- package/src/server/api/opencode-statistics.js +57 -0
- package/src/server/api/plugins.js +66 -19
- package/src/server/api/prompts.js +2 -2
- package/src/server/api/proxy.js +7 -4
- package/src/server/api/sessions.js +3 -0
- package/src/server/api/settings.js +111 -0
- package/src/server/api/skills.js +69 -18
- package/src/server/api/workspaces.js +78 -6
- package/src/server/codex-proxy-server.js +36 -22
- package/src/server/dev-server.js +1 -1
- package/src/server/gemini-proxy-server.js +21 -7
- package/src/server/index.js +174 -58
- package/src/server/opencode-proxy-server.js +5486 -0
- package/src/server/proxy-server.js +33 -22
- package/src/server/services/agents-service.js +61 -24
- package/src/server/services/channel-scheduler.js +9 -5
- package/src/server/services/channels.js +64 -37
- package/src/server/services/codex-channels.js +56 -43
- package/src/server/services/codex-sessions.js +105 -6
- package/src/server/services/codex-settings-manager.js +271 -49
- package/src/server/services/codex-statistics-service.js +2 -2
- package/src/server/services/commands-service.js +84 -25
- package/src/server/services/config-export-service.js +7 -45
- package/src/server/services/config-registry-service.js +63 -17
- package/src/server/services/config-sync-manager.js +160 -7
- package/src/server/services/config-templates-service.js +204 -51
- package/src/server/services/env-checker.js +50 -13
- package/src/server/services/env-manager.js +155 -19
- package/src/server/services/favorites.js +5 -3
- package/src/server/services/gemini-channels.js +33 -44
- package/src/server/services/gemini-statistics-service.js +2 -2
- package/src/server/services/mcp-service.js +350 -9
- package/src/server/services/model-detector.js +707 -221
- package/src/server/services/network-access.js +80 -0
- package/src/server/services/opencode-channels.js +208 -0
- package/src/server/services/opencode-gateway-converter.js +639 -0
- package/src/server/services/opencode-sessions.js +931 -0
- package/src/server/services/opencode-settings-manager.js +478 -0
- package/src/server/services/opencode-statistics-service.js +255 -0
- package/src/server/services/plugins-service.js +479 -22
- package/src/server/services/prompts-service.js +53 -11
- package/src/server/services/proxy-runtime.js +1 -1
- package/src/server/services/repo-scanner-base.js +1 -1
- package/src/server/services/response-decoder.js +21 -0
- package/src/server/services/security-config.js +1 -1
- package/src/server/services/session-cache.js +1 -1
- package/src/server/services/skill-service.js +300 -46
- package/src/server/services/speed-test.js +464 -186
- package/src/server/services/statistics-service.js +2 -2
- package/src/server/services/terminal-commands.js +10 -3
- package/src/server/services/terminal-config.js +1 -1
- package/src/server/services/ui-config.js +1 -1
- package/src/server/services/workspace-service.js +57 -100
- package/src/server/websocket-server.js +156 -8
- package/src/ui/menu.js +49 -40
- package/src/utils/port-helper.js +22 -8
- package/src/utils/session.js +5 -4
- package/dist/web/assets/icons-CO_2OFES.js +0 -1
- package/dist/web/assets/index-DI8QOi-E.js +0 -14
- package/dist/web/assets/index-uLHGdeZh.css +0 -41
- package/dist/web/assets/naive-ui-B1re3c-e.js +0 -1
- package/dist/web/assets/vue-vendor-6JaYHOiI.js +0 -44
- package/src/server/api/oauth.js +0 -294
- package/src/server/api/permissions.js +0 -385
- package/src/server/config/oauth-providers.js +0 -68
- package/src/server/services/oauth-callback-server.js +0 -284
- package/src/server/services/oauth-service.js +0 -378
- package/src/server/services/oauth-token-storage.js +0 -135
- package/src/server/services/permission-templates-service.js +0 -308
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
function normalizeAddress(address) {
|
|
2
|
+
if (!address) return '';
|
|
3
|
+
const trimmed = String(address).trim();
|
|
4
|
+
if (trimmed.startsWith('::ffff:')) {
|
|
5
|
+
return trimmed.slice(7);
|
|
6
|
+
}
|
|
7
|
+
return trimmed;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function isLoopbackAddress(address) {
|
|
11
|
+
const normalized = normalizeAddress(address).toLowerCase();
|
|
12
|
+
if (!normalized) return false;
|
|
13
|
+
if (normalized === '::1' || normalized === 'localhost') return true;
|
|
14
|
+
if (normalized === '127.0.0.1') return true;
|
|
15
|
+
if (normalized.startsWith('127.')) return true;
|
|
16
|
+
return false;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function isLoopbackRequest(req) {
|
|
20
|
+
if (!req) return false;
|
|
21
|
+
const socketAddress = req.socket && req.socket.remoteAddress;
|
|
22
|
+
if (!isLoopbackAddress(socketAddress)) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
const forwarded = req.headers && req.headers['x-forwarded-for'];
|
|
26
|
+
if (typeof forwarded === 'string' && forwarded.trim()) {
|
|
27
|
+
const first = forwarded.split(',')[0].trim();
|
|
28
|
+
return isLoopbackAddress(first);
|
|
29
|
+
}
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function createRemoteMutationGuard(options = {}) {
|
|
34
|
+
const enabled = options.enabled === true;
|
|
35
|
+
const allowRemoteMutation = options.allowRemoteMutation === true;
|
|
36
|
+
const message = options.message || 'LAN 模式下禁止远程写操作';
|
|
37
|
+
|
|
38
|
+
return (req, res, next) => {
|
|
39
|
+
if (!enabled || allowRemoteMutation) {
|
|
40
|
+
return next();
|
|
41
|
+
}
|
|
42
|
+
if (isLoopbackRequest(req)) {
|
|
43
|
+
return next();
|
|
44
|
+
}
|
|
45
|
+
if (req.method === 'GET' || req.method === 'HEAD' || req.method === 'OPTIONS') {
|
|
46
|
+
return next();
|
|
47
|
+
}
|
|
48
|
+
return res.status(403).json({
|
|
49
|
+
error: message,
|
|
50
|
+
code: 'LAN_REMOTE_WRITE_BLOCKED'
|
|
51
|
+
});
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function createRemoteRouteGuard(options = {}) {
|
|
56
|
+
const enabled = options.enabled === true;
|
|
57
|
+
const allowRemoteAccess = options.allowRemoteAccess === true;
|
|
58
|
+
const message = options.message || 'LAN 模式下禁止远程访问该接口';
|
|
59
|
+
|
|
60
|
+
return (req, res, next) => {
|
|
61
|
+
if (!enabled || allowRemoteAccess) {
|
|
62
|
+
return next();
|
|
63
|
+
}
|
|
64
|
+
if (isLoopbackRequest(req)) {
|
|
65
|
+
return next();
|
|
66
|
+
}
|
|
67
|
+
return res.status(403).json({
|
|
68
|
+
error: message,
|
|
69
|
+
code: 'LAN_REMOTE_ROUTE_BLOCKED'
|
|
70
|
+
});
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
module.exports = {
|
|
75
|
+
normalizeAddress,
|
|
76
|
+
isLoopbackAddress,
|
|
77
|
+
isLoopbackRequest,
|
|
78
|
+
createRemoteMutationGuard,
|
|
79
|
+
createRemoteRouteGuard
|
|
80
|
+
};
|
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
const crypto = require('crypto');
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* OpenCode 渠道管理服务
|
|
8
|
+
* 存储位置: ~/.cc-tool/opencode-channels.json
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
function normalizeGatewaySourceType(value) {
|
|
12
|
+
const normalized = String(value || '').trim().toLowerCase();
|
|
13
|
+
if (normalized === 'claude') return 'claude';
|
|
14
|
+
if (normalized === 'gemini') return 'gemini';
|
|
15
|
+
return 'codex';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 获取渠道存储文件路径
|
|
19
|
+
function getChannelsFilePath() {
|
|
20
|
+
const ccToolDir = path.join(os.homedir(), '.cc-tool');
|
|
21
|
+
if (!fs.existsSync(ccToolDir)) {
|
|
22
|
+
fs.mkdirSync(ccToolDir, { recursive: true });
|
|
23
|
+
}
|
|
24
|
+
return path.join(ccToolDir, 'opencode-channels.json');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// 读取所有渠道
|
|
28
|
+
function loadChannels() {
|
|
29
|
+
const filePath = getChannelsFilePath();
|
|
30
|
+
|
|
31
|
+
if (!fs.existsSync(filePath)) {
|
|
32
|
+
return { channels: [] };
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
37
|
+
const data = JSON.parse(content);
|
|
38
|
+
// 确保渠道有必要字段(兼容旧数据)
|
|
39
|
+
if (data.channels) {
|
|
40
|
+
data.channels = data.channels.map(ch => {
|
|
41
|
+
const normalized = {
|
|
42
|
+
...ch,
|
|
43
|
+
enabled: ch.enabled !== false,
|
|
44
|
+
weight: ch.weight || 1,
|
|
45
|
+
maxConcurrency: ch.maxConcurrency || null,
|
|
46
|
+
modelRedirects: ch.modelRedirects || [],
|
|
47
|
+
speedTestModel: ch.speedTestModel || null,
|
|
48
|
+
wireApi: ch.wireApi || 'openai', // OpenCode 默认使用 OpenAI 兼容格式
|
|
49
|
+
gatewaySourceType: normalizeGatewaySourceType(ch.gatewaySourceType),
|
|
50
|
+
allowedModels: ch.allowedModels || []
|
|
51
|
+
};
|
|
52
|
+
normalized.providerKey = deriveProviderKey(normalized);
|
|
53
|
+
return normalized;
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
return data;
|
|
57
|
+
} catch (err) {
|
|
58
|
+
console.error('[OpenCode Channels] Failed to parse channels file:', err);
|
|
59
|
+
return { channels: [] };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function deriveProviderKey(channel) {
|
|
64
|
+
const base = channel.wireApi || channel.providerKey || 'opencode';
|
|
65
|
+
if (typeof base === 'string' && base.startsWith('opencode_')) {
|
|
66
|
+
return base;
|
|
67
|
+
}
|
|
68
|
+
return `opencode_${base}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 保存渠道数据
|
|
72
|
+
function saveChannels(data) {
|
|
73
|
+
const filePath = getChannelsFilePath();
|
|
74
|
+
fs.writeFileSync(filePath, JSON.stringify(data, null, 2), 'utf8');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 获取所有渠道
|
|
78
|
+
function getChannels() {
|
|
79
|
+
const data = loadChannels();
|
|
80
|
+
return {
|
|
81
|
+
channels: data.channels || []
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// 添加渠道
|
|
86
|
+
function createChannel(name, baseUrl, apiKey, extraConfig = {}) {
|
|
87
|
+
const data = loadChannels();
|
|
88
|
+
|
|
89
|
+
const newChannel = {
|
|
90
|
+
id: crypto.randomUUID(),
|
|
91
|
+
name,
|
|
92
|
+
baseUrl,
|
|
93
|
+
apiKey,
|
|
94
|
+
wireApi: extraConfig.wireApi || 'openai',
|
|
95
|
+
enabled: extraConfig.enabled !== false,
|
|
96
|
+
weight: extraConfig.weight || 1,
|
|
97
|
+
maxConcurrency: extraConfig.maxConcurrency || null,
|
|
98
|
+
modelRedirects: extraConfig.modelRedirects || [],
|
|
99
|
+
speedTestModel: extraConfig.speedTestModel || null,
|
|
100
|
+
model: extraConfig.model || null,
|
|
101
|
+
gatewaySourceType: normalizeGatewaySourceType(extraConfig.gatewaySourceType),
|
|
102
|
+
providerKey: extraConfig.providerKey || null,
|
|
103
|
+
presetId: extraConfig.presetId || null,
|
|
104
|
+
websiteUrl: extraConfig.websiteUrl || '',
|
|
105
|
+
allowedModels: extraConfig.allowedModels || [],
|
|
106
|
+
createdAt: Date.now(),
|
|
107
|
+
updatedAt: Date.now()
|
|
108
|
+
};
|
|
109
|
+
newChannel.providerKey = extraConfig.providerKey || deriveProviderKey(newChannel);
|
|
110
|
+
|
|
111
|
+
data.channels.push(newChannel);
|
|
112
|
+
saveChannels(data);
|
|
113
|
+
|
|
114
|
+
return newChannel;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// 更新渠道
|
|
118
|
+
function updateChannel(channelId, updates) {
|
|
119
|
+
const data = loadChannels();
|
|
120
|
+
const index = data.channels.findIndex(c => c.id === channelId);
|
|
121
|
+
|
|
122
|
+
if (index === -1) {
|
|
123
|
+
throw new Error('Channel not found');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const oldChannel = data.channels[index];
|
|
127
|
+
|
|
128
|
+
const merged = {
|
|
129
|
+
...oldChannel,
|
|
130
|
+
...updates,
|
|
131
|
+
id: channelId,
|
|
132
|
+
createdAt: oldChannel.createdAt,
|
|
133
|
+
modelRedirects: updates.modelRedirects !== undefined ? updates.modelRedirects : (oldChannel.modelRedirects || []),
|
|
134
|
+
speedTestModel: updates.speedTestModel !== undefined ? updates.speedTestModel : (oldChannel.speedTestModel || null),
|
|
135
|
+
gatewaySourceType: normalizeGatewaySourceType(
|
|
136
|
+
updates.gatewaySourceType !== undefined
|
|
137
|
+
? updates.gatewaySourceType
|
|
138
|
+
: oldChannel.gatewaySourceType
|
|
139
|
+
),
|
|
140
|
+
updatedAt: Date.now()
|
|
141
|
+
};
|
|
142
|
+
merged.providerKey = updates.providerKey || deriveProviderKey(merged);
|
|
143
|
+
data.channels[index] = merged;
|
|
144
|
+
|
|
145
|
+
saveChannels(data);
|
|
146
|
+
return data.channels[index];
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// 删除渠道
|
|
150
|
+
async function deleteChannel(channelId) {
|
|
151
|
+
const data = loadChannels();
|
|
152
|
+
const index = data.channels.findIndex(c => c.id === channelId);
|
|
153
|
+
|
|
154
|
+
if (index === -1) {
|
|
155
|
+
throw new Error('Channel not found');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
data.channels.splice(index, 1);
|
|
159
|
+
saveChannels(data);
|
|
160
|
+
|
|
161
|
+
return { success: true };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// 获取所有启用的渠道
|
|
165
|
+
function getEnabledChannels() {
|
|
166
|
+
const data = loadChannels();
|
|
167
|
+
return data.channels.filter(c => c.enabled !== false);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// 保存渠道顺序
|
|
171
|
+
function saveChannelOrder(order) {
|
|
172
|
+
const data = loadChannels();
|
|
173
|
+
|
|
174
|
+
const orderedChannels = [];
|
|
175
|
+
for (const id of order) {
|
|
176
|
+
const channel = data.channels.find(c => c.id === id);
|
|
177
|
+
if (channel) {
|
|
178
|
+
orderedChannels.push(channel);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// 添加不在顺序中的渠道
|
|
183
|
+
for (const channel of data.channels) {
|
|
184
|
+
if (!orderedChannels.find(c => c.id === channel.id)) {
|
|
185
|
+
orderedChannels.push(channel);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
data.channels = orderedChannels;
|
|
190
|
+
saveChannels(data);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* 获取渠道的有效 API Key
|
|
195
|
+
*/
|
|
196
|
+
async function getEffectiveApiKey(channel) {
|
|
197
|
+
return channel.apiKey || null;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = {
|
|
201
|
+
getChannels,
|
|
202
|
+
createChannel,
|
|
203
|
+
updateChannel,
|
|
204
|
+
deleteChannel,
|
|
205
|
+
getEnabledChannels,
|
|
206
|
+
saveChannelOrder,
|
|
207
|
+
getEffectiveApiKey
|
|
208
|
+
};
|