chattool 7.1.2__tar.gz → 7.1.4__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 (111) hide show
  1. {chattool-7.1.2/src/chattool.egg-info → chattool-7.1.4}/PKG-INFO +30 -4
  2. {chattool-7.1.2 → chattool-7.1.4}/README.md +22 -0
  3. {chattool-7.1.2 → chattool-7.1.4}/pyproject.toml +8 -3
  4. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/__init__.py +1 -3
  5. chattool-7.1.4/src/chattool/client/main.py +202 -0
  6. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/config/__init__.py +0 -2
  7. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/config/elements.py +0 -2
  8. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/mcp/catalog.py +1 -7
  9. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/mcp/server.py +1 -3
  10. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/__init__.py +0 -2
  11. {chattool-7.1.2 → chattool-7.1.4/src/chattool.egg-info}/PKG-INFO +30 -4
  12. {chattool-7.1.2 → chattool-7.1.4}/src/chattool.egg-info/SOURCES.txt +0 -6
  13. {chattool-7.1.2 → chattool-7.1.4}/src/chattool.egg-info/requires.txt +5 -1
  14. chattool-7.1.2/src/chattool/client/main.py +0 -105
  15. chattool-7.1.2/src/chattool/config/zulip.py +0 -31
  16. chattool-7.1.2/src/chattool/tools/zulip/__init__.py +0 -3
  17. chattool-7.1.2/src/chattool/tools/zulip/cli.py +0 -539
  18. chattool-7.1.2/src/chattool/tools/zulip/client.py +0 -222
  19. chattool-7.1.2/src/chattool/tools/zulip/legacy.py +0 -354
  20. chattool-7.1.2/src/chattool/tools/zulip/mcp.py +0 -140
  21. {chattool-7.1.2 → chattool-7.1.4}/LICENSE +0 -0
  22. {chattool-7.1.2 → chattool-7.1.4}/MANIFEST.in +0 -0
  23. {chattool-7.1.2 → chattool-7.1.4}/scripts/migrate_chattool_envs_to_chatarch.py +0 -0
  24. {chattool-7.1.2 → chattool-7.1.4}/setup.cfg +0 -0
  25. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/_all.py +0 -0
  26. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/client/__init__.py +0 -0
  27. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/client/cert_client.py +0 -0
  28. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/client/svg2gif_client.py +0 -0
  29. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/config/aliyun.py +0 -0
  30. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/config/azure.py +0 -0
  31. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/config/base.py +0 -0
  32. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/config/browser.py +0 -0
  33. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/config/crs.py +0 -0
  34. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/config/openai.py +0 -0
  35. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/config/skills.py +0 -0
  36. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/config/tencent.py +0 -0
  37. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/config/tplink.py +0 -0
  38. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/const.py +0 -0
  39. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/docker/__init__.py +0 -0
  40. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/docker/cli.py +0 -0
  41. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/docker/elements.py +0 -0
  42. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/docker/main.py +0 -0
  43. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/__init__.py +0 -0
  44. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/arxiv/__init__.py +0 -0
  45. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/arxiv/cli.py +0 -0
  46. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/arxiv/client.py +0 -0
  47. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/arxiv/daily.py +0 -0
  48. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/arxiv/harvest.py +0 -0
  49. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/arxiv/models.py +0 -0
  50. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/arxiv/presets.py +0 -0
  51. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/arxiv/query.py +0 -0
  52. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/cli.py +0 -0
  53. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/wordpress/__init__.py +0 -0
  54. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/wordpress/client.py +0 -0
  55. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/explore/wordpress/models.py +0 -0
  56. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/interaction/__init__.py +0 -0
  57. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/interaction/command_schema.py +0 -0
  58. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/interaction/policy.py +0 -0
  59. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/interaction/warnings.py +0 -0
  60. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/llm/__init__.py +0 -0
  61. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/llm/chattype.py +0 -0
  62. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/llm/response.py +0 -0
  63. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/mcp/__init__.py +0 -0
  64. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/mcp/cli.py +0 -0
  65. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/serve/__init__.py +0 -0
  66. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/serve/capture.py +0 -0
  67. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/serve/cert_server.py +0 -0
  68. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/serve/cli.py +0 -0
  69. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/serve/local.py +0 -0
  70. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/serve/oauth.py +0 -0
  71. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/serve/svg2gif.py +0 -0
  72. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/skill/__init__.py +0 -0
  73. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/skill/cli.py +0 -0
  74. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/skill/interaction.py +0 -0
  75. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/skill/platforms.py +0 -0
  76. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/skill/validation.py +0 -0
  77. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/browser/__init__.py +0 -0
  78. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/browser/base.py +0 -0
  79. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/browser/cli.py +0 -0
  80. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/browser/playwright_impl.py +0 -0
  81. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/browser/selenium.py +0 -0
  82. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/browser/test_browser.py +0 -0
  83. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/browser/test_module.py +0 -0
  84. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/browser/test_remote.py +0 -0
  85. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/browser/xhs_qrcode.py +0 -0
  86. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/cc/__init__.py +0 -0
  87. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/cc/cli.py +0 -0
  88. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/cert/__init__.py +0 -0
  89. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/cert/cert_server.py +0 -0
  90. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/cert/email.py +0 -0
  91. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/crs/__init__.py +0 -0
  92. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/crs/api.py +0 -0
  93. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/crs/cli.py +0 -0
  94. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/crs/openai_oauth.py +0 -0
  95. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/interact.py +0 -0
  96. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/nginx/__init__.py +0 -0
  97. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/nginx/cli.py +0 -0
  98. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/nginx/templates.py +0 -0
  99. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/tplogin.py +0 -0
  100. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/tools/tplogin_cli.py +0 -0
  101. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/utils/__init__.py +0 -0
  102. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/utils/basic.py +0 -0
  103. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/utils/custom_logger.py +0 -0
  104. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/utils/fastobj.py +0 -0
  105. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/utils/httpclient.py +0 -0
  106. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/utils/pathing.py +0 -0
  107. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/utils/test_helpers.py +0 -0
  108. {chattool-7.1.2 → chattool-7.1.4}/src/chattool/utils/urltool.py +0 -0
  109. {chattool-7.1.2 → chattool-7.1.4}/src/chattool.egg-info/dependency_links.txt +0 -0
  110. {chattool-7.1.2 → chattool-7.1.4}/src/chattool.egg-info/entry_points.txt +0 -0
  111. {chattool-7.1.2 → chattool-7.1.4}/src/chattool.egg-info/top_level.txt +0 -0
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chattool
3
- Version: 7.1.2
3
+ Version: 7.1.4
4
4
  Summary: Toolkit for Chat API
5
5
  Author-email: Rex Wang <1073853456@qq.com>
6
6
  License: MIT license
7
- Project-URL: Homepage, https://github.com/cubenlp/chattool
8
- Project-URL: Repository, https://github.com/cubenlp/chattool
7
+ Project-URL: Homepage, https://github.com/CubeNLP/ChatTool
8
+ Project-URL: Repository, https://github.com/CubeNLP/ChatTool
9
+ Project-URL: Documentation, https://chattool.wzhecnu.cn
9
10
  Keywords: chattool
10
11
  Classifier: Development Status :: 2 - Pre-Alpha
11
12
  Classifier: Intended Audience :: Developers
@@ -31,6 +32,7 @@ Requires-Dist: ChatPyPI<0.3.0,>=0.2.3; extra == "arch"
31
32
  Requires-Dist: ChatNet<0.3.0,>=0.2.0; extra == "arch"
32
33
  Requires-Dist: ChatLark<0.2.0,>=0.1.1; extra == "arch"
33
34
  Requires-Dist: ChatImg[images]<0.2.0,>=0.1.3; extra == "arch"
35
+ Requires-Dist: ChatZulip<0.2.0,>=0.1.0; extra == "arch"
34
36
  Provides-Extra: interactive
35
37
  Requires-Dist: questionary>=2.1.0; extra == "interactive"
36
38
  Requires-Dist: prompt_toolkit; extra == "interactive"
@@ -43,6 +45,8 @@ Provides-Extra: lark
43
45
  Requires-Dist: ChatLark<0.2.0,>=0.1.1; extra == "lark"
44
46
  Provides-Extra: images
45
47
  Requires-Dist: ChatImg[images]<0.2.0,>=0.1.3; extra == "images"
48
+ Provides-Extra: zulip
49
+ Requires-Dist: ChatZulip<0.2.0,>=0.1.0; extra == "zulip"
46
50
  Provides-Extra: screenshot
47
51
  Requires-Dist: selenium; extra == "screenshot"
48
52
  Requires-Dist: pillow; extra == "screenshot"
@@ -67,7 +71,7 @@ Requires-Dist: ChatPyPI<0.3.0,>=0.2.3; extra == "pypi"
67
71
  Provides-Extra: setup
68
72
  Requires-Dist: chatup<0.3.0,>=0.2.0; extra == "setup"
69
73
  Provides-Extra: tools
70
- Requires-Dist: chattool[batch,browser,client,dns,images,interactive,lark,mcp,screenshot,serve]; extra == "tools"
74
+ Requires-Dist: chattool[batch,browser,client,dns,images,interactive,lark,mcp,screenshot,serve,zulip]; extra == "tools"
71
75
  Provides-Extra: tests
72
76
  Requires-Dist: coverage; extra == "tests"
73
77
  Requires-Dist: pytest>=3; extra == "tests"
@@ -313,6 +317,28 @@ chatpypi probe mychat
313
317
 
314
318
  如只想生成代码与测试骨架,可以用 `--without-mkdocs` 跳过 mkdocs/docs,用 `--without-workflows` 跳过 `.github/workflows/`。
315
319
 
320
+ ## CLI 树
321
+
322
+ `chattool --tree` 从真实 Click/LazyGroup 注册面生成,用于锁定当前 parent command surface。
323
+
324
+ ```text
325
+ chattool # Unified entry point for ChatTool services and scripts.
326
+ ├── --help # Show help for the current command.
327
+ ├── --version # Show package version.
328
+ ├── --tree # Print the registered CLI tree.
329
+ ├── browser # Browser automation helpers.
330
+ ├── cc # cc-connect helper commands.
331
+ ├── client # Remote client tools.
332
+ ├── crs # Claude Relay Service helper commands.
333
+ ├── docker # Docker utility commands.
334
+ ├── explore # Research and data exploration helpers.
335
+ ├── mcp # MCP server inspection and launch helpers.
336
+ ├── nginx # Nginx configuration generation helpers.
337
+ ├── serve # Local service helpers.
338
+ ├── skill # Install and manage ChatTool skills.
339
+ └── tplogin # TP-Link router helpers.
340
+ ```
341
+
316
342
  ## 开源协议
317
343
 
318
344
  MIT License
@@ -222,6 +222,28 @@ chatpypi probe mychat
222
222
 
223
223
  如只想生成代码与测试骨架,可以用 `--without-mkdocs` 跳过 mkdocs/docs,用 `--without-workflows` 跳过 `.github/workflows/`。
224
224
 
225
+ ## CLI 树
226
+
227
+ `chattool --tree` 从真实 Click/LazyGroup 注册面生成,用于锁定当前 parent command surface。
228
+
229
+ ```text
230
+ chattool # Unified entry point for ChatTool services and scripts.
231
+ ├── --help # Show help for the current command.
232
+ ├── --version # Show package version.
233
+ ├── --tree # Print the registered CLI tree.
234
+ ├── browser # Browser automation helpers.
235
+ ├── cc # cc-connect helper commands.
236
+ ├── client # Remote client tools.
237
+ ├── crs # Claude Relay Service helper commands.
238
+ ├── docker # Docker utility commands.
239
+ ├── explore # Research and data exploration helpers.
240
+ ├── mcp # MCP server inspection and launch helpers.
241
+ ├── nginx # Nginx configuration generation helpers.
242
+ ├── serve # Local service helpers.
243
+ ├── skill # Install and manage ChatTool skills.
244
+ └── tplogin # TP-Link router helpers.
245
+ ```
246
+
225
247
  ## 开源协议
226
248
 
227
249
  MIT License
@@ -40,6 +40,7 @@ arch = [
40
40
  "ChatNet>=0.2.0,<0.3.0",
41
41
  "ChatLark>=0.1.1,<0.2.0",
42
42
  "ChatImg[images]>=0.1.3,<0.2.0",
43
+ "ChatZulip>=0.1.0,<0.2.0",
43
44
  ]
