pac-proxy-cli 1.2.0 → 1.2.1
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/lib/config-cli.js +115 -56
- package/lib/index.js +14 -0
- package/package.json +1 -1
package/lib/config-cli.js
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
getPacRules,
|
|
11
11
|
setPacRules,
|
|
12
12
|
getProxyConfig,
|
|
13
|
+
getSslocalConfig,
|
|
13
14
|
} from './local-store.js';
|
|
14
15
|
import { getSslocalCiphers } from './sslocal-manager.js';
|
|
15
16
|
|
|
@@ -135,6 +136,102 @@ export async function tryPushProxyToRunningServe() {
|
|
|
135
136
|
return false;
|
|
136
137
|
}
|
|
137
138
|
|
|
139
|
+
/**
|
|
140
|
+
* 若本机已有 `pac-proxy serve` 在跑,对其发起 PUT /api/local/sslocal,与 Web「Shadowsocks」页保存一致(写盘、启停 sslocal、applyProxyService)。
|
|
141
|
+
* @returns {Promise<boolean>} 是否成功命中并同步
|
|
142
|
+
*/
|
|
143
|
+
export async function tryPushSslocalToRunningServe() {
|
|
144
|
+
const sslocal = getSslocalConfig();
|
|
145
|
+
const body = JSON.stringify(sslocal);
|
|
146
|
+
for (const port of getConsoleHttpPortsToProbe()) {
|
|
147
|
+
const ctrl = new AbortController();
|
|
148
|
+
const tid = setTimeout(() => ctrl.abort(), 800);
|
|
149
|
+
try {
|
|
150
|
+
const r = await fetch(`http://127.0.0.1:${port}/api/local/sslocal`, {
|
|
151
|
+
method: 'PUT',
|
|
152
|
+
headers: { 'Content-Type': 'application/json' },
|
|
153
|
+
body,
|
|
154
|
+
signal: ctrl.signal,
|
|
155
|
+
});
|
|
156
|
+
if (r.ok) {
|
|
157
|
+
console.log('已向运行中的控制台同步 Shadowsocks (sslocal) 设置(与 Web 保存一致)。');
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
} catch {
|
|
161
|
+
/* 下一端口 */
|
|
162
|
+
} finally {
|
|
163
|
+
clearTimeout(tid);
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* 与 Web「Shadowsocks / sslocal」页字段一致的交互问答。
|
|
171
|
+
* @param {Record<string, unknown>} currentSslocal
|
|
172
|
+
*/
|
|
173
|
+
async function promptSslocalFields(currentSslocal) {
|
|
174
|
+
const sslocal = { ...currentSslocal };
|
|
175
|
+
sslocal.enabled = await confirm({
|
|
176
|
+
message: '是否启用内置代理客户端 (sslocal)',
|
|
177
|
+
default: !!sslocal.enabled,
|
|
178
|
+
});
|
|
179
|
+
if (!sslocal.enabled) {
|
|
180
|
+
return { ...currentSslocal, enabled: false };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
const server = await input({
|
|
184
|
+
message: '服务器地址',
|
|
185
|
+
default: sslocal.server || '',
|
|
186
|
+
});
|
|
187
|
+
if ((server || '').trim()) sslocal.server = server.trim();
|
|
188
|
+
|
|
189
|
+
const sp = await input({
|
|
190
|
+
message: '服务器端口',
|
|
191
|
+
default: String(sslocal.serverPort ?? 8388),
|
|
192
|
+
validate: validatePortNum,
|
|
193
|
+
});
|
|
194
|
+
sslocal.serverPort = Number(sp);
|
|
195
|
+
|
|
196
|
+
const lp = await input({
|
|
197
|
+
message: '本地 SOCKS5 端口',
|
|
198
|
+
default: String(sslocal.localPort ?? 1080),
|
|
199
|
+
validate: validatePortNum,
|
|
200
|
+
});
|
|
201
|
+
sslocal.localPort = Number(lp);
|
|
202
|
+
|
|
203
|
+
const changePw = await confirm({
|
|
204
|
+
message: '是否修改密码?(否则保留已保存密码)',
|
|
205
|
+
default: false,
|
|
206
|
+
});
|
|
207
|
+
if (changePw) {
|
|
208
|
+
const pw = await password({ message: '密码', mask: '*' });
|
|
209
|
+
sslocal.password = pw || '';
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const methods = getSslocalCiphers();
|
|
213
|
+
const methodChoices = methods.map((m) => ({ name: m, value: m }));
|
|
214
|
+
const currentMethod = methods.includes(sslocal.method) ? sslocal.method : methods[0];
|
|
215
|
+
sslocal.method = await select({
|
|
216
|
+
message: '加密方法',
|
|
217
|
+
choices: methodChoices,
|
|
218
|
+
default: currentMethod,
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
const to = await input({
|
|
222
|
+
message: '超时(秒)',
|
|
223
|
+
default: String(sslocal.timeout ?? 300),
|
|
224
|
+
validate: (v) => {
|
|
225
|
+
const n = Number((v || '').trim());
|
|
226
|
+
if (!Number.isInteger(n) || n < 1) return '请输入正整数';
|
|
227
|
+
return true;
|
|
228
|
+
},
|
|
229
|
+
});
|
|
230
|
+
sslocal.timeout = Number((to || '').trim());
|
|
231
|
+
sslocal.enabled = true;
|
|
232
|
+
return sslocal;
|
|
233
|
+
}
|
|
234
|
+
|
|
138
235
|
export async function runConfigProxyModeInteractive() {
|
|
139
236
|
const proxy = getProxyConfig();
|
|
140
237
|
/** @type {'direct'|'global'|'pac'|'mitm'} */
|
|
@@ -524,66 +621,12 @@ export async function runConfigInteractive() {
|
|
|
524
621
|
default: proxy.applySystemProxy !== false,
|
|
525
622
|
});
|
|
526
623
|
|
|
527
|
-
|
|
528
|
-
message: '是否启用内置代理客户端 (sslocal)',
|
|
529
|
-
default: !!sslocal.enabled,
|
|
530
|
-
});
|
|
531
|
-
|
|
532
|
-
if (sslocal.enabled) {
|
|
533
|
-
const server = await input({
|
|
534
|
-
message: '服务器地址',
|
|
535
|
-
default: sslocal.server || '',
|
|
536
|
-
});
|
|
537
|
-
if ((server || '').trim()) sslocal.server = server.trim();
|
|
538
|
-
|
|
539
|
-
const sp = await input({
|
|
540
|
-
message: '服务器端口',
|
|
541
|
-
default: String(sslocal.serverPort ?? 8388),
|
|
542
|
-
validate: validatePortNum,
|
|
543
|
-
});
|
|
544
|
-
sslocal.serverPort = Number(sp);
|
|
545
|
-
|
|
546
|
-
const lp = await input({
|
|
547
|
-
message: '本地 SOCKS5 端口',
|
|
548
|
-
default: String(sslocal.localPort ?? 1080),
|
|
549
|
-
validate: validatePortNum,
|
|
550
|
-
});
|
|
551
|
-
sslocal.localPort = Number(lp);
|
|
552
|
-
|
|
553
|
-
const changePw = await confirm({
|
|
554
|
-
message: '是否修改密码?(否则保留已保存密码)',
|
|
555
|
-
default: false,
|
|
556
|
-
});
|
|
557
|
-
if (changePw) {
|
|
558
|
-
const pw = await password({ message: '密码', mask: '*' });
|
|
559
|
-
sslocal.password = pw || '';
|
|
560
|
-
}
|
|
561
|
-
|
|
562
|
-
const methods = getSslocalCiphers();
|
|
563
|
-
const methodChoices = methods.map((m) => ({ name: m, value: m }));
|
|
564
|
-
const currentMethod = methods.includes(sslocal.method) ? sslocal.method : methods[0];
|
|
565
|
-
sslocal.method = await select({
|
|
566
|
-
message: '加密方法',
|
|
567
|
-
choices: methodChoices,
|
|
568
|
-
default: currentMethod,
|
|
569
|
-
});
|
|
570
|
-
|
|
571
|
-
const to = await input({
|
|
572
|
-
message: '超时(秒)',
|
|
573
|
-
default: String(sslocal.timeout ?? 300),
|
|
574
|
-
validate: (v) => {
|
|
575
|
-
const n = Number((v || '').trim());
|
|
576
|
-
if (!Number.isInteger(n) || n < 1) return '请输入正整数';
|
|
577
|
-
return true;
|
|
578
|
-
},
|
|
579
|
-
});
|
|
580
|
-
sslocal.timeout = Number((to || '').trim());
|
|
581
|
-
}
|
|
624
|
+
const nextSslocal = await promptSslocalFields(sslocal);
|
|
582
625
|
|
|
583
626
|
cfg = {
|
|
584
627
|
...cfg,
|
|
585
628
|
proxy: { ...cfg.proxy, ...proxy },
|
|
586
|
-
sslocal:
|
|
629
|
+
sslocal: nextSslocal,
|
|
587
630
|
};
|
|
588
631
|
saveConfig(cfg);
|
|
589
632
|
|
|
@@ -597,6 +640,22 @@ export async function runConfigInteractive() {
|
|
|
597
640
|
printPathsHint(loadConfig());
|
|
598
641
|
}
|
|
599
642
|
|
|
643
|
+
/** 仅编辑 Shadowsocks (sslocal),与 Web「Shadowsocks」页共用 config.json 字段与保存副作用。 */
|
|
644
|
+
export async function runConfigShadowsocksInteractive() {
|
|
645
|
+
const cfg = loadConfig();
|
|
646
|
+
console.log('\nShadowsocks (sslocal) — 与 Web 控制台「代理客户端」页共用配置。\n');
|
|
647
|
+
|
|
648
|
+
const nextSslocal = await promptSslocalFields({ ...cfg.sslocal });
|
|
649
|
+
saveConfig({ ...cfg, sslocal: nextSslocal });
|
|
650
|
+
|
|
651
|
+
console.log('\n配置已保存。');
|
|
652
|
+
const pushed = await tryPushSslocalToRunningServe();
|
|
653
|
+
if (!pushed) {
|
|
654
|
+
console.log('未检测到运行中的控制台,sslocal 进程与上游代理未自动应用;可启动 pac-proxy serve 后重试本命令或在 Web 中保存。');
|
|
655
|
+
}
|
|
656
|
+
printPathsHint(loadConfig());
|
|
657
|
+
}
|
|
658
|
+
|
|
600
659
|
export async function runConfigInit() {
|
|
601
660
|
console.log('\n首次配置向导(可随时用 pac-proxy config 再次编辑)。\n');
|
|
602
661
|
printPathsHint(loadConfig());
|
package/lib/index.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
runConfigPrint,
|
|
8
8
|
runConfigInteractive,
|
|
9
9
|
runConfigInit,
|
|
10
|
+
runConfigShadowsocksInteractive,
|
|
10
11
|
runPacImportFile,
|
|
11
12
|
runPacRulesInteractive,
|
|
12
13
|
runConfigProxyModeInteractive,
|
|
@@ -84,6 +85,19 @@ configCmd
|
|
|
84
85
|
await runConfigProxyModeInteractive();
|
|
85
86
|
});
|
|
86
87
|
|
|
88
|
+
configCmd
|
|
89
|
+
.command('shadowsocks')
|
|
90
|
+
.alias('sslocal')
|
|
91
|
+
.description('交互式编辑 Shadowsocks (sslocal),与 Web「代理客户端」页一致')
|
|
92
|
+
.action(async () => {
|
|
93
|
+
if (!process.stdin.isTTY) {
|
|
94
|
+
console.error('非交互终端无法使用。请使用: pac-proxy config --print');
|
|
95
|
+
process.exitCode = 1;
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
await runConfigShadowsocksInteractive();
|
|
99
|
+
});
|
|
100
|
+
|
|
87
101
|
configCmd
|
|
88
102
|
.command('import')
|
|
89
103
|
.description('从 JSON 文件合并导入 PAC 规则(与 Web 导入逻辑一致)')
|