proxyctl 0.4.4__tar.gz → 0.4.6__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 (25) hide show
  1. {proxyctl-0.4.4 → proxyctl-0.4.6}/PKG-INFO +2 -2
  2. {proxyctl-0.4.4 → proxyctl-0.4.6}/README.md +1 -1
  3. {proxyctl-0.4.4 → proxyctl-0.4.6}/man/proxyctl.1 +1 -1
  4. {proxyctl-0.4.4 → proxyctl-0.4.6}/pyproject.toml +1 -1
  5. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/explain.py +99 -25
  6. {proxyctl-0.4.4 → proxyctl-0.4.6}/.gitignore +0 -0
  7. {proxyctl-0.4.4 → proxyctl-0.4.6}/LICENSE +0 -0
  8. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/__init__.py +0 -0
  9. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/_io.py +0 -0
  10. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/audit.py +0 -0
  11. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/builtin_plugins/__init__.py +0 -0
  12. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/builtin_plugins/connectivity_basic.py +0 -0
  13. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/builtin_plugins/corp_network.py +0 -0
  14. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/check.py +0 -0
  15. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/cli.py +0 -0
  16. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/completion.py +0 -0
  17. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/core/__init__.py +0 -0
  18. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/core/plugin.py +0 -0
  19. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/engine/__init__.py +0 -0
  20. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/engine/base.py +0 -0
  21. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/engine/mihomo.py +0 -0
  22. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/engine/singbox.py +0 -0
  23. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/status.py +0 -0
  24. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/subscription.py +0 -0
  25. {proxyctl-0.4.4 → proxyctl-0.4.6}/src/proxyctl/trace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: proxyctl
3
- Version: 0.4.4
3
+ Version: 0.4.6
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
@@ -193,7 +193,7 @@ uv tool install proxyctl # uv(推荐)
193
193
  pipx install proxyctl # 或 pipx
194
194
  pip install --user proxyctl # 或 pip
195
195
 
196
- proxyctl --version # → proxyctl v0.4.3
196
+ proxyctl --version # → proxyctl v0.4.6
197
197
  proxyctl --help
198
198
  ```
199
199
 
@@ -164,7 +164,7 @@ uv tool install proxyctl # uv(推荐)
164
164
  pipx install proxyctl # 或 pipx
165
165
  pip install --user proxyctl # 或 pip
166
166
 
167
- proxyctl --version # → proxyctl v0.4.3
167
+ proxyctl --version # → proxyctl v0.4.6
168
168
  proxyctl --help
169
169
  ```
170
170
 
@@ -1,4 +1,4 @@
1
- .TH PROXYCTL 1 "2026-05" "proxyctl 0.4.4" "User Commands"
1
+ .TH PROXYCTL 1 "2026-05" "proxyctl 0.4.6" "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.4"
3
+ version = "0.4.6"
4
4
  description = "Proxy configuration lifecycle management for macOS and Linux"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -74,15 +74,16 @@ def _t_rules(backend, config) -> TopicCard:
74
74
  def _t_nodes(backend, config) -> TopicCard:
75
75
  return {
76
76
  "topic": "nodes",
77
- "summary": "代理节点(线路)— 出口节点和分组定义;订阅由 mihomo/sing-box 自身管理。",
77
+ "summary": "代理节点(线路)— 出口节点和分组定义;订阅由 mihomo/sing-box 或用户脚本管理(详见 explain subscription)。",
78
78
  "file": f"{backend.config_file} [proxies: / proxy-providers: / proxy-groups: 段]",
79
79
  "edit": (
80
80
  " # 添加单个节点:在 proxies: 段加 entry,再加到 proxy-groups: 的某个组\n"
81
- " # 订阅源:用 mihomo 自身的 proxy-providers: + url + path + interval\n"
82
- " # proxyctl 不管订阅更新;用 mihomo 的 'proxy-providers' 热更新机制"
81
+ " # 订阅源:用 mihomo 自身的 proxy-providers: + url + path + interval;\n"
82
+ " # 或用户脚本拉订阅写 config + 写 ~/.config/proxyctl/subscription.json(v0.4.4+)\n"
83
+ " # proxyctl 不发起订阅拉取;但显示订阅状态(见 explain subscription)"
83
84
  ),
84
85
  "verify": "proxyctl bench <group> # 测节点延迟",
85
- "next_commands": ["bench", "explain engine"],
86
+ "next_commands": ["bench", "explain subscription", "explain engine"],
86
87
  }
87
88
 
88
89
 
@@ -118,7 +119,9 @@ def _t_engine(backend, config) -> TopicCard:
118
119
  "topic": "engine",
119
120
  "summary": (
120
121
  f"代理引擎(后端)。当前:{backend.name}。"
121
- "支持 mihomo / sing-box;通过 proxyctl engine <name> 切换后端,"
122
+ "支持 mihomo(首发,端到端验证)/ sing-box(预留,未端到端验证 — "
123
+ "类 / 路径 / audit / trace 解析已实现,但完整启停闭环未跑过生产);"
124
+ "通过 proxyctl engine <name> 切换后端,"
122
125
  "通过 proxyctl mode <tun|proxy> 切换流量入站方式。"
123
126
  ),
124
127
  "file": f"{backend.config_file}",
@@ -281,26 +284,51 @@ def _t_subscription(backend, config) -> TopicCard:
281
284
  return {
282
285
  "topic": "subscription",
283
286
  "summary": (
284
- "节点订阅更新边界:proxyctl 不更新订阅。"
285
- "订阅由 mihomo / sing-box 自身的 proxy-providers 管,"
286
- "或用户用 Clash API 主动 PUT 触发。"
287
+ "订阅边界(双重立场):"
288
+ "(1) proxyctl 不更新订阅 — 拉新节点由用户脚本或引擎自身的 proxy-providers 负责。"
289
+ "(2) v0.4.4 起 proxyctl 显示订阅状态(到期日 / 已用流量 / 拉取健康度)— "
290
+ "通过读取 ~/.config/proxyctl/subscription.json 契约文件,"
291
+ "由用户脚本每次拉订阅后写入。proxyctl 自身不发起任何网络请求。"
292
+ ),
293
+ "file": (
294
+ f"{backend.config_file} [节点 / 订阅源仍由用户管]\n"
295
+ f"~/.config/proxyctl/subscription.json [订阅状态契约文件,proxyctl 读、用户脚本写]"
287
296
  ),
288
- "file": f"{backend.config_file} [proxy-providers: 段]",
289
297
  "edit": (
290
- " # mihomo / sing-box 内置:\n"
298
+ " # === 拉订阅 / 加节点(proxyctl 不做)===\n"
299
+ " # 选项 A: 写用户脚本 cron 拉订阅,参考仓库 update-subscription.sh\n"
300
+ " # 选项 B: 用 mihomo / sing-box 内置 proxy-providers:\n"
291
301
  " # proxy-providers:\n"
292
302
  " # myprovider:\n"
293
303
  " # type: http\n"
294
304
  " # url: https://...\n"
295
- " # interval: 86400 # 自动每 24h 更新\n"
305
+ " # interval: 86400\n"
306
+ " # 选项 C: 手动 Clash API PUT 触发刷新:\n"
307
+ " # curl -X PUT -H 'Authorization: Bearer <api_secret>' \\\n"
308
+ " # http://127.0.0.1:9090/providers/proxies/myprovider\n"
296
309
  " #\n"
297
- " # 手动触发:\n"
298
- " # curl -X PUT \\\n"
299
- " # -H 'Authorization: Bearer <api_secret>' \\\n"
300
- " # http://127.0.0.1:9090/providers/proxies/myprovider"
310
+ " # === 让 proxyctl 显示订阅状态(v0.4.4+)===\n"
311
+ " # 用户脚本拉完订阅后写入契约文件:\n"
312
+ " # ~/.config/proxyctl/subscription.json (schema v1)\n"
313
+ " # 关键字段 (全部可选,缺失 → None):\n"
314
+ " # fetch_ok / fetch_http_status / fetch_error\n"
315
+ " # expire_at / expire_days_left\n"
316
+ " # traffic_used_bytes / traffic_total_bytes / traffic_used_pct\n"
317
+ " # info_nodes / node_count\n"
318
+ " # 详细 schema 见 proxyctl.subscription 模块 docstring。\n"
319
+ " # 失败时也要写(fetch_ok=false + fetch_error),让 proxyctl 能区分\n"
320
+ " # 「过期」vs「网络挂」vs「订阅服务方挂」。"
321
+ ),
322
+ "verify": (
323
+ "proxyctl status # 末尾 SUBSCRIPTION 段显示\n"
324
+ "proxyctl status --json | jq .data.subscription # agent 消费"
301
325
  ),
302
- "verify": "proxyctl bench # 看新节点是否参与测速",
303
- "next_commands": ["bench", "explain nodes", "log --tail 50 --no-follow"],
326
+ "next_commands": [
327
+ "status",
328
+ "status --json | jq .data.subscription",
329
+ "bench",
330
+ "explain nodes",
331
+ ],
304
332
  }
305
333
 
306
334
 
@@ -644,9 +672,11 @@ def _build_agent_guide(backend, config) -> str:
644
672
  lock_dir = os.path.join(os.path.expanduser("~"), ".config", "proxyctl")
645
673
  return f"""# proxyctl — Agent 接入指南(runtime / v0.3)
646
674
 
647
- > 一句话:proxyctl 是 macOS(含 Linux 部分支持)的代理 *生命周期管理* CLI。
675
+ > 一句话:proxyctl 是 macOS + Linux 的代理 *生命周期管理* CLI。
648
676
  > 它管「启停 / 状态 / 健康检查 / DNS 防护 / 配置切换」,**不装 mihomo、
649
- > 不改规则、不改订阅** —— 这些去引擎自己的配置文件里改。
677
+ > 不改规则、不更新订阅** —— 这些去引擎自己的配置文件里改。
678
+ > v0.4.4 起,proxyctl **显示**订阅状态(到期日 / 已用流量 / 拉取健康度),
679
+ > 通过用户脚本写入的契约文件读取 —— 详见下方 `Subscription Status` 段。
650
680
  >
651
681
  > 本文档由 `proxyctl agent-guide` 在运行时输出,含当前 backend/路径/端口。
652
682
  > 仓库视角(开发/贡献协议)见仓库根 `AGENTS.md`。
@@ -680,12 +710,53 @@ Step 6 proxyctl explain <topic> # 深入概念(topic 见下)
680
710
  ## Exclusions — 不能做什么(去别处改)
681
711
 
682
712
  - 添加 / 修改 / 删除分流规则 → 编辑 `{mcfg}` 的 `rules:` 段
683
- - 添加节点 / 改订阅 → 编辑 `{mcfg}` 的 `proxies:` / `proxy-providers:` 段
684
- - 触发订阅刷新 → mihomo `proxy-providers.interval` 自动 / Clash API 手动;
685
- proxyctl **不管订阅更新**(见 `proxyctl explain subscription`)
713
+ - 添加节点 / 改订阅源 → 编辑 `{mcfg}` 的 `proxies:` / `proxy-providers:` 段
714
+ - **更新订阅 / 拉新节点** → mihomo `proxy-providers.interval` 自动 / Clash API 手动 /
715
+ 用户自己写脚本(参考仓库 `update-subscription.sh`);proxyctl 自己不发起网络拉取。
716
+ **但 proxyctl 会显示订阅状态**(v0.4.4+),见 `Subscription Status` 段。
686
717
  - 安装 mihomo / sing-box → `brew install mihomo` 等
687
718
  - 重启第三方应用 → 浏览器 / Slack / VSCode 需用户自己重启读 system proxy
688
719
 
720
+ ## Subscription Status — 订阅状态展示(v0.4.4+)
721
+
722
+ proxyctl **不更新订阅**,但 **会显示订阅状态**——通过读取契约文件
723
+ `~/.config/proxyctl/subscription.json`(schema v1)。
724
+
725
+ ### Agent 怎么用
726
+
727
+ ```bash
728
+ proxyctl status --json | jq .data.subscription
729
+ # 关键字段(全部可选):
730
+ # fetch_ok / fetch_http_status / fetch_error (拉取健康度)
731
+ # expire_at / expire_days_left (套餐到期)
732
+ # traffic_used_bytes / traffic_total_bytes / traffic_used_pct (流量)
733
+ # info_nodes / node_count / url_host
734
+
735
+ proxyctl status --json | jq .hints
736
+ # 风险摘要也会进 envelope.hints[]:
737
+ # 过期 ≤ 7 天 → "subscription expires in Nd ..."
738
+ # 过期已发生 → "subscription EXPIRED Nd ago ..." (critical)
739
+ # 流量 ≥ 80% → "subscription traffic at X% ..."
740
+ # 流量 ≥ 100% → "subscription traffic exhausted ..." (critical)
741
+ # fetch_ok=false → "subscription fetch failed: <error>" (critical)
742
+ ```
743
+
744
+ ### 谁来写契约文件
745
+
746
+ **用户脚本**(不是 proxyctl)。proxyctl 自己不发起任何网络请求 / 不解析订阅 URL。
747
+ 本仓库 `update-subscription.sh` 是参考实现:拉两个订阅 → 解析 Subscription-Userinfo
748
+ HTTP header → 写出 subscription.json。**任何符合 schema v1 的脚本都行**。
749
+
750
+ 成功或失败都要写:fetch_ok=false 时也填出 fetch_error / fetch_http_status,
751
+ proxyctl 才能区分「过期 / 网络挂 / 订阅服务方挂」。
752
+
753
+ ### 探测 capability
754
+
755
+ `proxyctl --version --json` → `data.supported_features.status_subscription = true`
756
+ (0.4.4+)。`false` 或缺失字段表示老版本,agent 应忽略 `data.subscription`。
757
+
758
+ 详见 `proxyctl explain subscription`。
759
+
689
760
  ## Concept Map — "想改 X 去哪"
690
761
 
691
762
  | 想改 | 文件 | 段 / 字段 |
@@ -883,10 +954,13 @@ COMMANDS_META: list[dict] = [
883
954
  "examples": ["proxyctl restart-clean",
884
955
  "proxyctl restart-clean --dry-run"]},
885
956
  # diagnostic
886
- {"name": "status", "group": "diagnostic", "summary": "系统状态面板",
957
+ {"name": "status", "group": "diagnostic",
958
+ "summary": "系统状态面板(含订阅状态:到期/流量/拉取健康度,v0.4.4+)",
887
959
  "args": [], "supports_json": True, "side_effects": [],
888
960
  "needs_sudo": False, "interactive": False, "exit_codes": [0, 5],
889
- "examples": ["proxyctl status", "proxyctl status --json"]},
961
+ "examples": ["proxyctl status",
962
+ "proxyctl status --json",
963
+ "proxyctl status --json | jq .data.subscription"]},
890
964
  {"name": "doctor", "group": "diagnostic", "summary": "极简 5 项健康打分(最快)",
891
965
  "args": [], "supports_json": True, "side_effects": [],
892
966
  "needs_sudo": False, "interactive": False, "exit_codes": [0, 5],
@@ -925,7 +999,7 @@ COMMANDS_META: list[dict] = [
925
999
  "supports_json": False, "side_effects": ["config-write"],
926
1000
  "supports_dry_run": True,
927
1001
  "needs_sudo": True, "interactive": False,
928
- "exit_codes": [0, 1, 2, 4, 6, 8],
1002
+ "exit_codes": [0, 1, 2],
929
1003
  "examples": ["proxyctl mode tun", "proxyctl mode tun --dry-run"]},
930
1004
  {"name": "engine", "group": "config", "summary": "切换代理引擎后端",
931
1005
  "args": [{"name": "target", "choices": ["mihomo", "singbox"], "required": False}],
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes