whistle.easy-proxy 1.1.0 → 1.2.0
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/README.md +1 -0
- package/lib/cert-check.js +6 -5
- package/lib/cli/commands/index.js +2 -0
- package/lib/cli/commands/mirror.js +101 -0
- package/lib/cli/commands/proxy.js +50 -83
- package/lib/cli/commands/start.js +11 -1
- package/lib/cli/mirror-rulesets.js +58 -0
- package/lib/cli/mirror-sync.js +48 -0
- package/lib/cli/proxy-config.js +0 -5
- package/lib/config.js +2 -0
- package/package.json +8 -6
package/README.md
CHANGED
|
@@ -61,6 +61,7 @@ open http://local.whistlejs.com/plugin.easy-proxy/
|
|
|
61
61
|
| `easy-proxy accel dns` | 刷新 DNS 缓存 |
|
|
62
62
|
| `easy-proxy host add <域名> <IP>` | 添加手动 IP 覆盖 |
|
|
63
63
|
| `easy-proxy health` | 综合健康检查 |
|
|
64
|
+
| `easy-proxy mirror` | 开发镜像源加速(npm/pip/go,302 重定向到国内镜像) |
|
|
64
65
|
| `easy-proxy status` | 查看整体状态 |
|
|
65
66
|
|
|
66
67
|
**完整命令参考** → [docs/cli.md](docs/cli.md)
|
package/lib/cert-check.js
CHANGED
|
@@ -126,10 +126,8 @@ function fixCertCompliance(certPath) {
|
|
|
126
126
|
]);
|
|
127
127
|
cert.sign(key, forge.md.sha256.create());
|
|
128
128
|
fs.writeFileSync(certPath, forge.pki.certificateToPem(cert));
|
|
129
|
-
//
|
|
130
|
-
|
|
131
|
-
fs.unlinkSync(MERGED_CA_PATH);
|
|
132
|
-
} catch (_e) {}
|
|
129
|
+
// 不删合并 CA 包:重签后 root.crt 的 mtime 更新,ensureMergedCaBundle 的
|
|
130
|
+
// staleness 检查会自动重建(直接 unlink 会让环境变量悬空指向不存在的文件)
|
|
133
131
|
return true;
|
|
134
132
|
} catch (_e) {
|
|
135
133
|
return false;
|
|
@@ -218,7 +216,10 @@ function ensureMergedCaBundle(certPath) {
|
|
|
218
216
|
// whistle 生成的 root.crt 是 CRLF 行尾,Node/NODE_EXTRA_CA_CERTS 加载器会拒收混入的 CRLF 证书
|
|
219
217
|
var whistlePem = fs.readFileSync(certPath, 'utf-8').replace(/\r/g, '');
|
|
220
218
|
fs.mkdirSync(path.dirname(merged), { recursive: true });
|
|
221
|
-
|
|
219
|
+
// 原子写:先写临时文件再 rename,避免写一半崩溃留下坏文件
|
|
220
|
+
var tmp = merged + '.tmp-' + process.pid;
|
|
221
|
+
fs.writeFileSync(tmp, base + '\n' + whistlePem);
|
|
222
|
+
fs.renameSync(tmp, merged);
|
|
222
223
|
return merged;
|
|
223
224
|
} catch (_e) {
|
|
224
225
|
return null;
|
|
@@ -8,6 +8,7 @@ const { registerStatus } = require('./status');
|
|
|
8
8
|
const { registerConfig } = require('./config-cmd');
|
|
9
9
|
const { registerIntercept } = require('./intercept');
|
|
10
10
|
const { registerNetwork } = require('./network');
|
|
11
|
+
const { registerMirror } = require('./mirror');
|
|
11
12
|
|
|
12
13
|
function registerAllCommands(program) {
|
|
13
14
|
registerStart(program);
|
|
@@ -22,6 +23,7 @@ function registerAllCommands(program) {
|
|
|
22
23
|
registerConfig(program);
|
|
23
24
|
registerIntercept(program);
|
|
24
25
|
registerNetwork(program);
|
|
26
|
+
registerMirror(program);
|
|
25
27
|
}
|
|
26
28
|
|
|
27
29
|
module.exports = {
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
const { readConfig, writeConfig, c, info, ok, err, title, sep } = require('../helpers');
|
|
2
|
+
|
|
3
|
+
// 镜像开关状态存在主配置 config.json 的 mirrors 字段: { npm: true, pip: false, ... }
|
|
4
|
+
function loadMirrorState() {
|
|
5
|
+
const cfg = readConfig();
|
|
6
|
+
return cfg.mirrors || {};
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
function saveMirrorState(state) {
|
|
10
|
+
const cfg = readConfig();
|
|
11
|
+
cfg.mirrors = state;
|
|
12
|
+
writeConfig(cfg);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function registerMirror(program) {
|
|
16
|
+
const mirrorCmd = program.command('mirror').description('开发镜像源加速(npm/pip/go 等,302 重定向到国内镜像)');
|
|
17
|
+
|
|
18
|
+
mirrorCmd
|
|
19
|
+
.command('status')
|
|
20
|
+
.description('查看镜像源状态')
|
|
21
|
+
.action(() => {
|
|
22
|
+
const { MIRROR_RULESETS } = require('../mirror-rulesets');
|
|
23
|
+
const state = loadMirrorState();
|
|
24
|
+
title('镜像源加速');
|
|
25
|
+
sep();
|
|
26
|
+
for (const name of Object.keys(MIRROR_RULESETS)) {
|
|
27
|
+
const on = state[name] === true;
|
|
28
|
+
info(`${on ? `${c.green}✓ 开启` : `${c.red}✗ 关闭`}${c.reset} ${name}`);
|
|
29
|
+
}
|
|
30
|
+
sep();
|
|
31
|
+
info('开关立即同步到 whistle(需 whistle 运行中)');
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
mirrorCmd
|
|
35
|
+
.command('list')
|
|
36
|
+
.description('查看镜像规则内容')
|
|
37
|
+
.action(() => {
|
|
38
|
+
const { MIRROR_RULESETS } = require('../mirror-rulesets');
|
|
39
|
+
title('内置镜像规则');
|
|
40
|
+
sep();
|
|
41
|
+
for (const [name, rule] of Object.entries(MIRROR_RULESETS)) {
|
|
42
|
+
info(`${c.bold}${name}${c.reset}`);
|
|
43
|
+
rule
|
|
44
|
+
.split('\n')
|
|
45
|
+
.filter((l) => !l.startsWith('#'))
|
|
46
|
+
.forEach((l) => info(` ${l}`));
|
|
47
|
+
}
|
|
48
|
+
sep();
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
mirrorCmd
|
|
52
|
+
.command('on [name]')
|
|
53
|
+
.description('启用镜像(无参数=全部启用)')
|
|
54
|
+
.action(async (name) => {
|
|
55
|
+
const { MIRROR_RULESETS } = require('../mirror-rulesets');
|
|
56
|
+
if (name && !MIRROR_RULESETS[name]) {
|
|
57
|
+
err(`未知镜像: ${name}(可用: ${Object.keys(MIRROR_RULESETS).join(', ')})`);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const state = loadMirrorState();
|
|
61
|
+
if (name) state[name] = true;
|
|
62
|
+
else for (const k of Object.keys(MIRROR_RULESETS)) state[k] = true;
|
|
63
|
+
saveMirrorState(state);
|
|
64
|
+
|
|
65
|
+
const { syncMirrors } = require('../mirror-sync');
|
|
66
|
+
try {
|
|
67
|
+
await syncMirrors();
|
|
68
|
+
ok(name ? `镜像 ${name} 已开启` : '所有镜像已开启');
|
|
69
|
+
} catch (e) {
|
|
70
|
+
info('whistle 未运行,配置已保存,下次 start 自动生效');
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
mirrorCmd
|
|
75
|
+
.command('off [name]')
|
|
76
|
+
.description('关闭镜像(无参数=全部关闭)')
|
|
77
|
+
.action(async (name) => {
|
|
78
|
+
const { MIRROR_RULESETS } = require('../mirror-rulesets');
|
|
79
|
+
if (name && !MIRROR_RULESETS[name]) {
|
|
80
|
+
err(`未知镜像: ${name}(可用: ${Object.keys(MIRROR_RULESETS).join(', ')})`);
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
const state = loadMirrorState();
|
|
84
|
+
if (name) delete state[name];
|
|
85
|
+
else for (const k of Object.keys(MIRROR_RULESETS)) delete state[k];
|
|
86
|
+
saveMirrorState(state);
|
|
87
|
+
|
|
88
|
+
const { syncMirrors } = require('../mirror-sync');
|
|
89
|
+
try {
|
|
90
|
+
await syncMirrors();
|
|
91
|
+
ok(name ? `镜像 ${name} 已关闭` : '所有镜像已关闭');
|
|
92
|
+
} catch (_e) {
|
|
93
|
+
info('whistle 未运行,配置已保存,下次 start 自动生效');
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
module.exports = {
|
|
99
|
+
registerMirror,
|
|
100
|
+
loadMirrorState,
|
|
101
|
+
};
|
|
@@ -194,98 +194,65 @@ function registerProxy(program) {
|
|
|
194
194
|
});
|
|
195
195
|
|
|
196
196
|
// ---- proxy bypass ----
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
.
|
|
201
|
-
.description('查看当前跳过列表')
|
|
197
|
+
// 一行一个域名,# 注释;全删 = 恢复默认列表
|
|
198
|
+
const bypassCmd = proxyCmd
|
|
199
|
+
.command('bypass')
|
|
200
|
+
.description('编辑跳过代理的域名列表(打开 $EDITOR,一行一个域名)')
|
|
202
201
|
.action(() => {
|
|
203
202
|
const proxyConfig = require('../proxy-config');
|
|
204
|
-
const
|
|
205
|
-
const domains = config.bypassDomains || proxyConfig.getDefaultBypass();
|
|
206
|
-
title('跳过代理的域名');
|
|
207
|
-
sep();
|
|
208
|
-
if (domains.length === 0) {
|
|
209
|
-
info('(空)');
|
|
210
|
-
} else {
|
|
211
|
-
domains.forEach((d) => {
|
|
212
|
-
info(` ${d}`);
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
if (!config.bypassDomains) {
|
|
216
|
-
info(`${c.gray}(使用默认列表)${c.reset}`);
|
|
217
|
-
}
|
|
218
|
-
sep();
|
|
219
|
-
});
|
|
203
|
+
const oldList = proxyConfig.getEffectiveBypass();
|
|
220
204
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
.
|
|
228
|
-
.map((d) => d.trim())
|
|
229
|
-
.filter(Boolean);
|
|
230
|
-
if (list.length === 0) {
|
|
231
|
-
err('请提供至少一个域名');
|
|
232
|
-
return;
|
|
233
|
-
}
|
|
234
|
-
proxyConfig.saveConfig({ bypassDomains: list });
|
|
235
|
-
ok(`已设置跳过列表: ${list.join(', ')}`);
|
|
236
|
-
info('重新开启系统代理后生效: easy-proxy proxy on');
|
|
237
|
-
});
|
|
205
|
+
const buildContent = (domains) =>
|
|
206
|
+
[
|
|
207
|
+
'# 跳过代理的域名(一行一个,# 开头为注释)',
|
|
208
|
+
'# 清空所有域名 = 恢复默认列表;保存后需重新 proxy on 生效',
|
|
209
|
+
'',
|
|
210
|
+
...domains,
|
|
211
|
+
].join('\n');
|
|
238
212
|
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
213
|
+
let content = buildContent(oldList);
|
|
214
|
+
let newList;
|
|
215
|
+
while (true) {
|
|
216
|
+
let editedText;
|
|
217
|
+
try {
|
|
218
|
+
editedText = editInEditor(content, 'bypass');
|
|
219
|
+
} catch (_e) {
|
|
220
|
+
// 编辑器非零退出码(如 vi :q!)视为用户取消
|
|
221
|
+
info('已取消');
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
const list = [];
|
|
225
|
+
const seen = new Set();
|
|
226
|
+
for (const raw of editedText.replace(/\r/g, '').split('\n')) {
|
|
227
|
+
const line = raw.trim();
|
|
228
|
+
if (!line || line.startsWith('#')) continue;
|
|
229
|
+
const key = line.toLowerCase();
|
|
230
|
+
if (seen.has(key)) continue;
|
|
231
|
+
seen.add(key);
|
|
232
|
+
list.push(line);
|
|
233
|
+
}
|
|
234
|
+
// 清空 = 恢复默认列表
|
|
235
|
+
if (list.length === 0) {
|
|
236
|
+
proxyConfig.saveConfig({ bypassDomains: null });
|
|
237
|
+
ok(`已清空,恢复默认列表(${proxyConfig.getEffectiveBypass().length} 个域名)`);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
if (list.some((d) => /^https?:\/\//i.test(d))) {
|
|
241
|
+
err('域名不应包含 http:// 或 https:// 前缀,请修正后重新保存...');
|
|
242
|
+
content = editedText;
|
|
243
|
+
continue;
|
|
244
|
+
}
|
|
245
|
+
newList = list;
|
|
246
|
+
break;
|
|
251
247
|
}
|
|
252
|
-
list.push(domain);
|
|
253
|
-
proxyConfig.saveConfig({ bypassDomains: list });
|
|
254
|
-
ok(`已添加: ${domain}`);
|
|
255
|
-
info('重新开启系统代理后生效: easy-proxy proxy on');
|
|
256
|
-
});
|
|
257
248
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
.description('移除一个跳过域名')
|
|
261
|
-
.action((domain) => {
|
|
262
|
-
const proxyConfig = require('../proxy-config');
|
|
263
|
-
const config = proxyConfig.loadConfig();
|
|
264
|
-
if (!config.bypassDomains) {
|
|
265
|
-
warn('未自定义跳过列表,当前使用默认列表');
|
|
266
|
-
return;
|
|
267
|
-
}
|
|
268
|
-
const index = config.bypassDomains.indexOf(domain);
|
|
269
|
-
if (index < 0) {
|
|
270
|
-
warn(`未找到: ${domain}`);
|
|
249
|
+
if (JSON.stringify(oldList) === JSON.stringify(newList)) {
|
|
250
|
+
info('无改动');
|
|
271
251
|
return;
|
|
272
252
|
}
|
|
273
|
-
config.bypassDomains.splice(index, 1);
|
|
274
|
-
proxyConfig.saveConfig({ bypassDomains: config.bypassDomains });
|
|
275
|
-
ok(`已移除: ${domain}`);
|
|
276
|
-
info('重新开启系统代理后生效: easy-proxy proxy on');
|
|
277
|
-
});
|
|
278
253
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
.description('重置为默认跳过列表')
|
|
282
|
-
.action(() => {
|
|
283
|
-
const proxyConfig = require('../proxy-config');
|
|
284
|
-
proxyConfig.saveConfig({ bypassDomains: null });
|
|
285
|
-
ok('已重置为默认列表');
|
|
286
|
-
proxyConfig.getDefaultBypass().forEach((d) => {
|
|
287
|
-
info(` ${d}`);
|
|
288
|
-
});
|
|
254
|
+
proxyConfig.saveConfig({ bypassDomains: newList });
|
|
255
|
+
ok(`已保存跳过列表(${newList.length} 个域名)`);
|
|
289
256
|
info('重新开启系统代理后生效: easy-proxy proxy on');
|
|
290
257
|
});
|
|
291
258
|
}
|
|
@@ -15,7 +15,8 @@ function registerStart(program) {
|
|
|
15
15
|
if (certPath) ensureMergedCaBundle(certPath);
|
|
16
16
|
|
|
17
17
|
const args = opts.init ? 'start --init' : 'start';
|
|
18
|
-
|
|
18
|
+
// --dnsServer 绕过系统 DNS(ISP DNS 可能被污染,如 nodejs.org 解析到假 IP 导致 502 socket hang up)
|
|
19
|
+
execSync(`"${localW2Path}" ${args} --dnsServer 223.5.5.5`, { stdio: 'inherit' });
|
|
19
20
|
ok('whistle 已启动');
|
|
20
21
|
|
|
21
22
|
try {
|
|
@@ -66,6 +67,15 @@ function registerStart(program) {
|
|
|
66
67
|
}
|
|
67
68
|
if (count > 0) ok(`已同步 ${count} 个自定义规则`);
|
|
68
69
|
}
|
|
70
|
+
|
|
71
|
+
// 内置镜像规则集(按开关状态同步,默认 npm/pip/go 开启)
|
|
72
|
+
try {
|
|
73
|
+
const { syncMirrors } = require('../mirror-sync');
|
|
74
|
+
const { added } = await syncMirrors();
|
|
75
|
+
if (added.length > 0) ok(`已同步 ${added.length} 个镜像规则集`);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
debug('Mirror sync failed:', e.message);
|
|
78
|
+
}
|
|
69
79
|
} catch (e) {
|
|
70
80
|
err(`启动 whistle 失败: ${e.message}`);
|
|
71
81
|
process.exit(1);
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 内置镜像源规则集
|
|
3
|
+
* 每个 Key 是一个规则集名(easy-proxy mirror on/off <name>),value 是 whistle 规则文本。
|
|
4
|
+
*
|
|
5
|
+
* 原理:whistle 302 重定向。工具仍请求官方源,whistle 把请求重定向到国内镜像,
|
|
6
|
+
* 包管理器自动跟随重定向,无需改 .npmrc / pip.conf / GOPROXY 等工具配置。
|
|
7
|
+
*
|
|
8
|
+
* 已实测验证的链路:npm(302→npmmirror→tarball 下载成功)、pip(simple 页 + files.pythonhosted
|
|
9
|
+
* 文件下载成功)、go(proxy.golang.org → goproxy.cn)。Homebrew 的 formulae.brew.sh
|
|
10
|
+
* 重定向到清华镜像目录结构对不上,且 brew 本身走 GitHub(由 GitHub 加速覆盖),不内置。
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const MIRROR_RULESETS = {
|
|
14
|
+
npm: [
|
|
15
|
+
'# npm registry → npmmirror(npmpack/tarball 均跟随 302)',
|
|
16
|
+
'# 仅重定向 GET:npm publish(PUT/POST)直达官方源,不发镜像',
|
|
17
|
+
'^registry.npmjs.org/*** redirect://https://registry.npmmirror.com/$1 includeFilter://m:get',
|
|
18
|
+
'^registry.yarnpkg.com/*** redirect://https://registry.npmmirror.com/$1 includeFilter://m:get',
|
|
19
|
+
].join('\n'),
|
|
20
|
+
|
|
21
|
+
pip: [
|
|
22
|
+
'# pip → 清华 TUNA 镜像',
|
|
23
|
+
'# index 页(/simple/)和包文件(files.pythonhosted.org/packages/...)都要重定向,',
|
|
24
|
+
'# 否则 index 能拿到但下载 whl 还是走官方源',
|
|
25
|
+
'^pypi.org/simple/*** redirect://https://pypi.tuna.tsinghua.edu.cn/simple/$1',
|
|
26
|
+
'^files.pythonhosted.org/*** redirect://https://pypi.tuna.tsinghua.edu.cn/packages/$1',
|
|
27
|
+
].join('\n'),
|
|
28
|
+
|
|
29
|
+
go: [
|
|
30
|
+
'# Go module proxy → goproxy.cn',
|
|
31
|
+
'^proxy.golang.org/*** redirect://https://goproxy.cn/$1',
|
|
32
|
+
].join('\n'),
|
|
33
|
+
|
|
34
|
+
node: [
|
|
35
|
+
'# Node.js 二进制分发 → 清华镜像(fnm/nvm/n/volta 通用,工具自动跟随 302,无需设 FNM_NODE_DIST_MIRROR)',
|
|
36
|
+
'^nodejs.org/dist/*** redirect://https://mirrors.tuna.tsinghua.edu.cn/nodejs-release/$1',
|
|
37
|
+
].join('\n'),
|
|
38
|
+
|
|
39
|
+
conda: [
|
|
40
|
+
'# conda → 清华 anaconda 镜像',
|
|
41
|
+
'# 需配合工具配置 conda config --add channels 后再用,纯重定向只覆盖 repo.anaconda.com 主源',
|
|
42
|
+
'^repo.anaconda.com/pkgs/main/*** redirect://https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main/$1',
|
|
43
|
+
'^conda.anaconda.org/*** redirect://https://mirrors.tuna.tsinghua.edu.cn/anaconda-cloud/$1',
|
|
44
|
+
].join('\n'),
|
|
45
|
+
|
|
46
|
+
huggingface: [
|
|
47
|
+
'# HuggingFace → hf-mirror.com(模型/数据集下载)',
|
|
48
|
+
'^huggingface.co/*** redirect://https://hf-mirror.com/$1',
|
|
49
|
+
].join('\n'),
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// 默认启用的镜像规则集(easy-proxy start 同步时自动 select)
|
|
53
|
+
const DEFAULT_ENABLED = ['npm', 'pip', 'go'];
|
|
54
|
+
|
|
55
|
+
module.exports = {
|
|
56
|
+
MIRROR_RULESETS,
|
|
57
|
+
DEFAULT_ENABLED,
|
|
58
|
+
};
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 镜像规则集 → whistle 同步
|
|
3
|
+
* 规则集名统一加 "mirror-" 前缀放独立分组,关闭 = 从 whistle 删除规则集,
|
|
4
|
+
* 不污染用户的自定义规则空间。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
const MIRROR_GROUP = '\rEasyProxy 镜像加速';
|
|
8
|
+
const RULESET_PREFIX = 'mirror-';
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* 把当前开关状态同步到 whistle(规则集存在性 = 开关状态)
|
|
12
|
+
* @returns {Promise<{added: string[], removed: string[]}>}
|
|
13
|
+
*/
|
|
14
|
+
async function syncMirrors() {
|
|
15
|
+
const api = require('./rule-api');
|
|
16
|
+
const { MIRROR_RULESETS } = require('./mirror-rulesets');
|
|
17
|
+
const { loadMirrorState } = require('./commands/mirror');
|
|
18
|
+
const state = loadMirrorState();
|
|
19
|
+
|
|
20
|
+
await api.createGroup(MIRROR_GROUP);
|
|
21
|
+
const added = [];
|
|
22
|
+
const removed = [];
|
|
23
|
+
|
|
24
|
+
for (const [name, rule] of Object.entries(MIRROR_RULESETS)) {
|
|
25
|
+
const rulesetName = RULESET_PREFIX + name;
|
|
26
|
+
if (state[name] === true) {
|
|
27
|
+
await api.addRuleset(rulesetName, rule, MIRROR_GROUP);
|
|
28
|
+
await api.selectRuleset(rulesetName, rule);
|
|
29
|
+
added.push(rulesetName);
|
|
30
|
+
} else {
|
|
31
|
+
// 关闭 = 删除规则集(不存在时 remove 返回错误,忽略)
|
|
32
|
+
try {
|
|
33
|
+
await api.removeRuleset(rulesetName);
|
|
34
|
+
removed.push(rulesetName);
|
|
35
|
+
} catch (_e) {
|
|
36
|
+
// 本来就没开,跳过
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
return { added, removed };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
module.exports = {
|
|
45
|
+
syncMirrors,
|
|
46
|
+
MIRROR_GROUP,
|
|
47
|
+
RULESET_PREFIX,
|
|
48
|
+
};
|
package/lib/cli/proxy-config.js
CHANGED
|
@@ -28,14 +28,9 @@ function getEffectiveBypass() {
|
|
|
28
28
|
return bypassDomains?.length > 0 ? bypassDomains : DEFAULT_BYPASS_DOMAINS;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
function getDefaultBypass() {
|
|
32
|
-
return DEFAULT_BYPASS_DOMAINS;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
31
|
module.exports = {
|
|
36
32
|
DEFAULT_BYPASS_DOMAINS,
|
|
37
33
|
loadConfig: loadProxyConfig,
|
|
38
34
|
saveConfig: saveProxyConfig,
|
|
39
35
|
getEffectiveBypass,
|
|
40
|
-
getDefaultBypass,
|
|
41
36
|
};
|
package/lib/config.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "whistle.easy-proxy",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "EasyProxy whistle plugin — GitHub acceleration, SNI rewrite, hosts management",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"homepage": "https://github.com/sqlwwx/easy-proxy",
|
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
},
|
|
14
14
|
"author": "sqlwwx",
|
|
15
15
|
"license": "MIT",
|
|
16
|
+
"packageManager": "pnpm@11.18.0",
|
|
16
17
|
"files": [
|
|
17
18
|
"index.js",
|
|
18
19
|
"bin/",
|
|
@@ -51,9 +52,6 @@
|
|
|
51
52
|
"access": "public",
|
|
52
53
|
"registry": "https://registry.npmjs.org/"
|
|
53
54
|
},
|
|
54
|
-
"devDependencies": {
|
|
55
|
-
"@biomejs/biome": "^2.5.14"
|
|
56
|
-
},
|
|
57
55
|
"scripts": {
|
|
58
56
|
"preinstall": "npx only-allow pnpm",
|
|
59
57
|
"start": "easy-proxy start",
|
|
@@ -61,6 +59,10 @@
|
|
|
61
59
|
"test": "node --test",
|
|
62
60
|
"health": "easy-proxy health",
|
|
63
61
|
"format": "biome format . --write",
|
|
64
|
-
"format:check": "biome format ."
|
|
62
|
+
"format:check": "biome format .",
|
|
63
|
+
"prepublishOnly": "npm test"
|
|
64
|
+
},
|
|
65
|
+
"devDependencies": {
|
|
66
|
+
"@biomejs/biome": "^2.5.14"
|
|
65
67
|
}
|
|
66
|
-
}
|
|
68
|
+
}
|