whistle.easy-proxy 1.0.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.
@@ -0,0 +1,187 @@
1
+ const DEFAULT_BYPASS_DOMAINS = [
2
+ // GitHub 相关
3
+ 'objects-origin.githubusercontent.com',
4
+ '*.ghproxy.net',
5
+ '*.ghp.ci',
6
+ '*.kkgithub.com',
7
+ '*.dgithub.xyz',
8
+ 'pages.github.com',
9
+ 'help.github.com',
10
+ 'docs.github.com',
11
+ '*.github.blog',
12
+ '*.githubassets.com',
13
+ 'ssh.github.com',
14
+
15
+ // Docker 相关
16
+ 'www.docker.com',
17
+ 'login.docker.com',
18
+ 'api.dso.docker.com',
19
+ 'desktop.docker.com',
20
+ 'docs.docker.com',
21
+
22
+ // 微软相关
23
+ '*.s-microsoft.com',
24
+ '*.xboxlive.com',
25
+ '*.microsoftonline.com',
26
+ '*.msedge.net',
27
+ '*.microsoft.com',
28
+ '*.windows.com',
29
+ '*.office.com',
30
+ '*.office.net',
31
+ '*.msn.com',
32
+
33
+ // 国内服务
34
+ '*.mihoyo.com',
35
+ '*.bilicomic.com',
36
+ '*.bilibili.com',
37
+ '*.bilivideo.com',
38
+ '*.biliapi.net',
39
+ '*.csdn.net',
40
+ '*.so.com',
41
+ '*.baidu.com',
42
+ '*.baiducontent.com',
43
+ '*.bdimg.com',
44
+ '*.bdstatic.com',
45
+ '*.bdydns.com',
46
+ '*.tencent.com',
47
+ '*.qq.com',
48
+ '*.weixin.com',
49
+ '*.weixinbridge.com',
50
+ '*.wechat.com',
51
+ '*.idqqimg.com',
52
+ '*.gtimg.com',
53
+ '*.qpic.com',
54
+ '*.qlogo.com',
55
+ '*.myapp.com',
56
+ '*.aliyun.com',
57
+ '*.alipay.com',
58
+ '*.taobao.com',
59
+ '*.ut.taobao.com',
60
+ '*.tmall.com',
61
+ '*.alipayobjects.com',
62
+ '*.dingtalk.com',
63
+ '*.mmstat.com',
64
+ '*.alicdn.com',
65
+ '*.hdslb.com',
66
+ 'gitee.com',
67
+ '*.gitee.com',
68
+ '*.gitee.io',
69
+ '*.giteeusercontent.com',
70
+ '*.immersivetranslate.com',
71
+ '*.trafficmanager.net',
72
+ '*.ximalaya.com',
73
+ '*.xmcdn.com',
74
+ '*.dingtalkapps.com',
75
+ '*.aliyuncs.com',
76
+ '*.feishu.cn',
77
+ '*.volcengine.com',
78
+ '*.zijieapi.com',
79
+
80
+ // 其他服务
81
+ '*.cmicapm.com',
82
+ '*.cloudflare-cn.com',
83
+ '*kaspersky*.com',
84
+ '*.upd.kaspersky.com',
85
+ '*.lanhuapp.com',
86
+ '*.soboten.com',
87
+ '*.sandboxie-plus.com',
88
+ '*.wuyou.net',
89
+ '*.pyecharts.org',
90
+ '*.bcloudlink.com',
91
+ '*.qijishow.com',
92
+ '*.z-lib.fo',
93
+ '*.finalshell.com',
94
+ '*.minebbs.com',
95
+ '*.spigotmc.org',
96
+ '*.virustotal.com',
97
+ '*.gitlab.com',
98
+ '*.deepseek.com',
99
+ '*steaminventoryhelper.com',
100
+ '*.youdemai.com',
101
+ '*.casualthink.com',
102
+
103
+ // 特定 IP
104
+ '44.239.165.12',
105
+ '3.164.110.117',
106
+
107
+ // 中国相关
108
+ '*.cn',
109
+ '*china*',
110
+ '*.ghproxy.*',
111
+
112
+ // DeepL
113
+ 'www.deepl.com',
114
+
115
+ // Mozilla/Firefox
116
+ '*.mozilla.org',
117
+ '*.mozilla.com',
118
+ '*.mozilla.net',
119
+ '*.firefox.com',
120
+ '*.firefox.org',
121
+ '*.mozillademos.org',
122
+ '*.mozillians.org',
123
+ '*.mozillians.net',
124
+ '*.mozillians.com',
125
+
126
+ // Java 依赖
127
+ '*.sonatype.org',
128
+ '*.maven.org',
129
+ '*.mvnrepository.com',
130
+
131
+ // Apple
132
+ '*.apple.com',
133
+ '*.icloud.com',
134
+
135
+ // WPS
136
+ '*.wps.com',
137
+ '*.wps.net',
138
+ '*.ksord.com',
139
+
140
+ // 360
141
+ '*.qihoo.com',
142
+ '*.qihucdn.com',
143
+ '*.360.com',
144
+ '*.360safe.com',
145
+ '*.360buyimg.com',
146
+ '*.360buy.com',
147
+ '*.jd.com',
148
+ '*.jcloud.com',
149
+ '*.jcloudcs.com',
150
+ '*.jcloudcache.com',
151
+ '*.jcloudcdn.com',
152
+ '*.jcloudlb.com',
153
+
154
+ // 中国移动
155
+ '*.10086.com',
156
+ '*.10086cloud.com',
157
+ '*.139.com',
158
+ '*.xunlei.com',
159
+ '*.icpapi.com',
160
+ '*.navicat.com',
161
+
162
+ // 本地地址
163
+ 'localhost',
164
+ 'localhost.*',
165
+ '127.*.*.*',
166
+ 'test.*',
167
+ '10.*.*.*',
168
+ '172.16.*.*',
169
+ '172.17.*.*',
170
+ '172.18.*.*',
171
+ '172.19.*.*',
172
+ '172.20.*.*',
173
+ '172.21.*.*',
174
+ '172.22.*.*',
175
+ '172.23.*.*',
176
+ '172.24.*.*',
177
+ '172.25.*.*',
178
+ '172.26.*.*',
179
+ '172.27.*.*',
180
+ '172.28.*.*',
181
+ '172.29.*.*',
182
+ '172.30.*.*',
183
+ '172.31.*.*',
184
+ '192.168.*.*',
185
+ ];
186
+
187
+ module.exports = { DEFAULT_BYPASS_DOMAINS };
@@ -0,0 +1,184 @@
1
+ const { readConfig, writeConfig, ensureSni, c, info, ok, err, title, sep } = require('../helpers');
2
+
3
+ function registerAccel(program) {
4
+ const accelCmd = program.command('accel').description('GitHub 加速管理');
5
+
6
+ accelCmd
7
+ .command('on')
8
+ .description('启用加速')
9
+ .action(() => {
10
+ const cfg = readConfig();
11
+ cfg.enabled = true;
12
+ writeConfig(cfg);
13
+ ok('加速已启用');
14
+ });
15
+
16
+ accelCmd
17
+ .command('off')
18
+ .description('关闭加速')
19
+ .action(() => {
20
+ const cfg = readConfig();
21
+ cfg.enabled = false;
22
+ writeConfig(cfg);
23
+ ok('加速已关闭');
24
+ });
25
+
26
+ accelCmd
27
+ .command('dns')
28
+ .description('刷新 DNS 缓存')
29
+ .action(async () => {
30
+ info('正在刷新 DNS 缓存...');
31
+ try {
32
+ const { getOptions } = require('../rule-api');
33
+ const opts = getOptions();
34
+ const http = require('node:http');
35
+ await new Promise((resolve, reject) => {
36
+ const headers = {};
37
+ if (opts.auth) headers.Authorization = opts.auth;
38
+ headers['Content-Type'] = 'application/json';
39
+ const req = http.request(
40
+ {
41
+ hostname: opts.host,
42
+ port: opts.port,
43
+ path: '/plugin.easy-proxy/api/refresh',
44
+ method: 'POST',
45
+ headers,
46
+ },
47
+ (res) => {
48
+ let data = '';
49
+ res.on('data', (c) => (data += c));
50
+ res.on('end', () => {
51
+ if (res.statusCode === 200) resolve(data);
52
+ else reject(new Error(`HTTP ${res.statusCode}`));
53
+ });
54
+ }
55
+ );
56
+ req.on('error', () => reject(new Error('无法连接 whistle')));
57
+ req.write('{}');
58
+ req.end();
59
+ });
60
+ ok('DNS 缓存已刷新');
61
+ } catch (_e) {
62
+ info('whistle 未运行,已跳过运行时缓存刷新(配置文件不受影响)');
63
+ ok('完成');
64
+ }
65
+ });
66
+
67
+ accelCmd
68
+ .command('status')
69
+ .description('查看加速状态')
70
+ .action(() => {
71
+ const cfg = readConfig();
72
+ title('加速状态');
73
+ sep();
74
+ info(
75
+ '加速功能: ' +
76
+ (cfg.enabled ? `${c.green}✓ 已启用` : `${c.red}✗ 已关闭`) +
77
+ c.reset
78
+ );
79
+ info(
80
+ 'SNI 改写: ' +
81
+ (cfg.sniRewrite?.enabled ? `${c.green}✓ 已启用` : `${c.red}✗ 已关闭`) +
82
+ c.reset
83
+ );
84
+ info(`DoH 服务器: ${cfg.doh ? cfg.doh.host : 'dns.google'}`);
85
+ if (cfg.hosts?.length) info(`Hosts 覆盖: ${cfg.hosts.length} 条`);
86
+ sep();
87
+ });
88
+
89
+ const sniCmd = accelCmd.command('sni').description('SNI 改写管理');
90
+
91
+ sniCmd
92
+ .command('on')
93
+ .description('启用 SNI 改写')
94
+ .action(() => {
95
+ const cfg = readConfig();
96
+ ensureSni(cfg);
97
+ cfg.sniRewrite.enabled = true;
98
+ writeConfig(cfg);
99
+ ok('SNI 改写已启用');
100
+ });
101
+
102
+ sniCmd
103
+ .command('off')
104
+ .description('关闭 SNI 改写')
105
+ .action(() => {
106
+ const cfg = readConfig();
107
+ ensureSni(cfg);
108
+ cfg.sniRewrite.enabled = false;
109
+ writeConfig(cfg);
110
+ ok('SNI 改写已关闭');
111
+ });
112
+
113
+ sniCmd
114
+ .command('status')
115
+ .description('查看 SNI 改写状态')
116
+ .action(() => {
117
+ const cfg = readConfig();
118
+ const s = cfg.sniRewrite;
119
+ if (s?.enabled) ok('SNI 改写: 已启用');
120
+ else info('SNI 改写: 已关闭');
121
+ });
122
+
123
+ sniCmd
124
+ .command('config')
125
+ .description('查看 SNI 改写配置')
126
+ .action(() => {
127
+ const cfg = readConfig();
128
+ const s = cfg.sniRewrite || {};
129
+ title('SNI 改写配置');
130
+ sep();
131
+ info(`启用状态: ${s.enabled ? `${c.green}✓` : `${c.red}✗`}${c.reset}`);
132
+ info(`默认 SNI: ${s.defaultSni || '<无>'}`);
133
+ const sm = s.sniMap || {};
134
+ const entries = Object.entries(sm);
135
+ if (entries.length) {
136
+ info('SNI 映射:');
137
+ entries.forEach(([k, v]) => info(` ${k} → ${v}`));
138
+ } else {
139
+ info('SNI 映射: <无>');
140
+ }
141
+ sep();
142
+ });
143
+
144
+ sniCmd
145
+ .command('set-default <domain>')
146
+ .description('设置默认 SNI 域名')
147
+ .action((domain) => {
148
+ const cfg = readConfig();
149
+ ensureSni(cfg);
150
+ cfg.sniRewrite.defaultSni = domain;
151
+ writeConfig(cfg);
152
+ ok(`默认 SNI 已设置为: ${domain}`);
153
+ });
154
+
155
+ sniCmd
156
+ .command('add-map <from> <to>')
157
+ .description('添加 SNI 映射')
158
+ .action((from, to) => {
159
+ const cfg = readConfig();
160
+ ensureSni(cfg);
161
+ cfg.sniRewrite.sniMap[from] = to;
162
+ writeConfig(cfg);
163
+ ok(`SNI 映射已添加: ${from} → ${to}`);
164
+ });
165
+
166
+ sniCmd
167
+ .command('remove-map <from>')
168
+ .description('移除 SNI 映射')
169
+ .action((from) => {
170
+ const cfg = readConfig();
171
+ ensureSni(cfg);
172
+ if (cfg.sniRewrite.sniMap[from]) {
173
+ delete cfg.sniRewrite.sniMap[from];
174
+ writeConfig(cfg);
175
+ ok(`SNI 映射已移除: ${from}`);
176
+ } else {
177
+ err(`映射不存在: ${from}`);
178
+ }
179
+ });
180
+ }
181
+
182
+ module.exports = {
183
+ registerAccel,
184
+ };
@@ -0,0 +1,35 @@
1
+ const { localW2Path, ok, err, info } = require('../helpers');
2
+
3
+ function registerCert(program) {
4
+ const certCmd = program.command('cert').description('证书管理');
5
+
6
+ certCmd
7
+ .command('install')
8
+ .description('安装根证书(调用 w2 ca)')
9
+ .option('-f, --force', '强制重新生成证书')
10
+ .action((opts) => {
11
+ const { execSync } = require('node:child_process');
12
+ try {
13
+ if (opts.force) {
14
+ info('强制重新生成证书...');
15
+ }
16
+ execSync(`"${localW2Path}" ca`, { stdio: 'inherit' });
17
+ ok('证书安装完成');
18
+ } catch (e) {
19
+ err(`证书安装失败: ${e.message}`);
20
+ process.exit(1);
21
+ }
22
+ });
23
+
24
+ certCmd
25
+ .command('check')
26
+ .description('检查证书安装状态和有效期')
27
+ .action(async () => {
28
+ const { checkCertStatus } = require('../../cert-check');
29
+ await checkCertStatus();
30
+ });
31
+ }
32
+
33
+ module.exports = {
34
+ registerCert,
35
+ };
@@ -0,0 +1,50 @@
1
+ const { CONFIG_PATH } = require('../../config');
2
+ const { readConfig, info, ok, warn, err, title, sep } = require('../helpers');
3
+
4
+ function registerConfig(program) {
5
+ const configCmd = program.command('config').description('配置管理');
6
+
7
+ configCmd
8
+ .command('show')
9
+ .description('显示当前配置')
10
+ .action(() => {
11
+ const cfg = readConfig();
12
+ title('当前配置');
13
+ sep();
14
+ console.log(JSON.stringify(cfg, null, 2));
15
+ sep();
16
+ info(`配置文件: ${CONFIG_PATH}`);
17
+ });
18
+
19
+ configCmd
20
+ .command('path')
21
+ .description('显示配置文件路径')
22
+ .action(() => {
23
+ info(`配置文件: ${CONFIG_PATH}`);
24
+ });
25
+
26
+ configCmd
27
+ .command('edit')
28
+ .description('编辑配置文件(打开 $EDITOR)')
29
+ .action(() => {
30
+ const fs = require('node:fs');
31
+ const { execSync } = require('node:child_process');
32
+ const editor = process.env.EDITOR || process.env.VISUAL || 'vi';
33
+ try {
34
+ execSync(`${editor} "${CONFIG_PATH}"`, { stdio: 'inherit' });
35
+ // 验证配置
36
+ try {
37
+ JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf-8'));
38
+ ok('配置文件已更新');
39
+ } catch (e) {
40
+ warn(`配置文件格式错误: ${e.message}`);
41
+ }
42
+ } catch (e) {
43
+ err(`编辑配置失败: ${e.message}`);
44
+ }
45
+ });
46
+ }
47
+
48
+ module.exports = {
49
+ registerConfig,
50
+ };
@@ -0,0 +1,53 @@
1
+ const { getWhistleStatus, info, ok, warn, title, sep } = require('../helpers');
2
+
3
+ function registerHealth(program) {
4
+ program
5
+ .command('health')
6
+ .description('综合健康检查(whistle + 系统代理 + 证书)')
7
+ .action(async () => {
8
+ const { getLocalIp } = require('../../network-utils');
9
+ const { checkSystemProxy } = require('../../proxy-check');
10
+ const { checkCertStatus } = require('../../cert-check');
11
+
12
+ title('健康检查');
13
+ sep();
14
+
15
+ // 1. whistle 进程
16
+ const ws = getWhistleStatus();
17
+
18
+ if (ws.running) {
19
+ ok(`Whistle 代理: 运行中${ws.port ? ` (端口: ${ws.port})` : ''}`);
20
+ } else {
21
+ warn('Whistle 代理: 未运行');
22
+ info(' 运行 easy-proxy start --init 启动');
23
+ }
24
+
25
+ // 2. 系统代理
26
+ if (ws.running && ws.port) {
27
+ const proxyOn = await checkSystemProxy(ws.port);
28
+ if (proxyOn) {
29
+ ok('系统代理: 已开启');
30
+ } else {
31
+ warn('系统代理: 未指向 whistle');
32
+ info(' 运行 easy-proxy proxy on');
33
+ }
34
+ } else {
35
+ info('系统代理: (whistle 未运行,跳过检查)');
36
+ }
37
+
38
+ // 3. 证书
39
+ await checkCertStatus(true);
40
+
41
+ // 4. 本机 IP
42
+ info(`本机 IP: ${getLocalIp()}`);
43
+
44
+ sep();
45
+ if (!ws.running) {
46
+ process.exit(1);
47
+ }
48
+ });
49
+ }
50
+
51
+ module.exports = {
52
+ registerHealth,
53
+ };
@@ -0,0 +1,79 @@
1
+ const { readConfig, writeConfig, info, ok, err, title, sep } = require('../helpers');
2
+
3
+ function registerHost(program) {
4
+ const hostCmd = program.command('host').description('Hosts 覆盖管理');
5
+
6
+ hostCmd
7
+ .command('list')
8
+ .description('列出所有 Hosts 覆盖')
9
+ .action(() => {
10
+ const cfg = readConfig();
11
+ const hosts = cfg.hosts || [];
12
+ title('Hosts 覆盖');
13
+ sep();
14
+ if (!hosts.length) {
15
+ info('暂无 Hosts 覆盖');
16
+ info('使用: easy-proxy host add <域名> <IP>');
17
+ return;
18
+ }
19
+ hosts.forEach((h, i) => info(`${i + 1}. ${h.pattern} → ${h.ip}`));
20
+ sep();
21
+ info(`共 ${hosts.length} 条`);
22
+ });
23
+
24
+ hostCmd
25
+ .command('add <domain> <ip>')
26
+ .description('添加 Hosts 覆盖')
27
+ .action((domain, ip) => {
28
+ const cfg = readConfig();
29
+ if (!cfg.hosts) cfg.hosts = [];
30
+ const idx = cfg.hosts.findIndex((h) => h.pattern === domain);
31
+ if (idx >= 0) {
32
+ cfg.hosts[idx].ip = ip;
33
+ writeConfig(cfg);
34
+ ok(`已更新: ${domain} → ${ip}`);
35
+ } else {
36
+ cfg.hosts.push({ pattern: domain, ip });
37
+ writeConfig(cfg);
38
+ ok(`已添加: ${domain} → ${ip}`);
39
+ }
40
+ });
41
+
42
+ hostCmd
43
+ .command('remove <domain>')
44
+ .description('移除 Hosts 覆盖')
45
+ .action((domain) => {
46
+ const cfg = readConfig();
47
+ if (!cfg.hosts?.length) {
48
+ err('暂无 Hosts 覆盖');
49
+ return;
50
+ }
51
+ const idx = cfg.hosts.findIndex((h) => h.pattern === domain);
52
+ if (idx < 0) {
53
+ err(`覆盖不存在: ${domain}`);
54
+ return;
55
+ }
56
+ cfg.hosts.splice(idx, 1);
57
+ writeConfig(cfg);
58
+ ok(`已移除: ${domain}`);
59
+ });
60
+
61
+ hostCmd
62
+ .command('clear')
63
+ .description('清除所有 Hosts 覆盖')
64
+ .action(() => {
65
+ const cfg = readConfig();
66
+ if (!cfg.hosts?.length) {
67
+ info('已经为空');
68
+ return;
69
+ }
70
+ const n = cfg.hosts.length;
71
+ cfg.hosts = [];
72
+ writeConfig(cfg);
73
+ ok(`已清除 ${n} 条`);
74
+ });
75
+ }
76
+
77
+ module.exports = {
78
+ registerHost,
79
+ };
@@ -0,0 +1,31 @@
1
+ const { registerStart, registerStop, registerW2 } = require('./start');
2
+ const { registerAccel } = require('./accel');
3
+ const { registerHost } = require('./host');
4
+ const { registerRule } = require('./rule');
5
+ const { registerProxy } = require('./proxy');
6
+ const { registerCert } = require('./cert');
7
+ const { registerHealth } = require('./health');
8
+ const { registerStatus } = require('./status');
9
+ const { registerConfig } = require('./config-cmd');
10
+ const { registerIntercept } = require('./intercept');
11
+ const { registerNetwork } = require('./network');
12
+
13
+ function registerAllCommands(program) {
14
+ registerStart(program);
15
+ registerStop(program);
16
+ registerW2(program);
17
+ registerAccel(program);
18
+ registerHost(program);
19
+ registerRule(program);
20
+ registerProxy(program);
21
+ registerCert(program);
22
+ registerHealth(program);
23
+ registerStatus(program);
24
+ registerConfig(program);
25
+ registerIntercept(program);
26
+ registerNetwork(program);
27
+ }
28
+
29
+ module.exports = {
30
+ registerAllCommands,
31
+ };