proxyctl 0.5.3__tar.gz → 0.5.4__tar.gz

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.
Files changed (28) hide show
  1. {proxyctl-0.5.3 → proxyctl-0.5.4}/PKG-INFO +1 -1
  2. {proxyctl-0.5.3 → proxyctl-0.5.4}/pyproject.toml +1 -1
  3. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/cli.py +29 -22
  4. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/explain.py +25 -6
  5. {proxyctl-0.5.3 → proxyctl-0.5.4}/.gitignore +0 -0
  6. {proxyctl-0.5.3 → proxyctl-0.5.4}/LICENSE +0 -0
  7. {proxyctl-0.5.3 → proxyctl-0.5.4}/README.md +0 -0
  8. {proxyctl-0.5.3 → proxyctl-0.5.4}/man/proxyctl.1 +0 -0
  9. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/__init__.py +0 -0
  10. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/_io.py +0 -0
  11. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/audit.py +0 -0
  12. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/autostart.py +0 -0
  13. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/builtin_plugins/__init__.py +0 -0
  14. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/builtin_plugins/connectivity_basic.py +0 -0
  15. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/builtin_plugins/corp_network.py +0 -0
  16. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/check.py +0 -0
  17. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/completion.py +0 -0
  18. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/core/__init__.py +0 -0
  19. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/core/plugin.py +0 -0
  20. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/engine/__init__.py +0 -0
  21. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/engine/base.py +0 -0
  22. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/engine/mihomo.py +0 -0
  23. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/engine/singbox.py +0 -0
  24. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/status.py +0 -0
  25. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/subscription.py +0 -0
  26. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/suggest.py +0 -0
  27. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/suggest_rules.py +0 -0
  28. {proxyctl-0.5.3 → proxyctl-0.5.4}/src/proxyctl/trace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxyctl
3
- Version: 0.5.3
3
+ Version: 0.5.4
4
4
  Summary: Proxy configuration lifecycle management for macOS and Linux
5
5
  Project-URL: Homepage, https://github.com/crhan/proxyctl
6
6
  Project-URL: Issues, https://github.com/crhan/proxyctl/issues
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "proxyctl"
3
- version = "0.5.3"
3
+ version = "0.5.4"
4
4
  description = "Proxy configuration lifecycle management for macOS and Linux"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -2010,32 +2010,39 @@ def _plan_engine(backend, target: str) -> list[dict]:
2010
2010
 
2011
2011
 
2012
2012
  def _plan_fix(backend, config) -> list[dict]:
2013
- """fix 的 plan
2013
+ """fix 的 plan(v0.5.4:按平台分支)。
2014
+
2015
+ Linux:cmd_fix 仅走 Clash API 热重载,不改系统 DNS(systemd-resolved
2016
+ 上的 127.0.0.53 stub 不该被 networksetup 替换)。plan 同步只出 http_put。
2017
+ macOS:保留 DNS 重写 + 缓存清理 + 热重载三步。
2014
2018
 
2015
2019
  networksetup / scutil 迭代型操作不可逐条 argv 化(每个网络服务调一次),
2016
- 标 action=system_op + 描述性 target。http_put 是单次精确请求,target
2017
- 为完整 URL。cmd_fix 实际还会调插件 route_hooks + 第二次 curl 清 fakeip,
2018
- contract test 不做 subprocess 子集校验(plugin 可注入),只校验
2019
- placeholder 已消除(< / > 字符不出现)。
2020
+ 标 action=system_op + 描述性 target。contract test 不做 subprocess 子集
2021
+ 校验(plugin 可注入),只校验 placeholder 已消除。
2020
2022
  """
2021
2023
  api_base = config.get('api_base', 'http://127.0.0.1:9090')
2022
- return [
2023
- {"action": "system_op",
2024
- "target": "networksetup -setdnsservers (per service) 127.0.0.1",
2025
- "summary": "重置系统 DNS 指向 127.0.0.1(对抗 DHCP 续租)",
2026
- "reversible": True, "requires_sudo": True,
2027
- "side_effects": ["system"]},
2028
- {"action": "system_op",
2029
- "target": "scutil DNS reset + dscacheutil -flushcache",
2030
- "summary": "清 macOS DNS 缓存(含 fakeip 表)",
2031
- "reversible": True, "requires_sudo": True,
2032
- "side_effects": ["cache"]},
2033
- {"action": "http_put",
2034
- "target": f"{api_base}/configs?force=true",
2035
- "summary": "向 Clash API 发热重载请求(不重启进程)",
2036
- "reversible": True,
2037
- "side_effects": ["network-io"]},
2038
- ]
2024
+ plan = []
2025
+ if IS_MACOS:
2026
+ plan.extend([
2027
+ {"action": "system_op",
2028
+ "target": "networksetup -setdnsservers (per service) 127.0.0.1",
2029
+ "summary": "重置系统 DNS 指向 127.0.0.1(对抗 DHCP 续租)",
2030
+ "reversible": True, "requires_sudo": True,
2031
+ "side_effects": ["system"]},
2032
+ {"action": "system_op",
2033
+ "target": "scutil DNS reset + dscacheutil -flushcache",
2034
+ "summary": "清 macOS DNS 缓存(含 fakeip 表)",
2035
+ "reversible": True, "requires_sudo": True,
2036
+ "side_effects": ["cache"]},
2037
+ ])
2038
+ plan.append({
2039
+ "action": "http_put",
2040
+ "target": f"{api_base}/configs?force=true",
2041
+ "summary": "向 Clash API 发热重载请求(不重启进程)",
2042
+ "reversible": True,
2043
+ "side_effects": ["network-io"],
2044
+ })
2045
+ return plan
2039
2046
 
2040
2047
 
2041
2048
  def _plan_audit_apply(days: int, backend, config: dict) -> list[dict]:
@@ -1544,6 +1544,12 @@ def cmd_doctor(args: list, backend, config) -> None:
1544
1544
  port = config.get("proxy_port", 7890)
1545
1545
  api_base = config.get("api_base", "http://127.0.0.1:9090")
1546
1546
 
1547
+ # mode 先取(dns_ok 判定 + 显示均依赖;v0.5.4 起)
1548
+ try:
1549
+ mode_str = get_mode(backend)
1550
+ except Exception:
1551
+ mode_str = "unknown"
1552
+
1547
1553
  if suggest_only:
1548
1554
  # Fast path:跳过所有 5 项 score 探测(最慢可累计 5-10s)
1549
1555
  engine_up = None
@@ -1560,7 +1566,7 @@ def cmd_doctor(args: list, backend, config) -> None:
1560
1566
  else:
1561
1567
  engine_up = bool(service_running(backend))
1562
1568
  port_listen = _tcp_open("127.0.0.1", port)
1563
- dns_ok = _dns_points_to_loopback()
1569
+ dns_ok = _dns_check_ok(mode_str)
1564
1570
  system_proxy_ok = _system_proxy_points_to_loopback(port)
1565
1571
  connectivity_ok = (_quick_connectivity(api_base, port)
1566
1572
  if engine_up else False)
@@ -1575,10 +1581,6 @@ def cmd_doctor(args: list, backend, config) -> None:
1575
1581
  hint = _doctor_hint(flags_for_human)
1576
1582
 
1577
1583
  # informational extra(不计分)
1578
- try:
1579
- mode_str = get_mode(backend)
1580
- except Exception:
1581
- mode_str = "unknown"
1582
1584
  try:
1583
1585
  held = _io.held_lock_names()
1584
1586
  except Exception:
@@ -1643,7 +1645,9 @@ def cmd_doctor(args: list, backend, config) -> None:
1643
1645
  f"{DIM}engine={backend.name}{ev_tag} mode={mode_str} port={port}{NC}")
1644
1646
  print(f" {icon(engine_up)} engine_up ({backend.name} 服务运行中)")
1645
1647
  print(f" {icon(port_listen)} port_listen (127.0.0.1:{port})")
1646
- print(f" {icon(dns_ok)} dns_ok (系统 DNS 127.0.0.1)")
1648
+ dns_desc = ("proxy 模式无需 DNS 劫持" if mode_str == "proxy"
1649
+ else "系统 DNS 含 127.0.0.1")
1650
+ print(f" {icon(dns_ok)} dns_ok ({dns_desc})")
1647
1651
  print(f" {icon(system_proxy_ok)} system_proxy_ok (macOS HTTP/HTTPS proxy)")
1648
1652
  print(f" {icon(connectivity_ok)} connectivity_ok (https://www.google.com via proxy)")
1649
1653
  if held:
@@ -1765,6 +1769,21 @@ def _dns_points_to_loopback() -> bool:
1765
1769
  return False
1766
1770
 
1767
1771
 
1772
+ def _dns_check_ok(mode: str) -> bool:
1773
+ """doctor dns_ok 判定(v0.5.4:mode-aware)。
1774
+
1775
+ proxy 模式无需系统 DNS 指向 127.0.0.1——流量走 HTTP/SOCKS 代理,
1776
+ mihomo 不在 :53 监听,系统 DNS 用 systemd-resolved / DHCP 默认即可。
1777
+ 旧逻辑在 Ubuntu(DNS=127.0.0.53 stub)+ proxy 模式下永远判 ✗
1778
+ 并建议 `proxyctl fix`——但 fix 的 DNS 改写只对 macOS TUN 模式有意义。
1779
+
1780
+ tun / mixed / 未知模式:保留旧的"系统 DNS 含 127.0.0.1"判定。
1781
+ """
1782
+ if mode == "proxy":
1783
+ return True
1784
+ return _dns_points_to_loopback()
1785
+
1786
+
1768
1787
  def _system_proxy_points_to_loopback(port: int) -> bool:
1769
1788
  """macOS:networksetup -getwebproxy 是否指向 127.0.0.1:port。Linux:跳过判 True。"""
1770
1789
  if sys.platform != "darwin":
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes