proxyctl 0.5.7__tar.gz → 0.5.8__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 (33) hide show
  1. {proxyctl-0.5.7 → proxyctl-0.5.8}/PKG-INFO +1 -1
  2. {proxyctl-0.5.7 → proxyctl-0.5.8}/pyproject.toml +1 -1
  3. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/check.py +25 -7
  4. {proxyctl-0.5.7 → proxyctl-0.5.8}/.gitignore +0 -0
  5. {proxyctl-0.5.7 → proxyctl-0.5.8}/LICENSE +0 -0
  6. {proxyctl-0.5.7 → proxyctl-0.5.8}/README.md +0 -0
  7. {proxyctl-0.5.7 → proxyctl-0.5.8}/man/proxyctl.1 +0 -0
  8. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/__init__.py +0 -0
  9. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/_io.py +0 -0
  10. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/audit.py +0 -0
  11. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/autostart.py +0 -0
  12. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/builtin_plugins/__init__.py +0 -0
  13. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/builtin_plugins/connectivity_basic.py +0 -0
  14. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/builtin_plugins/corp_network.py +0 -0
  15. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/cli.py +0 -0
  16. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/completion.py +0 -0
  17. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/connections.py +0 -0
  18. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/connections_filters.py +0 -0
  19. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/connections_human.py +0 -0
  20. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/core/__init__.py +0 -0
  21. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/core/plugin.py +0 -0
  22. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/engine/__init__.py +0 -0
  23. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/engine/base.py +0 -0
  24. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/engine/mihomo.py +0 -0
  25. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/engine/singbox.py +0 -0
  26. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/explain.py +0 -0
  27. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/status.py +0 -0
  28. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/subscription.py +0 -0
  29. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/suggest.py +0 -0
  30. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/suggest_rules.py +0 -0
  31. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/trace.py +0 -0
  32. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/traffic.py +0 -0
  33. {proxyctl-0.5.7 → proxyctl-0.5.8}/src/proxyctl/traffic_store.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxyctl
3
- Version: 0.5.7
3
+ Version: 0.5.8
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.7"
3
+ version = "0.5.8"
4
4
  description = "Proxy configuration lifecycle management for macOS and Linux"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -458,10 +458,13 @@ def _ipgeo(ip: str, cache_file: str, api_secret: str,
458
458
  env = {k: v for k, v in os.environ.items()
459
459
  if k not in ("http_proxy", "https_proxy", "all_proxy",
460
460
  "HTTP_PROXY", "HTTPS_PROXY", "ALL_PROXY")}
461
+ # Geo lookup is metadata lookup for an already-known IP; it should not be
462
+ # routed through the proxy under test. In particular, users may route
463
+ # ipinfo.io itself through the claude group, so a claude outage must not
464
+ # hide the direct/proxy geo labels.
461
465
  r = subprocess.run(
462
- ["curl", "-s", "--max-time", "6",
463
- "--proxy", f"socks5h://127.0.0.1:{proxy_port}",
464
- f"https://ipinfo.io/{ip}/json"],
466
+ ["curl", "-s", "--max-time", "6", "--noproxy", "*",
467
+ f"http://ip-api.com/json/{ip}?fields=status,countryCode,city,isp,org,query"],
465
468
  capture_output=True, text=True, env=env, timeout=10
466
469
  )
467
470
  if not r.stdout:
@@ -469,9 +472,9 @@ def _ipgeo(ip: str, cache_file: str, api_secret: str,
469
472
  try:
470
473
  d = json.loads(r.stdout)
471
474
  city = d.get("city", "")
472
- country = d.get("country", "")
473
- org = d.get("org", "")
474
- if org:
475
+ country = d.get("countryCode") or d.get("country", "")
476
+ org = d.get("isp") or d.get("org", "")
477
+ if org and _re_mod.match(r"^AS\d+\s+", org):
475
478
  parts = org.split(" ", 1)
476
479
  org = parts[1] if len(parts) > 1 else org
477
480
  loc = ",".join(filter(None, [city, country]))
@@ -1123,9 +1126,24 @@ def cmd_check(engine, api: str, api_secret: str,
1123
1126
  cache_file = os.path.join(sb_dir, ".ipgeo-cache")
1124
1127
  if not probes:
1125
1128
  print(f" {YELLOW}—{NC} 无出口探测项")
1129
+ geo_results: dict[str, str] = {p.name: "" for p in probes}
1130
+ geo_threads = [
1131
+ threading.Thread(
1132
+ target=lambda p=probe: geo_results.__setitem__(
1133
+ p.name,
1134
+ _ipgeo(probe_ips.get(p.name, ""), cache_file, api_secret,
1135
+ proxy_port=proxy_port),
1136
+ )
1137
+ )
1138
+ for probe in probes
1139
+ ]
1140
+ for t in geo_threads:
1141
+ t.start()
1142
+ for t in geo_threads:
1143
+ t.join()
1126
1144
  for probe in probes:
1127
1145
  ip = probe_ips.get(probe.name, "")
1128
- geo = _ipgeo(ip, cache_file, api_secret, proxy_port=proxy_port)
1146
+ geo = geo_results.get(probe.name, "")
1129
1147
  print(f" {probe.name:<7s}{_fmt_ip(ip, geo)}")
1130
1148
  collector["stages"]["outbound_ip"][probe.name] = {
1131
1149
  "ip": ip, "geo": geo, "mode": getattr(probe, "mode", ""),
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes