@qpjoy/tunnel-cli 0.1.0 → 0.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/README.md CHANGED
@@ -17,19 +17,35 @@ Run commands directly through the bundled script:
17
17
 
18
18
  ```bash
19
19
  qp-tunnel-cli status
20
- qp-tunnel-cli tun-on
21
- qp-tunnel-cli tun-off
20
+ qp-tunnel-cli server-on
22
21
  qp-tunnel-cli update-subscription
23
22
  ```
24
23
 
25
24
  For server commands, `qp-tunnel-cli` re-runs itself with `sudo` when root is needed.
25
+ Use `server-on` for public VPS hosts: it keeps Mihomo running as a local outbound
26
+ proxy and configures shell, SSH, Docker/containerd/buildkit proxy drop-ins without
27
+ enabling TUN route takeover. Reserve `tun-on` for machines that are not serving
28
+ public inbound traffic.
29
+
30
+ Run any command through the active Mihomo local proxy:
31
+
32
+ ```bash
33
+ qp-tunnel-cli ./electron-server/scripts/manage.sh redeploy
34
+ qp-tunnel-cli -- docker compose build
35
+ ```
36
+
37
+ For host commands, `HTTP_PROXY` points at `127.0.0.1:<mixed-port>`. For
38
+ Docker/Compose build containers, the CLI also injects container-facing variables
39
+ such as `MARKET_CONTAINER_HTTP_PROXY` and `QP_TUNNEL_CONTAINER_HTTP_PROXY`
40
+ pointing at `host.docker.internal:<mixed-port>`.
26
41
 
27
42
  Install the bundled script as a normal Linux command:
28
43
 
29
44
  ```bash
30
45
  sudo qp-tunnel-cli install-script
46
+ sudo qp-tunnel-cli upgrade-systemd
31
47
  sudo mihomo-client status
32
- sudo mihomo-client tun-on
48
+ sudo mihomo-client server-on
33
49
  ```
34
50
 
35
51
  Use a custom target when needed:
@@ -0,0 +1,10 @@
1
+ ```bash
2
+ npm i -g @qpjoy/tunnel-cli@0.1.1
3
+
4
+ sudo qp-tunnel-cli install-script
5
+ sudo qp-tunnel-cli upgrade-systemd
6
+
7
+ sudo qp-tunnel-cli tun-off
8
+ sudo qp-tunnel-cli server-on
9
+ sudo qp-tunnel-cli status
10
+ ```
package/dist/index.js CHANGED
@@ -10,6 +10,28 @@ const packageRoot = (0, node_path_1.resolve)(__dirname, '..');
10
10
  const bundledClientScript = (0, node_path_1.resolve)(packageRoot, 'resources/mihomo-client.sh');
11
11
  const repoClientScript = (0, node_path_1.resolve)(packageRoot, '../../../scripts/mihomo-client.sh');
12
12
  const defaultInstallTarget = '/usr/local/bin/mihomo-client';
13
+ const defaultMihomoConfigFile = '/etc/mihomo-client/config.yaml';
14
+ const defaultNoProxyEntries = [
15
+ 'localhost',
16
+ '127.0.0.1',
17
+ '::1',
18
+ 'postgres',
19
+ 'market',
20
+ 'db',
21
+ 'redis',
22
+ 'host.docker.internal',
23
+ 'docker.for.mac.host.internal',
24
+ 'docker.for.win.localhost',
25
+ 'kubernetes.docker.internal',
26
+ '10.0.0.0/8',
27
+ '172.16.0.0/12',
28
+ '192.168.0.0/16',
29
+ '100.64.0.0/10',
30
+ '100.88.0.0/16',
31
+ '100.89.0.0/16',
32
+ '100.90.0.0/16',
33
+ '.local',
34
+ ];
13
35
  const clientCommands = new Set([
14
36
  'setup',
15
37
  'install',
@@ -21,6 +43,11 @@ const clientCommands = new Set([
21
43
  'logs',
22
44
  'enable',
23
45
  'disable',
46
+ 'upgrade-systemd',
47
+ 'server-on',
48
+ 'server-off',
49
+ 'egress-on',
50
+ 'egress-off',
24
51
  'proxy-on',
25
52
  'proxy-off',
26
53
  'tun-on',
@@ -45,19 +72,28 @@ Usage:
45
72
  qp-tunnel-cli script-path
46
73
  qp-tunnel-cli client-help
47
74
  qp-tunnel-cli <mihomo-client command> [options]
75
+ qp-tunnel-cli -- <command> [args...]
76
+ qp-tunnel-cli <command-path> [args...]
48
77
 
49
78
  Common commands:
50
79
  qp-tunnel-cli install --url http://IP:3434/peer_user01.mihomo.yaml --user download --password pass
51
80
  qp-tunnel-cli status
52
81
  qp-tunnel-cli start
82
+ qp-tunnel-cli server-on
53
83
  qp-tunnel-cli tun-on
54
84
  qp-tunnel-cli tun-off
55
85
  qp-tunnel-cli update-subscription
56
86
  qp-tunnel-cli uninstall --purge
87
+ qp-tunnel-cli ./electron-server/scripts/manage.sh redeploy
57
88
 
58
89
  The npm package is a thin distributor for the Linux mihomo-client script. Client
59
90
  commands re-run through sudo when needed, then execute the bundled shell script.
60
91
 
92
+ Unknown commands are executed with QPJoy proxy variables injected. Host commands
93
+ receive HTTP_PROXY=http://127.0.0.1:<mixed-port>; Docker/Compose build contexts
94
+ also receive container-facing variables such as MARKET_CONTAINER_HTTP_PROXY and
95
+ QP_TUNNEL_CONTAINER_HTTP_PROXY=http://host.docker.internal:<mixed-port>.
96
+
61
97
  Install the script as a normal server command:
62
98
  sudo qp-tunnel-cli install-script
63
99
  sudo mihomo-client status
@@ -123,6 +159,94 @@ function runClientCommand(scriptArgs) {
123
159
  }
124
160
  runScriptWithoutSudo(scriptArgs);
125
161
  }
162
+ function mixedPortFromConfig() {
163
+ const explicit = process.env.QP_TUNNEL_MIXED_PORT || process.env.MIHOMO_MIXED_PORT;
164
+ if (explicit && /^\d+$/.test(explicit)) {
165
+ return explicit;
166
+ }
167
+ const configFile = process.env.MIHOMO_CONFIG_FILE || defaultMihomoConfigFile;
168
+ if (!(0, node_fs_1.existsSync)(configFile)) {
169
+ process.stderr.write(`Mihomo config not found: ${configFile}\nRun: sudo qp-tunnel-cli install ... && sudo qp-tunnel-cli server-on\n`);
170
+ process.exit(1);
171
+ }
172
+ const content = (0, node_fs_1.readFileSync)(configFile, 'utf8');
173
+ const match = /^\s*mixed-port\s*:\s*(\d+)/m.exec(content);
174
+ if (!match) {
175
+ process.stderr.write(`Could not detect mixed-port from ${configFile}\n`);
176
+ process.exit(1);
177
+ }
178
+ return match[1];
179
+ }
180
+ function mergeCsvValues(...values) {
181
+ const seen = new Set();
182
+ const merged = [];
183
+ for (const value of values) {
184
+ for (const item of (value || '').split(',')) {
185
+ const trimmed = item.trim();
186
+ if (!trimmed || seen.has(trimmed))
187
+ continue;
188
+ seen.add(trimmed);
189
+ merged.push(trimmed);
190
+ }
191
+ }
192
+ return merged.join(',');
193
+ }
194
+ function proxyEnvironment() {
195
+ const port = mixedPortFromConfig();
196
+ const hostProxy = `http://127.0.0.1:${port}`;
197
+ const hostSocksProxy = `socks5://127.0.0.1:${port}`;
198
+ const containerHost = process.env.QP_TUNNEL_CONTAINER_HOST || 'host.docker.internal';
199
+ const containerProxy = `http://${containerHost}:${port}`;
200
+ const noProxy = mergeCsvValues(process.env.NO_PROXY, process.env.no_proxy, defaultNoProxyEntries.join(','));
201
+ const containerNoProxy = mergeCsvValues(process.env.MARKET_CONTAINER_NO_PROXY, process.env.QP_TUNNEL_CONTAINER_NO_PROXY, noProxy);
202
+ return {
203
+ ...process.env,
204
+ HTTP_PROXY: hostProxy,
205
+ HTTPS_PROXY: hostProxy,
206
+ ALL_PROXY: hostSocksProxy,
207
+ http_proxy: hostProxy,
208
+ https_proxy: hostProxy,
209
+ all_proxy: hostSocksProxy,
210
+ NO_PROXY: noProxy,
211
+ no_proxy: noProxy,
212
+ npm_config_proxy: hostProxy,
213
+ npm_config_https_proxy: hostProxy,
214
+ npm_config_noproxy: noProxy,
215
+ pnpm_config_proxy: hostProxy,
216
+ pnpm_config_https_proxy: hostProxy,
217
+ pnpm_config_noproxy: noProxy,
218
+ QP_TUNNEL_MIXED_PORT: port,
219
+ QP_TUNNEL_HOST_HTTP_PROXY: hostProxy,
220
+ QP_TUNNEL_HOST_HTTPS_PROXY: hostProxy,
221
+ QP_TUNNEL_HOST_ALL_PROXY: hostSocksProxy,
222
+ QP_TUNNEL_CONTAINER_HTTP_PROXY: containerProxy,
223
+ QP_TUNNEL_CONTAINER_HTTPS_PROXY: containerProxy,
224
+ QP_TUNNEL_CONTAINER_NO_PROXY: containerNoProxy,
225
+ CONTAINER_HTTP_PROXY: containerProxy,
226
+ CONTAINER_HTTPS_PROXY: containerProxy,
227
+ CONTAINER_NO_PROXY: containerNoProxy,
228
+ BUILD_CONTAINER_HTTP_PROXY: containerProxy,
229
+ BUILD_CONTAINER_HTTPS_PROXY: containerProxy,
230
+ BUILD_CONTAINER_NO_PROXY: containerNoProxy,
231
+ MARKET_CONTAINER_HTTP_PROXY: process.env.MARKET_CONTAINER_HTTP_PROXY || containerProxy,
232
+ MARKET_CONTAINER_HTTPS_PROXY: process.env.MARKET_CONTAINER_HTTPS_PROXY || containerProxy,
233
+ MARKET_CONTAINER_NO_PROXY: containerNoProxy,
234
+ };
235
+ }
236
+ function runExternalCommand(commandArgs) {
237
+ if (commandArgs.length === 0) {
238
+ process.stderr.write('Missing command after qp-tunnel-cli --\n');
239
+ process.exit(1);
240
+ }
241
+ const [rawCommand, ...rawArgs] = commandArgs;
242
+ const command = rawCommand === 'sudo' && !rawArgs.includes('-E') ? 'sudo' : rawCommand;
243
+ const commandArgsWithSudoEnv = rawCommand === 'sudo' && !rawArgs.includes('-E') ? ['-E', ...rawArgs] : rawArgs;
244
+ const result = (0, node_child_process_1.spawnSync)(command, commandArgsWithSudoEnv, {
245
+ stdio: 'inherit',
246
+ env: proxyEnvironment(),
247
+ });
248
+ exitFromSpawn(result);
249
+ }
126
250
  function parseInstallScriptArgs(scriptArgs) {
127
251
  let target = defaultInstallTarget;
128
252
  for (let index = 0; index < scriptArgs.length; index += 1) {
@@ -186,6 +310,9 @@ async function printScriptPath() {
186
310
  }
187
311
  async function main() {
188
312
  const command = args[0] ?? 'help';
313
+ if (command === '--') {
314
+ runExternalCommand(args.slice(1));
315
+ }
189
316
  if (command === 'help' || command === '--help' || command === '-h') {
190
317
  help();
191
318
  return;
@@ -209,9 +336,10 @@ async function main() {
209
336
  if (passthroughCommand && clientCommands.has(passthroughCommand)) {
210
337
  runClientCommand(args);
211
338
  }
212
- process.stderr.write(`Unknown command: ${command}\n`);
339
+ if (args.length > 0) {
340
+ runExternalCommand(args);
341
+ }
213
342
  help();
214
- process.exitCode = 1;
215
343
  }
216
344
  main().catch((error) => {
217
345
  const message = error instanceof Error ? error.message : String(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@qpjoy/tunnel-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Global QPJoy Tunnel CLI for installing and running the Linux mihomo-client script.",
5
5
  "private": false,
6
6
  "type": "commonjs",
@@ -16,6 +16,7 @@ MIHOMO_SERVICE_FILE="${MIHOMO_SERVICE_FILE:-/etc/systemd/system/$MIHOMO_SERVICE_
16
16
  MIHOMO_PROFILE_PROXY_FILE="${MIHOMO_PROFILE_PROXY_FILE:-/etc/profile.d/mihomo-client-proxy.sh}"
17
17
  MIHOMO_DAEMON_PROXY_SERVICES="${MIHOMO_DAEMON_PROXY_SERVICES:-docker.service containerd.service buildkit.service}"
18
18
  MIHOMO_DAEMON_PROXY_DROPIN_NAME="${MIHOMO_DAEMON_PROXY_DROPIN_NAME:-mihomo-proxy.conf}"
19
+ MIHOMO_NO_PROXY="${MIHOMO_NO_PROXY:-localhost,127.0.0.1,::1,10.0.0.0/8,172.16.0.0/12,192.168.0.0/16,100.64.0.0/10,100.88.0.0/16,100.89.0.0/16,100.90.0.0/16,host.docker.internal,.local}"
19
20
  MIHOMO_SSH_PROXY_HELPER="${MIHOMO_SSH_PROXY_HELPER:-/usr/local/bin/mihomo-ssh-proxy}"
20
21
  MIHOMO_SSH_CONFIG_DIR="${MIHOMO_SSH_CONFIG_DIR:-/etc/ssh/ssh_config.d}"
21
22
  MIHOMO_SSH_CONFIG_FILE="${MIHOMO_SSH_CONFIG_FILE:-$MIHOMO_SSH_CONFIG_DIR/99-mihomo-proxy.conf}"
@@ -41,6 +42,11 @@ Commands:
41
42
  logs Show recent service logs
42
43
  enable Enable service on boot
43
44
  disable Disable service on boot
45
+ upgrade-systemd Refresh the installed systemd unit from this script
46
+ server-on Enable persistent server-safe outbound proxy mode, without TUN
47
+ server-off Disable server-safe proxy integrations, keeping the service installed
48
+ egress-on Alias for server-on
49
+ egress-off Alias for server-off
44
50
  proxy-on Write /etc/profile.d proxy exports for login shells
45
51
  proxy-off Remove /etc/profile.d proxy exports
46
52
  tun-on Enable Mihomo TUN mode and also turn proxy-on on
@@ -85,11 +91,13 @@ Examples:
85
91
 
86
92
  sudo bash ./scripts/mihomo-client.sh update-subscription
87
93
  sudo bash ./scripts/mihomo-client.sh start
94
+ sudo bash ./scripts/mihomo-client.sh server-on
88
95
  sudo bash ./scripts/mihomo-client.sh proxy-on
89
96
  sudo bash ./scripts/mihomo-client.sh tun-on
90
97
  sudo bash ./scripts/mihomo-client.sh ssh-proxy-on
91
98
  sudo bash ./scripts/mihomo-client.sh daemon-proxy-on
92
99
  sudo bash ./scripts/mihomo-client.sh run curl -I https://www.google.com/generate_204
100
+ sudo bash ./scripts/mihomo-client.sh run ./electron-server/scripts/manage.sh redeploy
93
101
  sudo bash ./scripts/mihomo-client.sh print-env
94
102
  EOF
95
103
  }
@@ -525,10 +533,10 @@ docker_proxy_env_lines() {
525
533
  cat <<EOF
526
534
  HTTP_PROXY=http://127.0.0.1:$port
527
535
  HTTPS_PROXY=http://127.0.0.1:$port
528
- NO_PROXY=localhost,127.0.0.1,::1
536
+ NO_PROXY=$MIHOMO_NO_PROXY
529
537
  http_proxy=http://127.0.0.1:$port
530
538
  https_proxy=http://127.0.0.1:$port
531
- no_proxy=localhost,127.0.0.1,::1
539
+ no_proxy=$MIHOMO_NO_PROXY
532
540
  EOF
533
541
  }
534
542
 
@@ -589,6 +597,7 @@ status_command() {
589
597
  echo "TUN mode: $([[ -f "$MIHOMO_TUN_OVERLAY_FILE" ]] && echo enabled || echo disabled)"
590
598
  echo "SSH proxy config: $([[ -f "$MIHOMO_SSH_CONFIG_FILE" ]] && echo enabled || echo disabled)"
591
599
  echo "Managed daemon proxy services: ${daemon_services:-none}"
600
+ echo "NO_PROXY: $MIHOMO_NO_PROXY"
592
601
  echo
593
602
  systemctl status "$MIHOMO_SERVICE_NAME" --no-pager || true
594
603
  }
@@ -711,10 +720,10 @@ daemon_proxy_on_command() {
711
720
  [Service]
712
721
  Environment="HTTP_PROXY=http://127.0.0.1:$port"
713
722
  Environment="HTTPS_PROXY=http://127.0.0.1:$port"
714
- Environment="NO_PROXY=localhost,127.0.0.1,::1"
723
+ Environment="NO_PROXY=$MIHOMO_NO_PROXY"
715
724
  Environment="http_proxy=http://127.0.0.1:$port"
716
725
  Environment="https_proxy=http://127.0.0.1:$port"
717
- Environment="no_proxy=localhost,127.0.0.1,::1"
726
+ Environment="no_proxy=$MIHOMO_NO_PROXY"
718
727
  EOF
719
728
  done
720
729
  systemd_reload
@@ -771,17 +780,73 @@ tun_off_command() {
771
780
  fi
772
781
  }
773
782
 
783
+ server_on_command() {
784
+ local was_tun_enabled="false"
785
+ if tun_enabled; then
786
+ was_tun_enabled="true"
787
+ remove_tun_overlay
788
+ render_runtime_config
789
+ fi
790
+ if service_is_active; then
791
+ if [[ "$was_tun_enabled" == "true" ]]; then
792
+ systemctl restart "$MIHOMO_SERVICE_NAME"
793
+ fi
794
+ else
795
+ systemctl start "$MIHOMO_SERVICE_NAME"
796
+ fi
797
+ systemctl enable "$MIHOMO_SERVICE_NAME" >/dev/null 2>&1 || true
798
+ proxy_on_command
799
+ ssh_proxy_on_command
800
+ daemon_proxy_on_command
801
+ echo "Server-safe outbound proxy mode enabled."
802
+ echo "Mihomo stays resident as a local proxy; TUN mode is disabled so public inbound services keep their normal return path."
803
+ }
804
+
805
+ server_off_command() {
806
+ remove_tun_overlay
807
+ render_runtime_config
808
+ proxy_off_command
809
+ ssh_proxy_off_command
810
+ daemon_proxy_off_command
811
+ if service_is_active; then
812
+ systemctl restart "$MIHOMO_SERVICE_NAME"
813
+ fi
814
+ echo "Server-safe outbound proxy integrations disabled. Mihomo service remains installed."
815
+ }
816
+
774
817
  run_command() {
775
- local port
818
+ local port host_proxy host_socks_proxy container_host container_proxy
776
819
  [[ $# -gt 0 ]] || die "Usage: sudo bash ./scripts/mihomo-client.sh run <command> [args...]"
777
820
  port="$(mixed_port_from_config)"
778
821
  [[ -n "$port" ]] || die "Could not detect mixed-port from $MIHOMO_CONFIG_FILE"
779
- http_proxy="http://127.0.0.1:$port" \
780
- https_proxy="http://127.0.0.1:$port" \
781
- HTTP_PROXY="http://127.0.0.1:$port" \
782
- HTTPS_PROXY="http://127.0.0.1:$port" \
783
- all_proxy="socks5://127.0.0.1:$port" \
784
- ALL_PROXY="socks5://127.0.0.1:$port" \
822
+ host_proxy="http://127.0.0.1:$port"
823
+ host_socks_proxy="socks5://127.0.0.1:$port"
824
+ container_host="${QP_TUNNEL_CONTAINER_HOST:-host.docker.internal}"
825
+ container_proxy="http://${container_host}:$port"
826
+ http_proxy="$host_proxy" \
827
+ https_proxy="$host_proxy" \
828
+ HTTP_PROXY="$host_proxy" \
829
+ HTTPS_PROXY="$host_proxy" \
830
+ all_proxy="$host_socks_proxy" \
831
+ ALL_PROXY="$host_socks_proxy" \
832
+ no_proxy="$MIHOMO_NO_PROXY" \
833
+ NO_PROXY="$MIHOMO_NO_PROXY" \
834
+ QP_TUNNEL_MIXED_PORT="$port" \
835
+ QP_TUNNEL_HOST_HTTP_PROXY="$host_proxy" \
836
+ QP_TUNNEL_HOST_HTTPS_PROXY="$host_proxy" \
837
+ QP_TUNNEL_HOST_ALL_PROXY="$host_socks_proxy" \
838
+ QP_TUNNEL_CONTAINER_HTTP_PROXY="$container_proxy" \
839
+ QP_TUNNEL_CONTAINER_HTTPS_PROXY="$container_proxy" \
840
+ QP_TUNNEL_CONTAINER_NO_PROXY="$MIHOMO_NO_PROXY" \
841
+ CONTAINER_HTTP_PROXY="$container_proxy" \
842
+ CONTAINER_HTTPS_PROXY="$container_proxy" \
843
+ CONTAINER_NO_PROXY="$MIHOMO_NO_PROXY" \
844
+ BUILD_CONTAINER_HTTP_PROXY="$container_proxy" \
845
+ BUILD_CONTAINER_HTTPS_PROXY="$container_proxy" \
846
+ BUILD_CONTAINER_NO_PROXY="$MIHOMO_NO_PROXY" \
847
+ MARKET_CONTAINER_HTTP_PROXY="${MARKET_CONTAINER_HTTP_PROXY:-$container_proxy}" \
848
+ MARKET_CONTAINER_HTTPS_PROXY="${MARKET_CONTAINER_HTTPS_PROXY:-$container_proxy}" \
849
+ MARKET_CONTAINER_NO_PROXY="${MARKET_CONTAINER_NO_PROXY:-$MIHOMO_NO_PROXY}" \
785
850
  "$@"
786
851
  }
787
852
 
@@ -852,6 +917,23 @@ install_command() {
852
917
  fi
853
918
  }
854
919
 
920
+ upgrade_systemd_command() {
921
+ ensure_dirs
922
+ load_env
923
+ log "Installing current mihomo-client launcher"
924
+ install_client_launcher
925
+ log "Refreshing systemd service file"
926
+ write_service_file
927
+ systemd_reload
928
+ systemctl enable "$MIHOMO_SERVICE_NAME" >/dev/null 2>&1 || true
929
+ if service_is_active; then
930
+ systemctl restart "$MIHOMO_SERVICE_NAME"
931
+ echo "Mihomo client systemd unit updated and service restarted."
932
+ else
933
+ echo "Mihomo client systemd unit updated. Start it with: systemctl start $MIHOMO_SERVICE_NAME"
934
+ fi
935
+ }
936
+
855
937
  start_command() {
856
938
  systemctl start "$MIHOMO_SERVICE_NAME"
857
939
  }
@@ -972,6 +1054,15 @@ main() {
972
1054
  disable)
973
1055
  disable_command
974
1056
  ;;
1057
+ upgrade-systemd)
1058
+ upgrade_systemd_command
1059
+ ;;
1060
+ server-on|egress-on)
1061
+ server_on_command
1062
+ ;;
1063
+ server-off|egress-off)
1064
+ server_off_command
1065
+ ;;
975
1066
  proxy-on)
976
1067
  proxy_on_command
977
1068
  ;;