proxyctl 0.3.0__tar.gz → 0.3.2__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.
- {proxyctl-0.3.0 → proxyctl-0.3.2}/PKG-INFO +1 -1
- {proxyctl-0.3.0 → proxyctl-0.3.2}/pyproject.toml +1 -1
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/__init__.py +1 -1
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/audit.py +1 -1
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/check.py +1 -1
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/cli.py +13 -3
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/core/plugin.py +18 -1
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/trace.py +4 -4
- {proxyctl-0.3.0 → proxyctl-0.3.2}/.gitignore +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/LICENSE +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/README.md +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/man/proxyctl.1 +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/_io.py +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/builtin_plugins/__init__.py +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/builtin_plugins/connectivity_basic.py +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/builtin_plugins/corp_network.py +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/completion.py +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/core/__init__.py +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/engine/__init__.py +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/engine/base.py +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/engine/mihomo.py +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/engine/singbox.py +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/explain.py +0 -0
- {proxyctl-0.3.0 → proxyctl-0.3.2}/src/proxyctl/status.py +0 -0
|
@@ -439,7 +439,7 @@ def cmd_audit(audit_days: int, api_base: str, api_secret: str, do_apply: bool):
|
|
|
439
439
|
if candidates and not do_apply:
|
|
440
440
|
print(f"\n执行 {BOLD}proxyctl audit apply{NC} 自动写入双 config")
|
|
441
441
|
|
|
442
|
-
_audit_emit(as_json, _sys, _real_stdout, collector)
|
|
442
|
+
_audit_emit(as_json, as_plain, _sys, _real_stdout, collector)
|
|
443
443
|
|
|
444
444
|
|
|
445
445
|
def _audit_emit(as_json: bool, as_plain: bool, _sys, _real_stdout,
|
|
@@ -944,7 +944,7 @@ def cmd_check(engine, api: str, api_secret: str,
|
|
|
944
944
|
"detail": "skipped_in_structured_modes"},
|
|
945
945
|
{"stage": "connectivity",
|
|
946
946
|
"ok": all(c.get("ok") for c in conn) if conn else False,
|
|
947
|
-
"detail": ";".join(f"{c.get('
|
|
947
|
+
"detail": ";".join(f"{c.get('name')}={'ok' if c.get('ok') else 'X'}"
|
|
948
948
|
for c in conn)},
|
|
949
949
|
{"stage": "outbound_ip",
|
|
950
950
|
"ok": bool(out_ip),
|
|
@@ -1230,6 +1230,7 @@ def cmd_dns_unlock(config: dict):
|
|
|
1230
1230
|
print(f"{YELLOW}dns-unlock 仅支持 macOS{NC}")
|
|
1231
1231
|
return
|
|
1232
1232
|
dns_lock_label = config.get("dns_lock_label", DEFAULTS["dns_lock_label"])
|
|
1233
|
+
dns_lock_plist = f"/Library/LaunchDaemons/{dns_lock_label}.plist"
|
|
1233
1234
|
|
|
1234
1235
|
r = run(["launchctl", "bootout", f"system/{dns_lock_label}"], sudo=True, capture=True)
|
|
1235
1236
|
if r.returncode == 0:
|
|
@@ -1361,7 +1362,16 @@ def _read_log_lines(path: str, tail_n: int | None) -> list:
|
|
|
1361
1362
|
|
|
1362
1363
|
# ── 帮助 ──────────────────────────────────────────────────────────────────────
|
|
1363
1364
|
|
|
1364
|
-
|
|
1365
|
+
def _read_version() -> str:
|
|
1366
|
+
"""单一事实来源:pyproject.toml 的 [project] version。"""
|
|
1367
|
+
try:
|
|
1368
|
+
from importlib.metadata import version
|
|
1369
|
+
return version("proxyctl")
|
|
1370
|
+
except Exception:
|
|
1371
|
+
return "unknown"
|
|
1372
|
+
|
|
1373
|
+
|
|
1374
|
+
VERSION = _read_version()
|
|
1365
1375
|
|
|
1366
1376
|
|
|
1367
1377
|
# Help 输出按 group 分块的顺序(避免依赖 dict 插入顺序变化)
|
|
@@ -1714,7 +1724,7 @@ def _plan_mode(backend, target: str) -> list[dict]:
|
|
|
1714
1724
|
"reversible": True,
|
|
1715
1725
|
"side_effects": ["config-write"]},
|
|
1716
1726
|
{"action": "subprocess",
|
|
1717
|
-
"target": f"launchctl kickstart -k
|
|
1727
|
+
"target": f"launchctl kickstart -k {backend.label}",
|
|
1718
1728
|
"summary": f"重启 launchd 服务以读取新 mode",
|
|
1719
1729
|
"reversible": True, "requires_sudo": True,
|
|
1720
1730
|
"side_effects": ["process"]},
|
|
@@ -1731,7 +1741,7 @@ def _plan_engine(backend, target: str) -> list[dict]:
|
|
|
1731
1741
|
new_plist = f"/Library/LaunchDaemons/<{target}>.plist"
|
|
1732
1742
|
return [
|
|
1733
1743
|
{"action": "subprocess",
|
|
1734
|
-
"target": f"launchctl bootout
|
|
1744
|
+
"target": f"launchctl bootout {backend.label}",
|
|
1735
1745
|
"summary": f"停止当前引擎 {backend.name}",
|
|
1736
1746
|
"reversible": True, "requires_sudo": True,
|
|
1737
1747
|
"side_effects": ["process"]},
|
|
@@ -148,6 +148,20 @@ class Plugin:
|
|
|
148
148
|
return []
|
|
149
149
|
|
|
150
150
|
|
|
151
|
+
# ── 内部工具 ─────────────────────────────────────────────────────────────────
|
|
152
|
+
|
|
153
|
+
def _apply_color_policy(modname: str) -> None:
|
|
154
|
+
"""插件模块加载后立刻调用:若当前是关色态,把模块自定义的 RED/GREEN/...
|
|
155
|
+
常量抹空。解决用户插件(如 sb_private.py)在 cli.main() 的 set_no_color
|
|
156
|
+
之后加载,导致硬编码 ANSI 字面量泄漏到管道输出的问题。"""
|
|
157
|
+
try:
|
|
158
|
+
from proxyctl import _io
|
|
159
|
+
_io.maybe_disable_module_colors(modname)
|
|
160
|
+
except Exception:
|
|
161
|
+
# 插件加载链路不应被色彩策略阻断
|
|
162
|
+
pass
|
|
163
|
+
|
|
164
|
+
|
|
151
165
|
# ── Registry ─────────────────────────────────────────────────────────────────
|
|
152
166
|
|
|
153
167
|
class PluginRegistry:
|
|
@@ -171,7 +185,9 @@ class PluginRegistry:
|
|
|
171
185
|
if modname.startswith("_"):
|
|
172
186
|
continue
|
|
173
187
|
try:
|
|
174
|
-
|
|
188
|
+
full_name = f"proxyctl.builtin_plugins.{modname}"
|
|
189
|
+
mod = importlib.import_module(full_name)
|
|
190
|
+
_apply_color_policy(full_name)
|
|
175
191
|
self._discover_in_module(mod, config, source=f"builtin/{modname}")
|
|
176
192
|
except Exception as e:
|
|
177
193
|
self.errors.append((f"builtin/{modname}", _fmt_err(e)))
|
|
@@ -192,6 +208,7 @@ class PluginRegistry:
|
|
|
192
208
|
mod = importlib.util.module_from_spec(spec)
|
|
193
209
|
sys.modules[mod_name] = mod
|
|
194
210
|
spec.loader.exec_module(mod)
|
|
211
|
+
_apply_color_policy(mod_name)
|
|
195
212
|
self._discover_in_module(mod, config, source=f"user/{fname}")
|
|
196
213
|
except Exception as e:
|
|
197
214
|
self.errors.append((f"user/{fname}", _fmt_err(e)))
|
|
@@ -575,11 +575,12 @@ def cmd_trace(raw_input: str, api: str, secret: str, config: dict = None):
|
|
|
575
575
|
}
|
|
576
576
|
|
|
577
577
|
t.join()
|
|
578
|
-
lines,
|
|
578
|
+
lines, remote_ip = connectivity_result[0]
|
|
579
579
|
for line in lines:
|
|
580
580
|
print(line)
|
|
581
581
|
collector["stages"]["connectivity"] = {
|
|
582
|
-
"ok": bool(
|
|
582
|
+
"ok": bool(remote_ip),
|
|
583
|
+
"remote_ip": remote_ip,
|
|
583
584
|
"lines": [_strip_ansi(line) for line in lines],
|
|
584
585
|
}
|
|
585
586
|
|
|
@@ -588,8 +589,7 @@ def cmd_trace(raw_input: str, api: str, secret: str, config: dict = None):
|
|
|
588
589
|
if as_json:
|
|
589
590
|
_sys.stdout = _real_stdout
|
|
590
591
|
from proxyctl._io import emit_json, envelope, OK
|
|
591
|
-
emit_json(envelope("trace", data=collector,
|
|
592
|
-
ok=bool(conn_ok), code=OK))
|
|
592
|
+
emit_json(envelope("trace", data=collector, ok=True, code=OK))
|
|
593
593
|
_sys.exit(0)
|
|
594
594
|
|
|
595
595
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|