proxyctl 0.5.4__tar.gz → 0.5.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.
- {proxyctl-0.5.4 → proxyctl-0.5.6}/PKG-INFO +1 -1
- {proxyctl-0.5.4 → proxyctl-0.5.6}/man/proxyctl.1 +27 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/pyproject.toml +1 -1
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/_io.py +2 -1
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/check.py +42 -17
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/cli.py +34 -9
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/completion.py +7 -6
- proxyctl-0.5.6/src/proxyctl/connections.py +908 -0
- proxyctl-0.5.6/src/proxyctl/connections_filters.py +317 -0
- proxyctl-0.5.6/src/proxyctl/connections_human.py +239 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/explain.py +59 -7
- proxyctl-0.5.6/src/proxyctl/traffic.py +984 -0
- proxyctl-0.5.6/src/proxyctl/traffic_store.py +257 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/.gitignore +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/LICENSE +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/README.md +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/__init__.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/audit.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/autostart.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/builtin_plugins/__init__.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/builtin_plugins/connectivity_basic.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/builtin_plugins/corp_network.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/core/__init__.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/core/plugin.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/engine/__init__.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/engine/base.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/engine/mihomo.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/engine/singbox.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/status.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/subscription.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/suggest.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/suggest_rules.py +0 -0
- {proxyctl-0.5.4 → proxyctl-0.5.6}/src/proxyctl/trace.py +0 -0
|
@@ -131,6 +131,33 @@ Linux systemctl --user)。
|
|
|
131
131
|
4 阶段健康检查:basic / groups / connectivity / outbound-ip。
|
|
132
132
|
\fB--plain\fR 输出 TSV(stage / ok / detail)。
|
|
133
133
|
.TP
|
|
134
|
+
\fBproxyctl connections\fR [\fB--host\fR \fITEXT\fR ...] [\fB--chain\fR \fITEXT\fR ...] [\fB--route\fR \fIKIND\fR ...] [\fB--preset\fR \fINAME\fR ...] [\fB--agent\fR \fINAME\fR ...] [\fB--query\fR \fITEXT\fR ...] [\fB--app\fR \fINAME\fR ...] [\fB--all\fR] [\fB--json\fR]
|
|
135
|
+
把本机 App/进程的 TCP 连接与 \fIproxy_port\fR、mihomo \fB/connections\fR
|
|
136
|
+
API 的 \fIsourcePort\fR 连接详情关联。默认不做 AI 工具过滤,输出全部可关联
|
|
137
|
+
目的站点;\fB--host\fR 按 host/rulePayload/IP 过滤,\fB--chain\fR /
|
|
138
|
+
\fB--line\fR 按 Mihomo 链路节点过滤,\fB--route\fR 按 route_kind 过滤
|
|
139
|
+
(proxy/direct/reject/unknown,也接受 代理/直连/拒绝/未知),\fB--preset\fR
|
|
140
|
+
使用预设(如 ai/openai/anthropic/coding),\fB--agent\fR 按工具过滤(如
|
|
141
|
+
codex/claude/chatgpt/openclaw),\fB--query\fR / \fB--filter\fR 做全文过滤。
|
|
142
|
+
\fB--app\fR 保留兼容,用于进程/App 过滤;\fB--all\fR 为兼容 no-op。
|
|
143
|
+
macOS 上还会用 \fBnetstat -anv\fR 反查被
|
|
144
|
+
Network/System Extension 接管后的 socket owner,并在 JSON 中输出
|
|
145
|
+
\fBproxy_owner_connections\fR 与 \fBproxy_owner_groups\fR。后者按
|
|
146
|
+
rulePayload / host / IP 聚合目的地,输出 app_contexts / candidate_contexts,
|
|
147
|
+
并标出 mixed_chains / mixed_route_kind。
|
|
148
|
+
只读;非 mihomo 后端或本地 API 不可用时降级输出 unmatched。
|
|
149
|
+
.TP
|
|
150
|
+
\fBproxyctl traffic\fR [snapshot|sample|watch|report] [\fB--by\fR \fIline,app\fR] [\fB--interval\fR \fISEC\fR] [\fB--count\fR \fIN\fR] [\fB--since\fR \fI1h\fR] [\fB--store\fR \fIDIR\fR] [\fB--host\fR \fITEXT\fR ...] [\fB--chain\fR \fITEXT\fR ...] [\fB--route\fR \fIKIND\fR ...] [\fB--preset\fR \fINAME\fR ...] [\fB--agent\fR \fINAME\fR ...] [\fB--json\fR]
|
|
151
|
+
按当前活跃 Mihomo \fB/connections\fR 的 upload/download 统计流量快照。
|
|
152
|
+
默认 \fB--by line\fR 按首个链路节点统计线路;也可用
|
|
153
|
+
\fB--by line,app\fR 进一步按软件归因。软件归因分可信度:
|
|
154
|
+
可见进程为 process/socket-owner,被 Network/System Extension 遮蔽时按目的域名
|
|
155
|
+
给出 inferred 候选。\fBsample\fR 记录一次连接计数器增量,
|
|
156
|
+
\fBwatch\fR 按间隔重复记录,\fBreport\fR 读取已记录增量并按
|
|
157
|
+
\fB--since\fR 汇总。人类输出模式下 \fBwatch\fR 每次采样都会实时打印一行进度;
|
|
158
|
+
\fB--json\fR 保持最终单个 envelope。首次看到的连接只建立基线,不回填此前已经
|
|
159
|
+
产生的字节;未运行 sample/watch 的时间段无法补算。
|
|
160
|
+
.TP
|
|
134
161
|
\fBproxyctl trace\fR \fIDOMAIN\fR [\fB--json\fR]
|
|
135
162
|
4 阶段域名链路诊断:DNS 解析 / 规则匹配预测 / 连通性 / 实际连接。
|
|
136
163
|
.TP
|
|
@@ -72,6 +72,7 @@ _COLOR_NAMES = ("RED", "GREEN", "YELLOW", "CYAN", "BOLD", "DIM", "NC")
|
|
|
72
72
|
_COLOR_MODULES_KNOWN = (
|
|
73
73
|
"proxyctl.cli", "proxyctl.status", "proxyctl.check",
|
|
74
74
|
"proxyctl.trace", "proxyctl.audit", "proxyctl.explain",
|
|
75
|
+
"proxyctl.connections", "proxyctl.connections_human", "proxyctl.traffic",
|
|
75
76
|
)
|
|
76
77
|
|
|
77
78
|
|
|
@@ -398,7 +399,7 @@ def lock_paths(lock_dir: str | None = None) -> dict[str, str]:
|
|
|
398
399
|
home = os.path.expanduser("~")
|
|
399
400
|
base = lock_dir or os.path.join(home, ".config", "proxyctl")
|
|
400
401
|
# 已知锁名(与 cli._exec_with_lock 一致)
|
|
401
|
-
names = ["system", "config", "daemon"]
|
|
402
|
+
names = ["system", "config", "daemon", "traffic"]
|
|
402
403
|
return {n: os.path.join(base, f".lock.{n}") for n in names}
|
|
403
404
|
|
|
404
405
|
|
|
@@ -251,17 +251,9 @@ def _proxy_groups_section(api_base: str, api_secret: str,
|
|
|
251
251
|
print(f" {CYAN}{gname}{NC}({gtype}) → {BOLD}{gnow}{NC} "
|
|
252
252
|
f"{ds(gnow_d)}ms [{count_str} of {total}]{tested_str}")
|
|
253
253
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
sub = (proxies.get(gnow) if g.get("type") in ("Selector", "Fallback")
|
|
258
|
-
else None)
|
|
259
|
-
now_is_subgroup = bool(sub and sub.get("all"))
|
|
260
|
-
if g.get("type") in ("Selector", "Fallback") and not now_is_subgroup:
|
|
261
|
-
print_members(gmembers, gnow)
|
|
262
|
-
elif g.get("type") not in ("Selector", "Fallback"):
|
|
263
|
-
print_members(gmembers, gnow)
|
|
264
|
-
if now_is_subgroup:
|
|
254
|
+
def print_sub_summary(sub_name: str, sub: dict, is_now: bool) -> bool:
|
|
255
|
+
"""打印一行子组 summary(含 → now 标记 + 健康度 + tested ago)。
|
|
256
|
+
返回该子组之前是否已展开过叶子。"""
|
|
265
257
|
sub_type = "url" if sub.get("type") == "URLTest" else "sel"
|
|
266
258
|
sub_now = sub.get("now", "?")
|
|
267
259
|
sub_now_d = get_delay(sub_now)
|
|
@@ -275,13 +267,46 @@ def _proxy_groups_section(api_base: str, api_secret: str,
|
|
|
275
267
|
if s_nodata: sc.append(f"{YELLOW}{s_nodata}—{NC}")
|
|
276
268
|
st = group_tested_ago(sub_members)
|
|
277
269
|
st_str = f" {DIM}{st}{NC}" if st else ""
|
|
278
|
-
already =
|
|
270
|
+
already = sub_name in expanded_subgroups
|
|
279
271
|
tail = f" {DIM}(详见上方){NC}" if already else ""
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
272
|
+
marker = "→" if is_now else " "
|
|
273
|
+
print(f" {marker}{CYAN}{sub_name}{NC}({sub_type}) → "
|
|
274
|
+
f"{BOLD}{sub_now}{NC} "
|
|
275
|
+
f"{ds(sub_now_d)}ms [{'/'.join(sc)} of {len(sub_members)}]"
|
|
276
|
+
f"{st_str}{tail}")
|
|
277
|
+
return already
|
|
278
|
+
|
|
279
|
+
# v0.5.5:selector/fallback 含子组时,每个子组都打一行 summary(让
|
|
280
|
+
# 用户看到兄弟子组存在 + 健康度),但只对 now 子组展开叶子(保留
|
|
281
|
+
# v0.5.3 精简意图)。混在子组里的真节点(如 local-N)按 4 列表格
|
|
282
|
+
# flush。selector/fallback 全为真节点 / 其他类型组:原 print_members
|
|
283
|
+
# 行为不变。
|
|
284
|
+
if g.get("type") in ("Selector", "Fallback"):
|
|
285
|
+
has_subgroup = any(proxies.get(m, {}).get("all") for m in gmembers)
|
|
286
|
+
if not has_subgroup:
|
|
287
|
+
print_members(gmembers, gnow)
|
|
288
|
+
else:
|
|
289
|
+
leaf_buf: list = []
|
|
290
|
+
|
|
291
|
+
def flush_leaves():
|
|
292
|
+
if leaf_buf:
|
|
293
|
+
print_members(leaf_buf, gnow)
|
|
294
|
+
leaf_buf.clear()
|
|
295
|
+
|
|
296
|
+
for m in gmembers:
|
|
297
|
+
sub = proxies.get(m, {})
|
|
298
|
+
if sub.get("all"):
|
|
299
|
+
flush_leaves()
|
|
300
|
+
already = print_sub_summary(m, sub, is_now=(m == gnow))
|
|
301
|
+
if m == gnow and not already:
|
|
302
|
+
print_members(sub.get("all", []),
|
|
303
|
+
sub.get("now", "?"))
|
|
304
|
+
expanded_subgroups.add(m)
|
|
305
|
+
else:
|
|
306
|
+
leaf_buf.append(m)
|
|
307
|
+
flush_leaves()
|
|
308
|
+
else:
|
|
309
|
+
print_members(gmembers, gnow)
|
|
285
310
|
# 顶层组本身展开完后也标记,避免被下层组重新展开
|
|
286
311
|
expanded_subgroups.add(gname)
|
|
287
312
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
支持 Mihomo 后端(首发)/ Sing-box 后端(预留)
|
|
4
4
|
|
|
5
|
-
用法:proxyctl [start|stop|restart|status|log|check|
|
|
6
|
-
mode|audit|trace|bench|dns-lock|dns-unlock]
|
|
5
|
+
用法:proxyctl [start|stop|restart|status|log|check|connections|traffic|
|
|
6
|
+
fix|recover|mode|audit|trace|bench|dns-lock|dns-unlock]
|
|
7
7
|
"""
|
|
8
8
|
|
|
9
9
|
import functools
|
|
@@ -70,8 +70,11 @@ def load_config() -> dict:
|
|
|
70
70
|
user_cfg = yaml.safe_load(f) or {}
|
|
71
71
|
cfg.update(user_cfg)
|
|
72
72
|
except Exception as e:
|
|
73
|
-
print(f"{YELLOW}警告:读取配置文件失败:{e}{NC}")
|
|
74
|
-
print(
|
|
73
|
+
print(f"{YELLOW}警告:读取配置文件失败:{e}{NC}", file=sys.stderr)
|
|
74
|
+
print(
|
|
75
|
+
f" 使用默认配置,可能需要在 {CONFIG_FILE} 中配置 api_secret",
|
|
76
|
+
file=sys.stderr,
|
|
77
|
+
)
|
|
75
78
|
return cfg
|
|
76
79
|
|
|
77
80
|
|
|
@@ -1501,8 +1504,17 @@ def cmd_help():
|
|
|
1501
1504
|
f"{DIM}一键 JSON + 关色 + 非交互{NC}")
|
|
1502
1505
|
print()
|
|
1503
1506
|
|
|
1507
|
+
print(f"{BOLD}常用示例{NC}")
|
|
1508
|
+
print(f" {CYAN}proxyctl doctor --json{NC} "
|
|
1509
|
+
f"{DIM}最快健康基线,agent 首选{NC}")
|
|
1510
|
+
print(f" {CYAN}proxyctl mode tun -n --json{NC} "
|
|
1511
|
+
f"{DIM}预览写操作 plan,不执行{NC}")
|
|
1512
|
+
print(f" {CYAN}proxyctl help trace{NC} "
|
|
1513
|
+
f"{DIM}查看单命令用法和示例{NC}")
|
|
1514
|
+
print()
|
|
1515
|
+
|
|
1504
1516
|
print(f"{BOLD}用法{NC} proxyctl <command> [args] "
|
|
1505
|
-
f"[--json|--plain] [--dry-run] [--no-color] [--quiet]")
|
|
1517
|
+
f"[--json|--plain] [--dry-run|-n] [--no-color] [--quiet]")
|
|
1506
1518
|
print()
|
|
1507
1519
|
|
|
1508
1520
|
# 按 group 元数据驱动渲染
|
|
@@ -1533,7 +1545,7 @@ def cmd_help():
|
|
|
1533
1545
|
print(f"{BOLD}全局 flag{NC}")
|
|
1534
1546
|
print(f" --json 输出 envelope JSON(schema v2)")
|
|
1535
1547
|
print(f" --plain 输出纯 TSV(audit/check 等支持表格的命令)")
|
|
1536
|
-
print(f" --dry-run
|
|
1548
|
+
print(f" --dry-run/-n 预演写操作(输出 plan,不真正执行)")
|
|
1537
1549
|
print(f" --no-color 关闭 ANSI(也读 NO_COLOR)")
|
|
1538
1550
|
print(f" --quiet/-q 压制非关键 stderr")
|
|
1539
1551
|
print(f" --help/-h 本帮助")
|
|
@@ -1647,6 +1659,7 @@ def cmd_version_print() -> None:
|
|
|
1647
1659
|
"lock_path_in_error": True,
|
|
1648
1660
|
"side_effects_enum": True,
|
|
1649
1661
|
"dry_run": True,
|
|
1662
|
+
"dry_run_short_flag": True,
|
|
1650
1663
|
"plain": True,
|
|
1651
1664
|
"flag_position_invariant": True,
|
|
1652
1665
|
"agents_md": True,
|
|
@@ -1667,6 +1680,8 @@ def cmd_version_print() -> None:
|
|
|
1667
1680
|
"doctor_since_filter": True, # 0.5.0: --since <version> 屏蔽未来规则
|
|
1668
1681
|
"suggestions_ignore_file": True, # 0.5.0: ~/.config/proxyctl/suggestions.ignore
|
|
1669
1682
|
"proxy_group_dead_check": True, # 0.5.0: proxy_group.mostly_dead 规则
|
|
1683
|
+
"traffic_snapshot": True, # 0.5.6+: active /connections traffic grouping
|
|
1684
|
+
"traffic_history": True, # 0.5.6+: sample/watch/report recorded deltas
|
|
1670
1685
|
},
|
|
1671
1686
|
}
|
|
1672
1687
|
_io.emit_json(_io.envelope("version", data=data))
|
|
@@ -1751,7 +1766,7 @@ def _extract_global_flags(argv: list) -> tuple:
|
|
|
1751
1766
|
"""从 argv 中剥离全局 flag,返回 (剩余 argv, flag dict)。
|
|
1752
1767
|
|
|
1753
1768
|
clig.dev 原则:flag 位置无关 — 在任意位置出现的 --json / --no-color /
|
|
1754
|
-
--quiet / -q / --dry-run / --plain 都会被识别并剥离,剩余位置参数顺序保持不变。
|
|
1769
|
+
--quiet / -q / --dry-run / -n / --plain 都会被识别并剥离,剩余位置参数顺序保持不变。
|
|
1755
1770
|
"""
|
|
1756
1771
|
flags = {
|
|
1757
1772
|
"json": False, "no_color": False, "quiet": False,
|
|
@@ -1765,7 +1780,7 @@ def _extract_global_flags(argv: list) -> tuple:
|
|
|
1765
1780
|
flags["no_color"] = True
|
|
1766
1781
|
elif a in ("--quiet", "-q"):
|
|
1767
1782
|
flags["quiet"] = True
|
|
1768
|
-
elif a
|
|
1783
|
+
elif a in ("--dry-run", "-n"):
|
|
1769
1784
|
flags["dry_run"] = True
|
|
1770
1785
|
elif a == "--plain":
|
|
1771
1786
|
flags["plain"] = True
|
|
@@ -2732,6 +2747,14 @@ def _h_check(ctx):
|
|
|
2732
2747
|
cmd_check(ctx["backend"], ctx["api_base"], ctx["api_secret"],
|
|
2733
2748
|
ctx["config"], mode_str, registry=ctx["registry"])
|
|
2734
2749
|
|
|
2750
|
+
def _h_connections(ctx):
|
|
2751
|
+
from proxyctl.connections import cmd_connections
|
|
2752
|
+
cmd_connections(ctx["args"], ctx["backend"], ctx["config"])
|
|
2753
|
+
|
|
2754
|
+
def _h_traffic(ctx):
|
|
2755
|
+
from proxyctl.traffic import cmd_traffic
|
|
2756
|
+
cmd_traffic(ctx["args"], ctx["backend"], ctx["config"])
|
|
2757
|
+
|
|
2735
2758
|
def _h_bench(ctx):
|
|
2736
2759
|
from proxyctl.check import cmd_bench
|
|
2737
2760
|
bench_groups = ctx["args"] or None
|
|
@@ -2898,6 +2921,8 @@ DISPATCH: dict = {
|
|
|
2898
2921
|
"status": _h_status,
|
|
2899
2922
|
"log": _h_log,
|
|
2900
2923
|
"check": _h_check,
|
|
2924
|
+
"connections": _h_connections,
|
|
2925
|
+
"traffic": _h_traffic,
|
|
2901
2926
|
"bench": _h_bench,
|
|
2902
2927
|
"fix": _h_fix,
|
|
2903
2928
|
"recover": _h_recover,
|
|
@@ -2965,4 +2990,4 @@ def _suggest_command_and_exit(unknown: str) -> None:
|
|
|
2965
2990
|
|
|
2966
2991
|
|
|
2967
2992
|
if __name__ == "__main__":
|
|
2968
|
-
main()
|
|
2993
|
+
main()
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
0.3.x 起补全:
|
|
5
5
|
|
|
6
6
|
- 全局 flag: --help / --version / --json / --no-color / --quiet
|
|
7
|
-
/ --dry-run / --plain
|
|
7
|
+
/ --dry-run / -n / --plain
|
|
8
8
|
- explain <topic> → topic 列表
|
|
9
9
|
- mode <tun|proxy> → 模式
|
|
10
10
|
- engine <mihomo|singbox> → 引擎
|
|
@@ -66,7 +66,7 @@ _proxyctl_complete() {{
|
|
|
66
66
|
|
|
67
67
|
# 第二个 token = 子命令
|
|
68
68
|
if [ ${{COMP_CWORD}} -eq 1 ]; then
|
|
69
|
-
COMPREPLY=( $(compgen -W "${{cmds}} help --help --version --json --plain --dry-run --no-color --quiet" -- "${{cur}}") )
|
|
69
|
+
COMPREPLY=( $(compgen -W "${{cmds}} help --help --version --json --plain --dry-run -n --no-color --quiet" -- "${{cur}}") )
|
|
70
70
|
return 0
|
|
71
71
|
fi
|
|
72
72
|
|
|
@@ -133,7 +133,7 @@ _proxyctl_complete() {{
|
|
|
133
133
|
local dry_run_cmds=({dry_run_cmds})
|
|
134
134
|
for c in "${{dry_run_cmds[@]}}"; do
|
|
135
135
|
if [ "$first" = "$c" ]; then
|
|
136
|
-
COMPREPLY=( $(compgen -W "--dry-run --json --no-color --quiet --help" -- "${{cur}}") )
|
|
136
|
+
COMPREPLY=( $(compgen -W "--dry-run -n --json --no-color --quiet --help" -- "${{cur}}") )
|
|
137
137
|
return 0
|
|
138
138
|
fi
|
|
139
139
|
done
|
|
@@ -142,7 +142,7 @@ _proxyctl_complete() {{
|
|
|
142
142
|
local plain_cmds=({plain_cmds})
|
|
143
143
|
for c in "${{plain_cmds[@]}}"; do
|
|
144
144
|
if [ "$first" = "$c" ]; then
|
|
145
|
-
COMPREPLY=( $(compgen -W "--plain --json --dry-run --help" -- "${{cur}}") )
|
|
145
|
+
COMPREPLY=( $(compgen -W "--plain --json --dry-run -n --help" -- "${{cur}}") )
|
|
146
146
|
return 0
|
|
147
147
|
fi
|
|
148
148
|
done
|
|
@@ -160,7 +160,7 @@ _proxyctl_complete() {{
|
|
|
160
160
|
fi
|
|
161
161
|
|
|
162
162
|
# 默认 flag 补全
|
|
163
|
-
COMPREPLY=( $(compgen -W "--help --json --plain --dry-run --no-color --quiet" -- "${{cur}}") )
|
|
163
|
+
COMPREPLY=( $(compgen -W "--help --json --plain --dry-run -n --no-color --quiet" -- "${{cur}}") )
|
|
164
164
|
}}
|
|
165
165
|
complete -F _proxyctl_complete proxyctl
|
|
166
166
|
"""
|
|
@@ -204,6 +204,7 @@ _proxyctl() {{
|
|
|
204
204
|
'--json[结构化 envelope v2 输出]' \\
|
|
205
205
|
'--plain[纯 TSV 输出(audit/check);与 --json 互斥]' \\
|
|
206
206
|
'--dry-run[预演写命令;输出 data.plan]' \\
|
|
207
|
+
'-n[预演写命令;输出 data.plan]' \\
|
|
207
208
|
'--no-color[关闭 ANSI 颜色]' \\
|
|
208
209
|
'--quiet[安静模式]' \\
|
|
209
210
|
'-q[安静模式]'
|
|
@@ -258,7 +259,7 @@ def _gen_fish() -> str:
|
|
|
258
259
|
"complete -c proxyctl -l version -d '显示版本'",
|
|
259
260
|
"complete -c proxyctl -l json -d 'envelope v2 输出'",
|
|
260
261
|
"complete -c proxyctl -l plain -d 'TSV 输出(audit/check)'",
|
|
261
|
-
"complete -c proxyctl -l dry-run -d '预演写命令'",
|
|
262
|
+
"complete -c proxyctl -s n -l dry-run -d '预演写命令'",
|
|
262
263
|
"complete -c proxyctl -l no-color -d '关闭 ANSI'",
|
|
263
264
|
"complete -c proxyctl -l quiet -d '安静模式'",
|
|
264
265
|
""]
|