44
45
  interactive = [
45
46
  "questionary>=2.1.0",
@@ -58,6 +59,9 @@ lark = [
58
59
  images = [
59
60
  "ChatImg[images]>=0.1.3,<0.2.0",
60
61
  ]
62
+ zulip = [
63
+ "ChatZulip>=0.1.0,<0.2.0",
64
+ ]
61
65
  screenshot = [
62
66
  "selenium",
63
67
  "pillow",
@@ -89,7 +93,7 @@ setup = [
89
93
  "chatup>=0.2.0,<0.3.0",
90
94
  ]
91
95
  tools = [
92
- "chattool[batch,browser,client,dns,images,interactive,lark,mcp,serve,screenshot]",
96
+ "chattool[batch,browser,client,dns,images,interactive,lark,mcp,serve,screenshot,zulip]",
93
97
  ]
94
98
  tests = [
95
99
  "coverage",
@@ -114,8 +118,9 @@ dev = [
114
118
  "chattool[arch,docs,setup,tests,tools]",
115
119
  ]
116
120
  [project.urls]
117
- Homepage = "https://github.com/cubenlp/chattool"
118
- Repository = "https://github.com/cubenlp/chattool"
121
+ Homepage = "https://github.com/CubeNLP/ChatTool"
122
+ Repository = "https://github.com/CubeNLP/ChatTool"
123
+ Documentation = "https://chattool.wzhecnu.cn"
119
124
 
120
125
  [project.scripts]
121
126
  chattool = "chattool.client:main_cli"
@@ -4,7 +4,7 @@ import importlib
4
4
 
5
5
  __author__ = """Rex Wang"""
6
6
  __email__ = "1073853456@qq.com"
7
- __version__ = "7.1.2"
7
+ __version__ = "7.1.4"
8
8
 
9
9
  from dotenv import load_dotenv
10
10
 
@@ -25,7 +25,6 @@ from .config import (
25
25
  AzureConfig,
26
26
  AliyunConfig,
27
27
  TencentConfig,
28
- ZulipConfig,
29
28
  BaseEnvConfig,
30
29
  )
31
30
 
@@ -64,6 +63,5 @@ __all__ = [
64
63
  "AzureConfig",
65
64
  "AliyunConfig",
66
65
  "TencentConfig",
67
- "ZulipConfig",
68
66
  "SSLCertUpdater",
69
67
  ]
@@ -0,0 +1,202 @@
1
+ from __future__ import annotations
2
+
3
+ import importlib
4
+ from typing import Callable
5
+
6
+ import click
7
+
8
+ from chattool import __version__
9
+
10
+
11
+ class LazyGroup(click.Group):
12
+ def __init__(
13
+ self,
14
+ *args,
15
+ lazy_commands: dict[str, Callable[[], click.Command]] | None = None,
16
+ lazy_command_help: dict[str, str] | None = None,
17
+ **kwargs,
18
+ ):
19
+ super().__init__(*args, **kwargs)
20
+ self._lazy_commands = dict(lazy_commands or {})
21
+ self._lazy_command_help = dict(lazy_command_help or {})
22
+
23
+ def list_commands(self, ctx: click.Context) -> list[str]:
24
+ commands = set(self.commands)
25
+ commands.update(self._lazy_commands)
26
+ return sorted(commands)
27
+
28
+ def get_command(self, ctx: click.Context, name: str) -> click.Command | None:
29
+ if name in self._lazy_commands:
30
+ self.commands[name] = self._lazy_commands.pop(name)()
31
+ return super().get_command(ctx, name)
32
+
33
+ def format_commands(
34
+ self, ctx: click.Context, formatter: click.HelpFormatter
35
+ ) -> None:
36
+ rows = []
37
+ for subcommand in self.list_commands(ctx):
38
+ if subcommand in self.commands:
39
+ cmd = self.commands[subcommand]
40
+ if cmd is None or cmd.hidden:
41
+ continue
42
+ help_text = cmd.get_short_help_str()
43
+ else:
44
+ help_text = self._lazy_command_help.get(subcommand, "")
45
+ rows.append((subcommand, help_text))
46
+
47
+ if rows:
48
+ with formatter.section("Commands"):
49
+ formatter.write_dl(rows)
50
+
51
+
52
+ _TREE_PARAM_PURPOSES = {
53
+ "help": "Show help for the current command.",
54
+ "version": "Show package version.",
55
+ "tree": "Print the registered CLI tree.",
56
+ }
57
+
58
+
59
+ def _load_attr(module_path: str, attr: str) -> click.Command:
60
+ module = importlib.import_module(module_path)
61
+ return getattr(module, attr)
62
+
63
+
64
+ def _build_serve_group() -> click.Command:
65
+ return _load_attr("chattool.serve.cli", "serve_cli")
66
+
67
+
68
+ def _build_client_group() -> click.Command:
69
+ return LazyGroup(
70
+ name="client",
71
+ help="Remote client tools.",
72
+ lazy_commands={
73
+ "cert": lambda: _load_attr("chattool.client.cert_client", "cert_client"),
74
+ "svg2gif": lambda: _load_attr(
75
+ "chattool.client.svg2gif_client", "svg2gif_client"
76
+ ),
77
+ },
78
+ lazy_command_help={
79
+ "cert": "Manage certificates through the remote cert service.",
80
+ "svg2gif": "Convert SVG to GIF through the remote svg2gif service.",
81
+ },
82
+ )
83
+
84
+
85
+ def _command_help(command: click.Command) -> str:
86
+ if command.help:
87
+ return command.help
88
+ if command.short_help:
89
+ return command.short_help
90
+ if command.callback and command.callback.__doc__:
91
+ return command.callback.__doc__.strip()
92
+ return ""
93
+
94
+
95
+ def _param_signature(param: click.Parameter) -> str:
96
+ opts = getattr(param, "opts", None) or []
97
+ secondary = getattr(param, "secondary_opts", None) or []
98
+ names = [*opts, *secondary]
99
+ if names:
100
+ return "/".join(names)
101
+ return param.human_readable_name.upper().replace("_", "-")
102
+
103
+
104
+ def _param_purpose(param: click.Parameter) -> str:
105
+ if param.name in _TREE_PARAM_PURPOSES:
106
+ return _TREE_PARAM_PURPOSES[param.name]
107
+ if isinstance(param, click.Option) and param.help:
108
+ return param.help.rstrip(".") + "."
109
+ return "Option registered by Click."
110
+
111
+
112
+ def _iter_command_rows(command: click.Command) -> list[tuple[str, str]]:
113
+ rows = [("--help", _TREE_PARAM_PURPOSES["help"])]
114
+ for param in command.params:
115
+ if getattr(param, "hidden", False):
116
+ continue
117
+ rows.append((_param_signature(param), _param_purpose(param)))
118
+ if isinstance(command, LazyGroup):
119
+ ctx = click.Context(command)
120
+ for name in command.list_commands(ctx):
121
+ if name in command.commands:
122
+ child = command.commands[name]
123
+ if child is None or child.hidden:
124
+ continue
125
+ help_text = command._lazy_command_help.get(name)
126
+ if help_text is None and name in command.commands:
127
+ help_text = command.commands[name].get_short_help_str()
128
+ rows.append((name, help_text or ""))
129
+ elif isinstance(command, click.Group):
130
+ for name, child in sorted(command.commands.items()):
131
+ if child is None or child.hidden:
132
+ continue
133
+ rows.append((name, child.get_short_help_str()))
134
+ return rows
135
+
136
+
137
+ def render_command_tree(command: click.Command, name: str = "chattool") -> str:
138
+ lines = [f"{name} # {_command_help(command)}"]
139
+ rows = _iter_command_rows(command)
140
+ for index, (label, purpose) in enumerate(rows):
141
+ connector = "└──" if index == len(rows) - 1 else "├──"
142
+ lines.append(f"{connector} {label} # {purpose}")
143
+ return "\n".join(lines)
144
+
145
+
146
+ @click.group(cls=LazyGroup, invoke_without_command=True)
147
+ @click.version_option(__version__, prog_name="chattool")
148
+ @click.option(
149
+ "--tree",
150
+ "show_tree",
151
+ is_flag=True,
152
+ help="Print the registered CLI tree.",
153
+ )
154
+ @click.pass_context
155
+ def cli(ctx: click.Context, show_tree: bool) -> None:
156
+ """Unified entry point for ChatTool services and scripts."""
157
+ if show_tree:
158
+ click.echo(render_command_tree(ctx.command, "chattool"))
159
+ ctx.exit(0)
160
+ if ctx.invoked_subcommand is None:
161
+ click.echo(ctx.get_help())
162
+ ctx.exit(0)
163
+
164
+
165
+ cli._lazy_commands.update(
166
+ {
167
+ "serve": _build_serve_group,
168
+ "client": _build_client_group,
169
+ "nginx": lambda: _load_attr("chattool.tools.nginx.cli", "cli"),
170
+ "mcp": lambda: _load_attr("chattool.mcp.cli", "cli"),
171
+ "tplogin": lambda: _load_attr("chattool.tools.tplogin_cli", "cli"),
172
+ "browser": lambda: _load_attr("chattool.tools.browser.cli", "cli"),
173
+ "skill": lambda: _load_attr("chattool.skill.cli", "skill_cli"),
174
+ "cc": lambda: _load_attr("chattool.tools.cc.cli", "cli"),
175
+ "docker": lambda: _load_attr("chattool.docker.cli", "docker_cmd"),
176
+ "explore": lambda: _load_attr("chattool.explore.cli", "explore_cli"),
177
+ "crs": lambda: _load_attr("chattool.tools.crs.cli", "cli"),
178
+ }
179
+ )
180
+ cli._lazy_command_help.update(
181
+ {
182
+ "browser": "Browser automation helpers.",
183
+ "cc": "cc-connect helper commands.",
184
+ "client": "Remote client tools.",
185
+ "crs": "Claude Relay Service helper commands.",
186
+ "docker": "Docker utility commands.",
187
+ "explore": "Research and data exploration helpers.",
188
+ "mcp": "MCP server inspection and launch helpers.",
189
+ "nginx": "Nginx configuration generation helpers.",
190
+ "serve": "Local service helpers.",
191
+ "skill": "Install and manage ChatTool skills.",
192
+ "tplogin": "TP-Link router helpers.",
193
+ }
194
+ )
195
+
196
+
197
+ def main():
198
+ cli()
199
+
200
+
201
+ if __name__ == "__main__":
202
+ main()
@@ -5,7 +5,6 @@ from .skills import SkillsConfig
5
5
  from .azure import AzureConfig
6
6
  from .aliyun import AliyunConfig
7
7
  from .tencent import TencentConfig
8
- from .zulip import ZulipConfig
9
8
  from .tplink import TPLinkConfig
10
9
  from .browser import BrowserConfig
11
10
 
@@ -16,7 +15,6 @@ __all__ = [
16
15
  "AzureConfig",
17
16
  "OpenAIConfig",
18
17
  "CRSConfig",
19
- "ZulipConfig",
20
18
  "AliyunConfig",
21
19
  "TencentConfig",
22
20
  "BrowserConfig",
@@ -7,7 +7,6 @@ from .skills import SkillsConfig
7
7
  from .azure import AzureConfig
8
8
  from .aliyun import AliyunConfig
9
9
  from .tencent import TencentConfig
10
- from .zulip import ZulipConfig
11
10
  from .tplink import TPLinkConfig
12
11
  from .browser import BrowserConfig
13
12
 
@@ -18,7 +17,6 @@ __all__ = [
18
17
  "AzureConfig",
19
18
  "OpenAIConfig",
20
19
  "CRSConfig",
21
- "ZulipConfig",
22
20
  "AliyunConfig",
23
21
  "TencentConfig",
24
22
  "BrowserConfig",
@@ -12,13 +12,7 @@ class MCPToolSpec:
12
12
  summary: str
13
13
 
14
14
 
15
- TOOL_SPECS: Sequence[MCPToolSpec] = (
16
- MCPToolSpec("zulip_list_streams", "zulip", ("zulip", "read"), "列出 Zulip 频道"),
17
- MCPToolSpec("zulip_get_messages", "zulip", ("zulip", "read"), "查询 Zulip 消息"),
18
- MCPToolSpec("zulip_send_message", "zulip", ("zulip", "write"), "发送 Zulip 消息"),
19
- MCPToolSpec("zulip_react", "zulip", ("zulip", "write"), "添加 Zulip 表情反应"),
20
- MCPToolSpec("zulip_upload_file", "zulip", ("zulip", "write"), "上传 Zulip 文件"),
21
- )
15
+ TOOL_SPECS: Sequence[MCPToolSpec] = ()
22
16
 
23
17
 
24
18
  def get_tool_specs() -> List[MCPToolSpec]:
@@ -10,9 +10,7 @@ from chattool.utils import setup_logger
10
10
 
11
11
  logger = setup_logger("mcp_server", log_level="INFO")
12
12
  SERVER_NAME = "ChatTool MCP Server"
13
- REGISTER_MODULES = (
14
- "chattool.tools.zulip.mcp",
15
- )
13
+ REGISTER_MODULES: tuple[str, ...] = ()
16
14
 
17
15
 
18
16
  def _register_optional_tools(mcp_instance):
@@ -2,7 +2,6 @@ import importlib
2
2
 
3
3
 
4
4
  _LAZY_ATTRS = {
5
- "ZulipClient": ("chattool.tools.zulip", "ZulipClient"),
6
5
  "InteractiveShell": ("chattool.tools.interact", "InteractiveShell"),
7
6
  "SimpleAsyncShell": ("chattool.tools.interact", "SimpleAsyncShell"),
8
7
  "SSLCertUpdater": ("chatdns", "SSLCertUpdater"),
@@ -20,7 +19,6 @@ def __getattr__(name: str):
20
19
  return value
21
20
 
22
21
  __all__ = [
23
- "ZulipClient",
24
22
  "InteractiveShell", "SimpleAsyncShell",
25
23
  "SSLCertUpdater",
26
24
  "TPLogin",
@@ -1,11 +1,12 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: chattool
3
- Version: 7.1.2
3
+ Version: 7.1.4
4
4
  Summary: Toolkit for Chat API
5
5
  Author-email: Rex Wang <1073853456@qq.com>
6
6
  License: MIT license
7
- Project-URL: Homepage, https://github.com/cubenlp/chattool
8
- Project-URL: Repository, https://github.com/cubenlp/chattool
7
+ Project-URL: Homepage, https://github.com/CubeNLP/ChatTool
8
+ Project-URL: Repository, https://github.com/CubeNLP/ChatTool
9
+ Project-URL: Documentation, https://chattool.wzhecnu.cn
9
10
  Keywords: chattool
10
11
  Classifier: Development Status :: 2 - Pre-Alpha
11
12
  Classifier: Intended Audience :: Developers
@@ -31,6 +32,7 @@ Requires-Dist: ChatPyPI<0.3.0,>=0.2.3; extra == "arch"
31
32
  Requires-Dist: ChatNet<0.3.0,>=0.2.0; extra == "arch"
32
33
  Requires-Dist: ChatLark<0.2.0,>=0.1.1; extra == "arch"
33
34
  Requires-Dist: ChatImg[images]<0.2.0,>=0.1.3; extra == "arch"
35
+ Requires-Dist: ChatZulip<0.2.0,>=0.1.0; extra == "arch"
34
36
  Provides-Extra: interactive
35
37
  Requires-Dist: questionary>=2.1.0; extra == "interactive"
36
38
  Requires-Dist: prompt_toolkit; extra == "interactive"
@@ -43,6 +45,8 @@ Provides-Extra: lark
43
45
  Requires-Dist: ChatLark<0.2.0,>=0.1.1; extra == "lark"
44
46
  Provides-Extra: images
45
47
  Requires-Dist: ChatImg[images]<0.2.0,>=0.1.3; extra == "images"
48
+ Provides-Extra: zulip
49
+ Requires-Dist: ChatZulip<0.2.0,>=0.1.0; extra == "zulip"
46
50
  Provides-Extra: screenshot
47
51
  Requires-Dist: selenium; extra == "screenshot"
48
52
  Requires-Dist: pillow; extra == "screenshot"
@@ -67,7 +71,7 @@ Requires-Dist: ChatPyPI<0.3.0,>=0.2.3; extra == "pypi"
67
71
  Provides-Extra: setup
68
72
  Requires-Dist: chatup<0.3.0,>=0.2.0; extra == "setup"
69
73
  Provides-Extra: tools
70
- Requires-Dist: chattool[batch,browser,client,dns,images,interactive,lark,mcp,screenshot,serve]; extra == "tools"
74
+ Requires-Dist: chattool[batch,browser,client,dns,images,interactive,lark,mcp,screenshot,serve,zulip]; extra == "tools"
71
75
  Provides-Extra: tests
72
76
  Requires-Dist: coverage; extra == "tests"
73
77
  Requires-Dist: pytest>=3; extra == "tests"
@@ -313,6 +317,28 @@ chatpypi probe mychat
313
317
 
314
318
  如只想生成代码与测试骨架,可以用 `--without-mkdocs` 跳过 mkdocs/docs,用 `--without-workflows` 跳过 `.github/workflows/`。
315
319
 
320
+ ## CLI 树
321
+
322
+ `chattool --tree` 从真实 Click/LazyGroup 注册面生成,用于锁定当前 parent command surface。
323
+
324
+ ```text
325
+ chattool # Unified entry point for ChatTool services and scripts.
326
+ ├── --help # Show help for the current command.
327
+ ├── --version # Show package version.
328
+ ├── --tree # Print the registered CLI tree.
329
+ ├── browser # Browser automation helpers.
330
+ ├── cc # cc-connect helper commands.
331
+ ├── client # Remote client tools.
332
+ ├── crs # Claude Relay Service helper commands.
333
+ ├── docker # Docker utility commands.
334
+ ├── explore # Research and data exploration helpers.
335
+ ├── mcp # MCP server inspection and launch helpers.
336
+ ├── nginx # Nginx configuration generation helpers.
337
+ ├── serve # Local service helpers.
338
+ ├── skill # Install and manage ChatTool skills.
339
+ └── tplogin # TP-Link router helpers.
340
+ ```
341
+
316
342
  ## 开源协议
317
343
 
318
344
  MIT License
@@ -27,7 +27,6 @@ src/chattool/config/openai.py
27
27
  src/chattool/config/skills.py
28
28
  src/chattool/config/tencent.py
29
29
  src/chattool/config/tplink.py
30
- src/chattool/config/zulip.py
31
30
  src/chattool/docker/__init__.py
32
31
  src/chattool/docker/cli.py
33
32
  src/chattool/docker/elements.py
@@ -93,11 +92,6 @@ src/chattool/tools/crs/openai_oauth.py
93
92
  src/chattool/tools/nginx/__init__.py
94
93
  src/chattool/tools/nginx/cli.py
95
94
  src/chattool/tools/nginx/templates.py
96
- src/chattool/tools/zulip/__init__.py
97
- src/chattool/tools/zulip/cli.py
98
- src/chattool/tools/zulip/client.py
99
- src/chattool/tools/zulip/legacy.py
100
- src/chattool/tools/zulip/mcp.py
101
95
  src/chattool/utils/__init__.py
102
96
  src/chattool/utils/basic.py
103
97
  src/chattool/utils/custom_logger.py
@@ -16,6 +16,7 @@ ChatPyPI<0.3.0,>=0.2.3
16
16
  ChatNet<0.3.0,>=0.2.0
17
17
  ChatLark<0.2.0,>=0.1.1
18
18
  ChatImg[images]<0.2.0,>=0.1.3
19
+ ChatZulip<0.2.0,>=0.1.0
19
20
 
20
21
  [batch]
21
22
  batch_executor>=0.3.0
@@ -89,4 +90,7 @@ pytest-asyncio
89
90
  pytest-mock
90
91
 
91
92
  [tools]
92
- chattool[batch,browser,client,dns,images,interactive,lark,mcp,screenshot,serve]
93
+ chattool[batch,browser,client,dns,images,interactive,lark,mcp,screenshot,serve,zulip]
94
+
95
+ [zulip]
96
+ ChatZulip<0.2.0,>=0.1.0
@@ -1,105 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import importlib
4
- from typing import Callable
5
-
6
- import click
7
-
8
-
9
- class LazyGroup(click.Group):
10
- def __init__(
11
- self,
12
- *args,
13
- lazy_commands: dict[str, Callable[[], click.Command]] | None = None,
14
- lazy_command_help: dict[str, str] | None = None,
15
- **kwargs,
16
- ):
17
- super().__init__(*args, **kwargs)
18
- self._lazy_commands = dict(lazy_commands or {})
19
- self._lazy_command_help = dict(lazy_command_help or {})
20
-
21
- def list_commands(self, ctx: click.Context) -> list[str]:
22
- commands = set(self.commands)
23
- commands.update(self._lazy_commands)
24
- return sorted(commands)
25
-
26
- def get_command(self, ctx: click.Context, name: str) -> click.Command | None:
27
- if name in self._lazy_commands:
28
- self.commands[name] = self._lazy_commands.pop(name)()
29
- return super().get_command(ctx, name)
30
-
31
- def format_commands(
32
- self, ctx: click.Context, formatter: click.HelpFormatter
33
- ) -> None:
34
- rows = []
35
- for subcommand in self.list_commands(ctx):
36
- if subcommand in self.commands:
37
- cmd = self.commands[subcommand]
38
- if cmd is None or cmd.hidden:
39
- continue
40
- help_text = cmd.get_short_help_str()
41
- else:
42
- help_text = self._lazy_command_help.get(subcommand, "")
43
- rows.append((subcommand, help_text))
44
-
45
- if rows:
46
- with formatter.section("Commands"):
47
- formatter.write_dl(rows)
48
-
49
-
50
- def _load_attr(module_path: str, attr: str) -> click.Command:
51
- module = importlib.import_module(module_path)
52
- return getattr(module, attr)
53
-
54
-
55
- def _build_serve_group() -> click.Command:
56
- return _load_attr("chattool.serve.cli", "serve_cli")
57
-
58
-
59
- def _build_client_group() -> click.Command:
60
- return LazyGroup(
61
- name="client",
62
- help="Remote client tools.",
63
- lazy_commands={
64
- "cert": lambda: _load_attr("chattool.client.cert_client", "cert_client"),
65
- "svg2gif": lambda: _load_attr(
66
- "chattool.client.svg2gif_client", "svg2gif_client"
67
- ),
68
- },
69
- lazy_command_help={
70
- "cert": "Manage certificates through the remote cert service.",
71
- "svg2gif": "Convert SVG to GIF through the remote svg2gif service.",
72
- },
73
- )
74
-
75
-
76
- @click.group(cls=LazyGroup)
77
- def cli():
78
- """ChatTool CLI - Unified entry point for ChatTool services and scripts."""
79
- pass
80
-
81
-
82
- cli._lazy_commands.update(
83
- {
84
- "serve": _build_serve_group,
85
- "client": _build_client_group,
86
- "nginx": lambda: _load_attr("chattool.tools.nginx.cli", "cli"),
87
- "mcp": lambda: _load_attr("chattool.mcp.cli", "cli"),
88
- "tplogin": lambda: _load_attr("chattool.tools.tplogin_cli", "cli"),
89
- "browser": lambda: _load_attr("chattool.tools.browser.cli", "cli"),
90
- "zulip": lambda: _load_attr("chattool.tools.zulip.cli", "cli"),
91
- "skill": lambda: _load_attr("chattool.skill.cli", "skill_cli"),
92
- "cc": lambda: _load_attr("chattool.tools.cc.cli", "cli"),
93
- "docker": lambda: _load_attr("chattool.docker.cli", "docker_cmd"),
94
- "explore": lambda: _load_attr("chattool.explore.cli", "explore_cli"),
95
- "crs": lambda: _load_attr("chattool.tools.crs.cli", "cli"),
96
- }
97
- )
98
-
99
-
100
- def main():
101
- cli()
102
-
103
-
104
- if __name__ == "__main__":
105
- main()