proxyctl 0.4.2__tar.gz → 0.4.3__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 (24) hide show
  1. {proxyctl-0.4.2 → proxyctl-0.4.3}/PKG-INFO +1 -1
  2. {proxyctl-0.4.2 → proxyctl-0.4.3}/man/proxyctl.1 +1 -1
  3. {proxyctl-0.4.2 → proxyctl-0.4.3}/pyproject.toml +1 -1
  4. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/check.py +50 -2
  5. {proxyctl-0.4.2 → proxyctl-0.4.3}/.gitignore +0 -0
  6. {proxyctl-0.4.2 → proxyctl-0.4.3}/LICENSE +0 -0
  7. {proxyctl-0.4.2 → proxyctl-0.4.3}/README.md +0 -0
  8. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/__init__.py +0 -0
  9. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/_io.py +0 -0
  10. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/audit.py +0 -0
  11. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/builtin_plugins/__init__.py +0 -0
  12. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/builtin_plugins/connectivity_basic.py +0 -0
  13. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/builtin_plugins/corp_network.py +0 -0
  14. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/cli.py +0 -0
  15. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/completion.py +0 -0
  16. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/core/__init__.py +0 -0
  17. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/core/plugin.py +0 -0
  18. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/engine/__init__.py +0 -0
  19. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/engine/base.py +0 -0
  20. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/engine/mihomo.py +0 -0
  21. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/engine/singbox.py +0 -0
  22. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/explain.py +0 -0
  23. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/status.py +0 -0
  24. {proxyctl-0.4.2 → proxyctl-0.4.3}/src/proxyctl/trace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxyctl
3
- Version: 0.4.2
3
+ Version: 0.4.3
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,4 +1,4 @@
1
- .TH PROXYCTL 1 "2026-05" "proxyctl 0.4.2" "User Commands"
1
+ .TH PROXYCTL 1 "2026-05" "proxyctl 0.4.3" "User Commands"
2
2
  .SH NAME
3
3
  proxyctl \- Proxy configuration lifecycle management for macOS / Linux
4
4
  .SH SYNOPSIS
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "proxyctl"
3
- version = "0.4.2"
3
+ version = "0.4.3"
4
4
  description = "Proxy configuration lifecycle management for macOS and Linux"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -532,6 +532,52 @@ def _fetch_probe(probe, env_clean: dict, proxy_port: int = 7890) -> str:
532
532
  return text
533
533
 
534
534
 
535
+ def _collect_fail_hints(collector: dict, *, dns_bad: bool, failed: bool) -> list[str]:
536
+ """从 check 的 collector 提取每个失败 stage 的简短摘要。
537
+
538
+ 设计目标:envelope.ok=False 时 agent 不需要挖 stages.*.ok 自行定位真凶。
539
+ 本函数为 cmd_check 在 emit_json 之前调用,输出 hints 列表。
540
+
541
+ Args:
542
+ collector: cmd_check 沉淀的事实快照(见 cmd_check.collector)
543
+ dns_bad: basic.network.dns 不健康
544
+ failed: 整体是否任一 stage fail(fail 变量真值)
545
+
546
+ Returns:
547
+ 摘要 hint 行列表。整体 pass 时返回空列表。
548
+ """
549
+ hints: list[str] = []
550
+ if not failed:
551
+ return hints
552
+
553
+ stages = collector.get("stages") or {}
554
+ basic = stages.get("basic") or {}
555
+ ports = basic.get("ports") or {}
556
+ fail_ports = ports.get("fail") or []
557
+ if fail_ports:
558
+ hints.append(f"missing ports: {' '.join(fail_ports)}")
559
+
560
+ if basic.get("daemon_up") is False:
561
+ hints.append("engine not running")
562
+
563
+ # connectivity: 列表形式,每项 {name, url, ok, message}
564
+ conn = stages.get("connectivity") or []
565
+ failed_conns = [c.get("name", "?") for c in conn
566
+ if isinstance(c, dict) and not c.get("ok")]
567
+ if failed_conns:
568
+ hints.append(f"connectivity failed: {','.join(failed_conns)}")
569
+
570
+ split = stages.get("split_routing")
571
+ if isinstance(split, dict) and split.get("ok") is False:
572
+ hints.append("split routing inactive (proxy == direct egress)")
573
+
574
+ if dns_bad:
575
+ # 保留旧行为:DNS 异常时引导 proxyctl fix
576
+ hints.append("DNS unhealthy — try `proxyctl fix`")
577
+
578
+ return hints
579
+
580
+
535
581
  def cmd_check(engine, api: str, api_secret: str,
536
582
  config: dict, mode_str: str = "", registry=None):
537
583
  """proxyctl check — 4 阶段全面健康检查。
@@ -923,11 +969,13 @@ def cmd_check(engine, api: str, api_secret: str,
923
969
  if as_json:
924
970
  _sys.stdout = _real_stdout
925
971
  from proxyctl._io import emit_json, envelope, OK, GENERIC
926
- hint = "proxyctl fix" if dns_bad else None
972
+ # 失败时聚合每个失败 stage 的简短摘要到 hints。
973
+ # 不让 agent 在 envelope.ok=False 时还要挖 stages.*.ok 才定位真凶。
974
+ hints = _collect_fail_hints(collector, dns_bad=dns_bad, failed=fail)
927
975
  emit_json(envelope("check", data=collector,
928
976
  ok=(not fail),
929
977
  code=OK if not fail else GENERIC,
930
- hint=hint))
978
+ hints=hints))
931
979
  _sys.exit(0 if not fail else 1)
932
980
  if as_plain:
933
981
  _sys.stdout = _real_stdout
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes