coding-tool-x 3.3.6 → 3.3.8
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 +14 -0
- package/dist/web/assets/{Analytics-TtaduRqL.js → Analytics-DLpoDZ2M.js} +1 -1
- package/dist/web/assets/{ConfigTemplates-BP2lLBMN.js → ConfigTemplates-D_hRb55W.js} +1 -1
- package/dist/web/assets/Home-BMoFdAwy.css +1 -0
- package/dist/web/assets/Home-DNwp-0J-.js +1 -0
- package/dist/web/assets/{PluginManager-HmISlyMK.js → PluginManager-JXsyym1s.js} +1 -1
- package/dist/web/assets/{ProjectList-DoN8Hjbu.js → ProjectList-DZWSeb-q.js} +1 -1
- package/dist/web/assets/{SessionList-Da8BYzNi.js → SessionList-Cs624DR3.js} +1 -1
- package/dist/web/assets/{SkillManager-DqLAXh9o.js → SkillManager-bEliz7qz.js} +1 -1
- package/dist/web/assets/{WorkspaceManager-B_TxOgPW.js → WorkspaceManager-J3RecFGn.js} +1 -1
- package/dist/web/assets/{icons-B29onFfZ.js → icons-Cuc23WS7.js} +1 -1
- package/dist/web/assets/index-BXeSvAwU.js +2 -0
- package/dist/web/assets/index-DWAC3Tdv.css +1 -0
- package/dist/web/index.html +3 -3
- package/package.json +3 -2
- package/src/commands/daemon.js +44 -6
- package/src/commands/toggle-proxy.js +100 -5
- package/src/config/default.js +1 -1
- package/src/config/model-metadata.js +2 -2
- package/src/config/model-metadata.json +7 -2
- package/src/config/paths.js +102 -19
- package/src/server/api/channels.js +9 -0
- package/src/server/api/codex-channels.js +9 -0
- package/src/server/api/codex-proxy.js +22 -11
- package/src/server/api/gemini-proxy.js +22 -11
- package/src/server/api/mcp.js +26 -4
- package/src/server/api/oauth-credentials.js +163 -0
- package/src/server/api/opencode-proxy.js +22 -10
- package/src/server/api/plugins.js +3 -1
- package/src/server/api/proxy.js +39 -44
- package/src/server/api/skills.js +91 -13
- package/src/server/codex-proxy-server.js +1 -11
- package/src/server/index.js +26 -2
- package/src/server/services/channels.js +18 -22
- package/src/server/services/codex-channels.js +124 -175
- package/src/server/services/codex-config.js +2 -5
- package/src/server/services/codex-settings-manager.js +12 -348
- package/src/server/services/config-export-service.js +572 -117
- package/src/server/services/gemini-channels.js +11 -9
- package/src/server/services/mcp-client.js +70 -13
- package/src/server/services/mcp-service.js +74 -29
- package/src/server/services/model-detector.js +1 -0
- package/src/server/services/native-keychain.js +243 -0
- package/src/server/services/native-oauth-adapters.js +890 -0
- package/src/server/services/oauth-credentials-service.js +786 -0
- package/src/server/services/oauth-utils.js +49 -0
- package/src/server/services/opencode-channels.js +13 -9
- package/src/server/services/opencode-settings-manager.js +169 -16
- package/src/server/services/plugins-service.js +22 -1
- package/src/server/services/settings-manager.js +13 -0
- package/src/server/services/skill-service.js +712 -332
- package/src/utils/port-helper.js +87 -2
- package/dist/web/assets/Home-BsSioaaB.css +0 -1
- package/dist/web/assets/Home-CbbyopS-.js +0 -1
- package/dist/web/assets/index-By3mDEvx.js +0 -2
- package/dist/web/assets/index-CsWInMQV.css +0 -1
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const crypto = require('crypto');
|
|
3
|
+
|
|
4
|
+
function maskToken(value) {
|
|
5
|
+
const token = String(value || '').trim();
|
|
6
|
+
if (!token) {
|
|
7
|
+
return '';
|
|
8
|
+
}
|
|
9
|
+
if (token.length <= 8) {
|
|
10
|
+
return `${token.slice(0, 2)}***`;
|
|
11
|
+
}
|
|
12
|
+
return `${token.slice(0, 4)}...${token.slice(-4)}`;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function decodeJwtPayload(token) {
|
|
16
|
+
if (typeof token !== 'string' || !token.includes('.')) {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
try {
|
|
21
|
+
const payload = token.split('.')[1];
|
|
22
|
+
const normalized = payload.replace(/-/g, '+').replace(/_/g, '/');
|
|
23
|
+
const padded = normalized.padEnd(Math.ceil(normalized.length / 4) * 4, '=');
|
|
24
|
+
return JSON.parse(Buffer.from(padded, 'base64').toString('utf8'));
|
|
25
|
+
} catch {
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function removeFileIfExists(filePath) {
|
|
31
|
+
try {
|
|
32
|
+
if (filePath && fs.existsSync(filePath)) {
|
|
33
|
+
fs.unlinkSync(filePath);
|
|
34
|
+
}
|
|
35
|
+
} catch {
|
|
36
|
+
// ignore cleanup failures
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function sha256(value) {
|
|
41
|
+
return crypto.createHash('sha256').update(String(value || ''), 'utf8').digest('hex');
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
maskToken,
|
|
46
|
+
decodeJwtPayload,
|
|
47
|
+
removeFileIfExists,
|
|
48
|
+
sha256
|
|
49
|
+
};
|
|
@@ -2,6 +2,8 @@ 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
|
+
const { setChannelConfig } = require('./opencode-settings-manager');
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
* OpenCode 渠道管理服务
|
|
@@ -189,14 +191,6 @@ function updateChannel(channelId, updates) {
|
|
|
189
191
|
console.log(`[OpenCode Single-channel mode] Enabled "${merged.name}", disabled all others`);
|
|
190
192
|
}
|
|
191
193
|
|
|
192
|
-
// Prevent disabling last enabled channel when proxy is OFF
|
|
193
|
-
if (!isProxyRunning && !merged.enabled && oldChannel.enabled) {
|
|
194
|
-
const enabledCount = data.channels.filter(ch => ch.enabled).length;
|
|
195
|
-
if (enabledCount === 0) {
|
|
196
|
-
throw new Error('无法禁用最后一个启用的渠道。请先启用其他渠道或启动动态切换。');
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
194
|
saveChannels(data);
|
|
201
195
|
return data.channels[index];
|
|
202
196
|
}
|
|
@@ -259,6 +253,9 @@ function applyChannelToSettings(channelId) {
|
|
|
259
253
|
});
|
|
260
254
|
saveChannels(data);
|
|
261
255
|
|
|
256
|
+
clearNativeOAuth('opencode');
|
|
257
|
+
setChannelConfig(channel);
|
|
258
|
+
|
|
262
259
|
return channel;
|
|
263
260
|
}
|
|
264
261
|
|
|
@@ -371,6 +368,12 @@ async function getEffectiveApiKey(channel) {
|
|
|
371
368
|
return candidates[0] || null;
|
|
372
369
|
}
|
|
373
370
|
|
|
371
|
+
function disableAllChannels() {
|
|
372
|
+
const data = loadChannels();
|
|
373
|
+
data.channels.forEach(ch => { ch.enabled = false; });
|
|
374
|
+
saveChannels(data);
|
|
375
|
+
}
|
|
376
|
+
|
|
374
377
|
module.exports = {
|
|
375
378
|
getChannels,
|
|
376
379
|
createChannel,
|
|
@@ -380,5 +383,6 @@ module.exports = {
|
|
|
380
383
|
saveChannelOrder,
|
|
381
384
|
applyChannelToSettings,
|
|
382
385
|
getEffectiveApiKey,
|
|
383
|
-
getEffectiveApiKeyCandidates
|
|
386
|
+
getEffectiveApiKeyCandidates,
|
|
387
|
+
disableAllChannels
|
|
384
388
|
};
|
|
@@ -169,6 +169,11 @@ function isManagedProxyProvider(provider) {
|
|
|
169
169
|
return apiKey === PROXY_API_KEY && isLocalProxyBaseUrl(baseUrl);
|
|
170
170
|
}
|
|
171
171
|
|
|
172
|
+
function isManagedChannelProvider(provider) {
|
|
173
|
+
if (!provider || typeof provider !== 'object') return false;
|
|
174
|
+
return provider?.[MANAGED_PROVIDER_MARKER] === true;
|
|
175
|
+
}
|
|
176
|
+
|
|
172
177
|
function isManagedProxyConfig(config) {
|
|
173
178
|
if (!config || typeof config !== 'object') return false;
|
|
174
179
|
// Check legacy single-provider format
|
|
@@ -244,6 +249,92 @@ function resolveProxyBaseUrl(config) {
|
|
|
244
249
|
return '';
|
|
245
250
|
}
|
|
246
251
|
|
|
252
|
+
function collectChannelModelCandidates(channel = {}) {
|
|
253
|
+
const seen = new Set();
|
|
254
|
+
const models = [];
|
|
255
|
+
const add = (value) => {
|
|
256
|
+
if (typeof value !== 'string') return;
|
|
257
|
+
const trimmed = value.trim();
|
|
258
|
+
if (!trimmed) return;
|
|
259
|
+
const key = trimmed.toLowerCase();
|
|
260
|
+
if (seen.has(key)) return;
|
|
261
|
+
seen.add(key);
|
|
262
|
+
models.push(trimmed);
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
add(channel.model);
|
|
266
|
+
add(channel.speedTestModel);
|
|
267
|
+
|
|
268
|
+
if (Array.isArray(channel.allowedModels)) {
|
|
269
|
+
channel.allowedModels.forEach(add);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (channel.modelConfig && typeof channel.modelConfig === 'object') {
|
|
273
|
+
add(channel.modelConfig.model);
|
|
274
|
+
add(channel.modelConfig.opusModel);
|
|
275
|
+
add(channel.modelConfig.sonnetModel);
|
|
276
|
+
add(channel.modelConfig.haikuModel);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
if (Array.isArray(channel.modelRedirects)) {
|
|
280
|
+
channel.modelRedirects.forEach((rule) => {
|
|
281
|
+
add(rule?.from);
|
|
282
|
+
add(rule?.to);
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
return models;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
function clearManagedProviders(config) {
|
|
290
|
+
const removedProviderKeys = [];
|
|
291
|
+
|
|
292
|
+
if (!config?.provider || typeof config.provider !== 'object') {
|
|
293
|
+
return removedProviderKeys;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (isLegacyProxyProvider(config.provider[LEGACY_PROVIDER_ID])) {
|
|
297
|
+
delete config.provider[LEGACY_PROVIDER_ID];
|
|
298
|
+
removedProviderKeys.push(LEGACY_PROVIDER_ID);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (isManagedProxyProvider(config.provider[PROXY_PROVIDER_ID])) {
|
|
302
|
+
delete config.provider[PROXY_PROVIDER_ID];
|
|
303
|
+
removedProviderKeys.push(PROXY_PROVIDER_ID);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
Object.keys(config.provider).forEach((key) => {
|
|
307
|
+
const provider = config.provider[key];
|
|
308
|
+
if (isManagedProxyProvider(provider) || isManagedChannelProvider(provider)) {
|
|
309
|
+
delete config.provider[key];
|
|
310
|
+
removedProviderKeys.push(key);
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
if (Object.keys(config.provider).length === 0) {
|
|
315
|
+
delete config.provider;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
return removedProviderKeys;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function clearManagedModelRef(config, removedProviderKeys = []) {
|
|
322
|
+
const modelRef = String(config?.model || '').trim();
|
|
323
|
+
if (!modelRef) {
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (isOldManagedModelRef(modelRef)) {
|
|
328
|
+
delete config.model;
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const providerId = modelRef.includes('/') ? modelRef.split('/')[0] : '';
|
|
333
|
+
if (providerId && removedProviderKeys.includes(providerId)) {
|
|
334
|
+
delete config.model;
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
|
|
247
338
|
function backupConfig(filePath) {
|
|
248
339
|
ensureConfigDir();
|
|
249
340
|
const backupPath = getBackupPath(filePath);
|
|
@@ -324,23 +415,13 @@ function setProxyConfig(proxyPort, options = {}) {
|
|
|
324
415
|
if (!next.provider || typeof next.provider !== 'object') {
|
|
325
416
|
next.provider = {};
|
|
326
417
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
delete next.provider[LEGACY_PROVIDER_ID];
|
|
330
|
-
}
|
|
331
|
-
if (Object.prototype.hasOwnProperty.call(next.provider[LEGACY_PROVIDER_ID] || {}, 'model')) {
|
|
332
|
-
delete next.provider[LEGACY_PROVIDER_ID].model;
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// Remove old single ctx-proxy provider (superseded by per-channel providers)
|
|
336
|
-
delete next.provider[PROXY_PROVIDER_ID];
|
|
418
|
+
const removedProviderKeys = clearManagedProviders(next);
|
|
419
|
+
clearManagedModelRef(next, removedProviderKeys);
|
|
337
420
|
|
|
338
|
-
//
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
}
|
|
343
|
-
});
|
|
421
|
+
// clearManagedProviders 可能在清空所有 provider 后 delete next.provider,重新确保存在
|
|
422
|
+
if (!next.provider || typeof next.provider !== 'object') {
|
|
423
|
+
next.provider = {};
|
|
424
|
+
}
|
|
344
425
|
|
|
345
426
|
const channels = Array.isArray(options.channels) ? options.channels : null;
|
|
346
427
|
|
|
@@ -427,6 +508,73 @@ function setProxyConfig(proxyPort, options = {}) {
|
|
|
427
508
|
return { success: true, port: proxyPort, path: filePath };
|
|
428
509
|
}
|
|
429
510
|
|
|
511
|
+
function setChannelConfig(channel = {}) {
|
|
512
|
+
const filePath = selectConfigPath();
|
|
513
|
+
backupConfig(filePath);
|
|
514
|
+
|
|
515
|
+
const current = readConfig(filePath);
|
|
516
|
+
const next = (current && typeof current === 'object') ? current : {};
|
|
517
|
+
|
|
518
|
+
if (!next.provider || typeof next.provider !== 'object') {
|
|
519
|
+
next.provider = {};
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
const removedProviderKeys = clearManagedProviders(next);
|
|
523
|
+
clearManagedModelRef(next, removedProviderKeys);
|
|
524
|
+
|
|
525
|
+
const baseProviderKey = sanitizeProviderKey(channel.providerKey || channel.name || 'ctx-channel');
|
|
526
|
+
let providerKey = baseProviderKey;
|
|
527
|
+
let suffix = 2;
|
|
528
|
+
while (next.provider[providerKey] && !isManagedChannelProvider(next.provider[providerKey])) {
|
|
529
|
+
providerKey = `${baseProviderKey}-${suffix}`;
|
|
530
|
+
suffix += 1;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
const modelCandidates = collectChannelModelCandidates(channel);
|
|
534
|
+
const fallbackModel = String(channel.model || channel.speedTestModel || modelCandidates[0] || '').trim();
|
|
535
|
+
const modelsMap = buildModelsMap(modelCandidates, fallbackModel);
|
|
536
|
+
const modelIds = Object.keys(modelsMap);
|
|
537
|
+
|
|
538
|
+
if (modelIds.length === 0) {
|
|
539
|
+
throw new Error('OpenCode 渠道缺少可写入的模型,请至少配置默认模型或可用模型。');
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
next.provider[providerKey] = {
|
|
543
|
+
[MANAGED_PROVIDER_MARKER]: true,
|
|
544
|
+
npm: '@ai-sdk/openai-compatible',
|
|
545
|
+
name: channel.name || providerKey,
|
|
546
|
+
options: {
|
|
547
|
+
baseURL: String(channel.baseUrl || '').trim(),
|
|
548
|
+
apiKey: String(channel.apiKey || '').trim()
|
|
549
|
+
},
|
|
550
|
+
models: modelsMap
|
|
551
|
+
};
|
|
552
|
+
|
|
553
|
+
const topModel = normalizeOpenCodeModel(fallbackModel || modelIds[0], providerKey);
|
|
554
|
+
if (topModel) {
|
|
555
|
+
next.model = topModel;
|
|
556
|
+
} else {
|
|
557
|
+
clearManagedModelRef(next, [providerKey]);
|
|
558
|
+
}
|
|
559
|
+
|
|
560
|
+
writeConfig(filePath, next);
|
|
561
|
+
return { success: true, path: filePath, providerKey, model: next.model || null };
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
function clearManagedChannelConfig() {
|
|
565
|
+
const filePath = selectConfigPath();
|
|
566
|
+
if (!fs.existsSync(filePath)) {
|
|
567
|
+
return { success: true, path: filePath };
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
const current = readConfig(filePath);
|
|
571
|
+
const next = (current && typeof current === 'object') ? current : {};
|
|
572
|
+
const removedProviderKeys = clearManagedProviders(next);
|
|
573
|
+
clearManagedModelRef(next, removedProviderKeys);
|
|
574
|
+
writeConfig(filePath, next);
|
|
575
|
+
return { success: true, path: filePath };
|
|
576
|
+
}
|
|
577
|
+
|
|
430
578
|
function isOldManagedModelRef(modelRef) {
|
|
431
579
|
const s = String(modelRef || '');
|
|
432
580
|
return s.startsWith(`${PROXY_PROVIDER_ID}/`) || s.startsWith(`${LEGACY_PROVIDER_ID}/`);
|
|
@@ -485,7 +633,12 @@ function getCurrentProxyPort() {
|
|
|
485
633
|
module.exports = {
|
|
486
634
|
configExists,
|
|
487
635
|
hasBackup,
|
|
636
|
+
readConfig,
|
|
637
|
+
writeConfig,
|
|
638
|
+
selectConfigPath,
|
|
488
639
|
setProxyConfig,
|
|
640
|
+
setChannelConfig,
|
|
641
|
+
clearManagedChannelConfig,
|
|
489
642
|
restoreSettings,
|
|
490
643
|
deleteBackup,
|
|
491
644
|
isProxyConfig,
|
|
@@ -109,6 +109,9 @@ class PluginsService {
|
|
|
109
109
|
this.ccToolConfigDir = path.join(HOME_DIR, '.cc-tool');
|
|
110
110
|
this.opencodePluginsDir = path.join(OPENCODE_CONFIG_DIR, 'plugins');
|
|
111
111
|
this.opencodeLegacyPluginsDir = path.join(OPENCODE_CONFIG_DIR, 'plugin');
|
|
112
|
+
const prefix = this.platform === 'opencode' ? 'opencode-' : '';
|
|
113
|
+
this.marketCachePath = path.join(this.ccToolConfigDir, `${prefix}plugins-market-cache.json`);
|
|
114
|
+
this._marketCache = null;
|
|
112
115
|
}
|
|
113
116
|
|
|
114
117
|
_ensureDir(dirPath) {
|
|
@@ -1153,7 +1156,20 @@ class PluginsService {
|
|
|
1153
1156
|
* Get market plugins from configured repositories
|
|
1154
1157
|
* @returns {Promise<Array>} List of available market plugins
|
|
1155
1158
|
*/
|
|
1156
|
-
async getMarketPlugins() {
|
|
1159
|
+
async getMarketPlugins(forceRefresh = false) {
|
|
1160
|
+
if (!forceRefresh) {
|
|
1161
|
+
if (this._marketCache) return this._marketCache;
|
|
1162
|
+
try {
|
|
1163
|
+
if (fs.existsSync(this.marketCachePath)) {
|
|
1164
|
+
const data = JSON.parse(fs.readFileSync(this.marketCachePath, 'utf-8'));
|
|
1165
|
+
if (Array.isArray(data.plugins)) {
|
|
1166
|
+
this._marketCache = data.plugins;
|
|
1167
|
+
return this._marketCache;
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
} catch (err) { /* ignore */ }
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1157
1173
|
const repos = this.getRepos().filter(r => r.enabled);
|
|
1158
1174
|
const marketPlugins = [];
|
|
1159
1175
|
|
|
@@ -1260,6 +1276,11 @@ class PluginsService {
|
|
|
1260
1276
|
plugin.isInstalled = installedNames.has(plugin.name);
|
|
1261
1277
|
});
|
|
1262
1278
|
|
|
1279
|
+
this._marketCache = marketPlugins;
|
|
1280
|
+
try {
|
|
1281
|
+
fs.writeFileSync(this.marketCachePath, JSON.stringify({ plugins: marketPlugins }), 'utf-8');
|
|
1282
|
+
} catch (err) { /* ignore */ }
|
|
1283
|
+
|
|
1263
1284
|
return marketPlugins;
|
|
1264
1285
|
}
|
|
1265
1286
|
}
|
|
@@ -84,6 +84,18 @@ function restoreSettings() {
|
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function deleteBackup() {
|
|
88
|
+
try {
|
|
89
|
+
if (fs.existsSync(getBackupPath())) {
|
|
90
|
+
fs.unlinkSync(getBackupPath());
|
|
91
|
+
}
|
|
92
|
+
return { success: true };
|
|
93
|
+
} catch (err) {
|
|
94
|
+
console.warn('Failed to delete Claude backup file:', err.message);
|
|
95
|
+
return { success: false, error: err.message };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
87
99
|
// 设置代理配置
|
|
88
100
|
function setProxyConfig(proxyPort) {
|
|
89
101
|
try {
|
|
@@ -156,6 +168,7 @@ module.exports = {
|
|
|
156
168
|
writeSettings,
|
|
157
169
|
backupSettings,
|
|
158
170
|
restoreSettings,
|
|
171
|
+
deleteBackup,
|
|
159
172
|
setProxyConfig,
|
|
160
173
|
isProxyConfig,
|
|
161
174
|
getCurrentProxyPort
|