ipclick 2.1.1__tar.gz → 2.1.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 (124) hide show
  1. {ipclick-2.1.1 → ipclick-2.1.3}/PKG-INFO +84 -90
  2. {ipclick-2.1.1 → ipclick-2.1.3}/README.md +80 -86
  3. {ipclick-2.1.1 → ipclick-2.1.3}/pyproject.toml +6 -6
  4. {ipclick-2.1.1 → ipclick-2.1.3}/pyproject.toml.orig +6 -6
  5. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/__init__.py +1 -1
  6. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/browser_engines.py +8 -4
  7. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/dto/proto/task_pb2_grpc.py +1 -1
  8. {ipclick-2.1.1 → ipclick-2.1.3}/LICENSE +0 -0
  9. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/__main__.py +0 -0
  10. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/__init__.py +0 -0
  11. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/base.py +0 -0
  12. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/browser_adapter.py +0 -0
  13. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/browser_settings.py +0 -0
  14. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/curl_cffi_adapter.py +0 -0
  15. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/drission_adapter.py +0 -0
  16. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/niquests_adapter.py +0 -0
  17. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/redirects.py +0 -0
  18. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/registry.py +0 -0
  19. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/retry.py +0 -0
  20. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/sessions.py +0 -0
  21. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/adapters/settings.py +0 -0
  22. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/aio.py +0 -0
  23. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/async_limiter.py +0 -0
  24. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/async_server.py +0 -0
  25. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/auth.py +0 -0
  26. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cli/__init__.py +0 -0
  27. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cli/agent.py +0 -0
  28. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cli/main.py +0 -0
  29. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cli/output.py +0 -0
  30. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cli/skill_cmd.py +0 -0
  31. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cluster/__init__.py +0 -0
  32. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cluster/async_forwarder.py +0 -0
  33. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cluster/balancer.py +0 -0
  34. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cluster/client.py +0 -0
  35. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cluster/discovery.py +0 -0
  36. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cluster/forwarder.py +0 -0
  37. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cluster/node.py +0 -0
  38. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cluster/pool.py +0 -0
  39. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cluster/probe.py +0 -0
  40. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cluster/status_page.py +0 -0
  41. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/cluster/tokens.py +0 -0
  42. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/components.py +0 -0
  43. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/compression.py +0 -0
  44. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/config_loader/__init__.py +0 -0
  45. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/config_loader/dotenv.py +0 -0
  46. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/config_loader/env_writer.py +0 -0
  47. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/config_loader/loader.py +0 -0
  48. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/config_loader/placeholders.py +0 -0
  49. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/config_loader/writer.py +0 -0
  50. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/configs/__init__.py +0 -0
  51. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/configs/default_config.toml +0 -0
  52. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/dto/__init__.py +0 -0
  53. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/dto/models.py +0 -0
  54. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/dto/proto/__init__.py +0 -0
  55. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/dto/proto/generate.py +0 -0
  56. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/dto/proto/task.proto +0 -0
  57. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/dto/proto/task_pb2.py +0 -0
  58. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/dto/proto/task_pb2.pyi +0 -0
  59. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/dto/response.py +0 -0
  60. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/exceptions.py +0 -0
  61. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/factory.py +0 -0
  62. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/health.py +0 -0
  63. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/limiter.py +0 -0
  64. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/multiprocess.py +0 -0
  65. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/ports.py +0 -0
  66. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/protocols.py +0 -0
  67. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/py.typed +0 -0
  68. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/resume.py +0 -0
  69. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/rpc/__init__.py +0 -0
  70. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/rpc/channel.py +0 -0
  71. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/rpc/options.py +0 -0
  72. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/sdk.py +0 -0
  73. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/secrets.py +0 -0
  74. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/server.py +0 -0
  75. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/server_settings.py +0 -0
  76. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/services/__init__.py +0 -0
  77. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/services/async_task_service.py +0 -0
  78. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/services/components.py +0 -0
  79. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/services/detached.py +0 -0
  80. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/services/errors.py +0 -0
  81. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/services/task_service.py +0 -0
  82. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/skill.py +0 -0
  83. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/skills/ipclick/SKILL.md +0 -0
  84. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/tls.py +0 -0
  85. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/trace/__init__.py +0 -0
  86. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/trace/counters.py +0 -0
  87. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/trace/recorder.py +0 -0
  88. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/trace/records.py +0 -0
  89. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/trace/store.py +0 -0
  90. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/utils/__init__.py +0 -0
  91. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/utils/coerce.py +0 -0
  92. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/utils/config_util.py +0 -0
  93. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/utils/log_util.py +0 -0
  94. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/utils/module_probe.py +0 -0
  95. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/utils/path_util.py +0 -0
  96. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/utils/secure_util.py +0 -0
  97. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/utils/url_util.py +0 -0
  98. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/__init__.py +0 -0
  99. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/assets.py +0 -0
  100. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/auth.py +0 -0
  101. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/curl_parser.py +0 -0
  102. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/deploy.py +0 -0
  103. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/editable.py +0 -0
  104. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/installer.py +0 -0
  105. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/pages/__init__.py +0 -0
  106. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/pages/components.py +0 -0
  107. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/pages/context.py +0 -0
  108. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/pages/sandbox.py +0 -0
  109. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/pages/settings.py +0 -0
  110. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/pages/skill.py +0 -0
  111. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/pages/trace.py +0 -0
  112. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/proxy_tunnel.py +0 -0
  113. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/server.py +0 -0
  114. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/snapshot.py +0 -0
  115. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/templates/__init__.py +0 -0
  116. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/templates/base.py +0 -0
  117. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/templates/components.py +0 -0
  118. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/templates/config.py +0 -0
  119. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/templates/dashboard.py +0 -0
  120. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/templates/deploy.py +0 -0
  121. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/templates/login.py +0 -0
  122. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/templates/sandbox.py +0 -0
  123. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/templates/skill.py +0 -0
  124. {ipclick-2.1.1 → ipclick-2.1.3}/src/ipclick/web/templates/trace.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ipclick
3
- Version: 2.1.1
3
+ Version: 2.1.3
4
4
  Summary: IPClick Tool
5
5
  Keywords: http,grpc,downloader,scraping,browser,cluster
6
6
  Author: 元气码农少女酱
@@ -17,15 +17,15 @@ Classifier: Programming Language :: Python :: 3.14
17
17
  Classifier: Topic :: Internet :: WWW/HTTP
18
18
  Classifier: Topic :: Software Development :: Libraries :: Python Modules
19
19
  Classifier: Typing :: Typed
20
- Requires-Dist: curl-cffi>=0.16.0,<0.17.0
21
- Requires-Dist: grpcio>=1.84.0,<2.0.0
20
+ Requires-Dist: curl-cffi>=0.16.0
21
+ Requires-Dist: grpcio>=1.85.1,<2.0.0
22
22
  Requires-Dist: grpcio-health-checking>=1.83.0,<2.0.0
23
23
  Requires-Dist: protobuf>=7.35.1,<8.0.0
24
24
  Requires-Dist: click>=8.4.2,<9.0.0
25
25
  Requires-Dist: fake-useragent>=2.2.0,<3.0.0
26
26
  Requires-Dist: loguru>=0.7.3,<0.8.0
27
27
  Requires-Dist: python-box[all]>=7.4.1,<8.0.0
28
- Requires-Dist: uuid-utils>=0.17.0,<0.18.0
28
+ Requires-Dist: uuid-utils>=0.17.0,<1.3.0
29
29
  Requires-Dist: typing-extensions>=4.16.0,<5.0.0
30
30
  Requires-Dist: camoufox>=0.5.4,<1.0.0 ; extra == 'camoufox'
31
31
  Requires-Dist: drissionpage>=4.1.1,<5.0.0 ; extra == 'drissionpage'
@@ -51,14 +51,13 @@ Description-Content-Type: text/markdown
51
51
 
52
52
  ![IPClick Logo](https://raw.githubusercontent.com/yuanqimanong/IPClick/master/.github/logo.png)
53
53
 
54
- > IPClick 名字灵感来源于动画《Link Click》(时光代理人)。正如时光代理人穿梭于不同的时空执行任务,IPClick 帮助您将 HTTP 请求分发到不同的节点高效执行。
54
+ > IPClick 的名字灵感来自《Link Click》(时光代理人)。项目将 HTTP 请求分发到不同节点执行。
55
55
 
56
- IPClick 是一个基于 gRPC 的分布式 HTTP 请求代理。你把请求交给它,它带着浏览器 TLS
57
- 指纹、重试策略、限流与准入规则去发,必要时换一台机器的出口 IP 去发,并把每一次
58
- 执行都记成一条可查的链路。
56
+ IPClick 是一个基于 gRPC 的分布式 HTTP 请求代理。调用方通过 Python SDK、命令行或 Web
57
+ 管理端提交请求,服务端负责适配器选择、TLS 指纹伪装、重试、限流和 URL 准入。
58
+ 多机部署时,请求可以由不同节点的出口 IP 发出,执行过程会记录为可查询的链路。
59
59
 
60
- > 📚 **完整文档在 [Wiki](https://github.com/yuanqimanong/IPClick/wiki)**。本页只讲"这是什么、怎么跑起来",
61
- > 深入用法都在 Wiki 里。
60
+ > 📚 **完整文档在 Wiki**。本页介绍安装、启动和常用入口,详细配置与 API 用法见文档目录。[Wiki](https://github.com/yuanqimanong/IPClick/wiki)
62
61
 
63
62
  | | |
64
63
  |---|---|
@@ -81,22 +80,22 @@ IPClick 是一个基于 gRPC 的分布式 HTTP 请求代理。你把请求交给
81
80
 
82
81
  ## ✨ 特性
83
82
 
84
- - **多适配器**:默认 `curl_cffi`(带 TLS 指纹伪装),按需加装 `niquests`(HTTP/3)
85
- 或四个浏览器引擎之一;也可注册自定义适配器
86
- - **轻量安装**:`pip install ipclick` 只带 curl_cffi,其余走 extras
87
- - **浏览器渲染**:起真实浏览器执行 JS,camoufox / patchright / playwright /
88
- DrissionPage 四选一,默认按平台挑
89
- - **集群与故障转移**:轮询 / 随机 / 加权均衡、健康探测、自动换节点(仅 GET/HEAD/OPTIONS);服务端转发与
90
- 客户端分发两种形态
91
- - **按 host 限流**:按目标域名限制并发与 QPS,不会把单个站点打爆
83
+ - **多适配器**。默认使用带 TLS 指纹伪装的 `curl_cffi`,可按需安装 `niquests`(HTTP/3)
84
+ 或浏览器引擎,也支持注册自定义适配器
85
+ - **按需安装**。`pip install ipclick` 安装核心依赖,可选适配器通过 extras 安装
86
+ - **浏览器渲染**。通过 camoufox / patchright / playwright / DrissionPage 执行 JS,
87
+ 默认引擎按平台选择
88
+ - **集群与故障转移**。支持轮询、随机、加权均衡和健康探测,以及服务端转发与客户端分发。
89
+ 仅 GET/HEAD/OPTIONS 请求会自动换节点重试
90
+ - **按 host 限流**。按目标域名控制并发与 QPS
92
91
  - **流式下载 · 断点续传 · 批量 · 异步客户端**
93
92
  - **令牌鉴权 · TLS/mTLS · SSRF 准入 · `grpc.health.v1` 健康检查**
94
- - **链路记录**:每个请求记一条(谁执行的、重试几次、排队多久),内存环形缓冲 +
95
- 可选 SQLite,零第三方依赖
96
- - **Web 管理端**:带登录的界面——总览、实时请求流、就地发请求看源码、配置改完写回
97
- `ipclick.toml`、集群节点增删、可选组件装卸。前端零构建链路
98
- - **AI 可调用**:一组结构化输出、退出码分类的命令,外加随包分发的技能包
99
- - **完整类型标注**:随包提供 `py.typed`
93
+ - **链路记录**。记录执行节点、重试次数和排队时间,使用内存环形缓冲或可选 SQLite 存储,
94
+ 记录模块使用标准库实现
95
+ - **Web 管理端**。提供登录、总览、实时请求流、请求调试、配置写回、集群节点管理和组件装卸,
96
+ 前端无需构建
97
+ - **AI 可调用**。提供结构化输出、分类退出码和随包分发的技能包
98
+ - **完整类型标注**。随包提供 `py.typed`
100
99
 
101
100
  ## 📦 安装
102
101
 
@@ -106,8 +105,8 @@ pip install "ipclick[niquests]" # 加 HTTP/2 + HTTP/3
106
105
  pip install "ipclick[camoufox]" # 加浏览器渲染(还需下载浏览器本体)
107
106
  ```
108
107
 
109
- **没有 `[all]`**:四个浏览器内核全装是 70+ 个包和上 G 的浏览器本体,绝大多数部署
110
- 只需要其中一个。装哪个、怎么下浏览器本体,见 [安装](https://github.com/yuanqimanong/IPClick/wiki/Installation)。
108
+ 可选适配器按用途安装,项目未提供 `[all]`。浏览器引擎还需要单独准备浏览器本体。
109
+ 引擎选择与安装步骤见 [安装](https://github.com/yuanqimanong/IPClick/wiki/Installation)。
111
110
 
112
111
  需要 Python 3.11+。
113
112
 
@@ -135,15 +134,14 @@ ipclick run --verbose
135
134
  ipclick run --port 9628 --web-port 9531 -w
136
135
  ```
137
136
 
138
- 默认端口是 **gRPC 9528** 与 **Web 9527**。两者都跑在 HTTP/2 之上但协议不同,
139
- 把客户端指到 Web 端口上会在 gRPC 握手阶段失败,报出的错误还和端口毫无关系——
140
- 所以连不上时 IPClick 会额外附一句端口提示。
137
+ 默认端口分别为 **gRPC 9528** 与 **Web 9527**。SDK 和其他节点连接 gRPC 端口,
138
+ 浏览器访问 Web 端口。客户端误连 Web 端口时会在 gRPC 握手阶段失败,错误信息中会附带端口提示。
141
139
 
142
- ### 吞吐上不去?先加进程,不是加线程
140
+ ### 服务端并发与多进程
143
141
 
144
- 服务端默认一请求一线程,**GIL 才是吞吐天花板**——调大 `[SERVER].max_workers` 对吞吐没有
145
- 影响。要用满多核就起多个工作进程,靠 SO_REUSEPORT 共享同一个端口,分发由内核做,对调用方
146
- 完全透明(仍然只有一个地址一个端口):
142
+ 服务端默认在线程池中处理请求,`[SERVER].max_workers` 控制单个进程的线程数。
143
+ 需要利用多个 CPU 核时,可配置多个工作进程,通过 SO_REUSEPORT 共享监听端口,
144
+ 由内核分发连接。客户端仍使用同一个服务地址。
147
145
 
148
146
  ```toml
149
147
  [SERVER]
@@ -153,24 +151,25 @@ max_concurrent_rpcs = 0 # 0 = max_workers × 8。决定"排队能排多长",
153
151
  async_mode = false # true = grpc.aio 协程模式(实验性,默认关)
154
152
  ```
155
153
 
156
- **仅 Unix。** Windows 上没有 `os.fork`,会打一条告警后降级成单进程。Web 管理端只在 0 号
157
- 进程里起,内存按进程数线性增长——每进程各有一份适配器与连接池,开浏览器渲染时尤其要算。
154
+ **多进程模式仅支持 Unix。** Windows 上缺少 `os.fork` 时会告警并降级为单进程。
155
+ Web 管理端只在 0 号进程中启动。每个工作进程各自持有适配器和连接池,配置进程数时需计算
156
+ 这部分内存开销,尤其是启用浏览器渲染的节点。
158
157
 
159
- 协程和多进程**不是二选一**:前者解决并发模型,后者解决多核,两者叠加才是终态。
158
+ 协程模式控制单个进程内的并发方式,多进程用于利用多个 CPU 核,两项可以组合使用。
160
159
 
161
160
  基准数据、按 host 限流与容量规划见 [性能与容量](https://github.com/yuanqimanong/IPClick/wiki/Performance)。
162
- 初始化配置(推荐):
161
+ 使用以下命令初始化配置。
163
162
 
164
163
  ```bash
165
164
  ipclick init
166
165
  ```
167
166
 
168
- 一次生成两份文件:`ipclick.toml`(行为配置)和 `.env`(机密)。`.env` 用 **600**
169
- 权限创建、预填一个随机 Web 密码;`.gitignore` 只在已存在时才会被追加一行 `.env`
170
- ——干净的新目录里 `init` 只会打一句提醒,不会替你创建 `.gitignore`,这种情况得自己加。
171
- 文件已存在时拒绝覆盖(要覆盖加 `--force`)。
167
+ 该命令生成 `ipclick.toml`(行为配置)和 `.env`(机密),并在 `.env` 中填入随机 Web 密码。
168
+ POSIX 上 `.env` 使用 **600** 权限,Windows 上由目录的 NTFS ACL 控制访问。
169
+ 已有 `.gitignore` 会补入缺少的 `.env` 规则。新目录需要自行创建 `.gitignore` 并加入该项。
170
+ 已有配置文件会被保留,需要覆盖时使用 `--force`。
172
171
 
173
- 也可以只要模板:
172
+ 也可以分别导出配置模板。
174
173
 
175
174
  ```bash
176
175
  ipclick -e > ipclick.toml # 配置模板(-e 等价于 -e toml)
@@ -195,15 +194,14 @@ IPCLICK_PROXY_AUTH_PASSWORD 代理密码
195
194
  IPCLICK_CLUSTER_SECRET 集群共享密钥(所有节点一致;每台机器的令牌由它派生)
196
195
  ```
197
196
 
198
- **机密写在 `ipclick.toml` 里仍然生效**——受信环境里图省事是合理的。只是启动时会
199
- 点名提醒(`ipclick.toml` 通常要进版本库,机密会跟着进 git、备份、CI 日志)。
200
- 确实想这么放就设 `[SECURITY].allow_secrets_in_config = true` 关掉提醒。
201
- 两边都写时**环境变量优先**。
197
+ 机密也可以写入 `ipclick.toml`,启动时会列出提醒,避免凭据随配置进入 Git、备份或 CI 日志。
198
+ 确认允许这种配置时,可设 `[SECURITY].allow_secrets_in_config = true` 关闭提醒。
199
+ 同一项同时出现在配置文件和环境变量中时,**环境变量优先**。
202
200
 
203
- `ipclick config-info` 会逐项显示每个机密**来自哪里**(环境变量 / 配置文件 / 未配置)
204
- ——配错了地方是最难自己发现的一类问题。
201
+ `ipclick config-info` 会逐项显示机密来源(环境变量 / 配置文件 / 未配置),
202
+ 同时列出当前生效的 TLS、鉴权、限流、浏览器引擎和运行模式。
205
203
 
206
- 查看当前**实际生效**的配置——TLS、鉴权、限流、浏览器引擎、运行模式一目了然:
204
+ 查看生效配置。
207
205
 
208
206
  ```bash
209
207
  ipclick config-info
@@ -237,17 +235,17 @@ with Downloader() as downloader:
237
235
  print(f"请求失败: {response.error}")
238
236
  ```
239
237
 
240
- `request()` 及各便捷方法不会因为网络问题抛异常——传输失败和 DNS 解析失败都返回
241
- `status_code == -1` 且 `error` 非空的 `DownloadResponse`。会抛出来的是这几类:
238
+ `request()` 及各便捷方法在传输失败、DNS 解析失败时返回 `status_code == -1` 且 `error`
239
+ 非空的 `DownloadResponse`。参数、准入策略或依赖问题会抛出下表中的异常。
242
240
 
243
241
  | 异常 | 什么时候 |
244
242
  |---|---|
245
243
  | `ValidationError` | 参数不合法(URL 为空、方法不支持…),**以及重定向超过 10 跳** |
246
244
  | `URLNotAllowedError` | 入口 URL **或任何一跳重定向目标**被 `[SECURITY]` 拒绝 |
247
245
  | `AdapterError` | 适配器/浏览器没准备好 |
248
- | `HostLimitTimeout` | 等 per-host 配额超时
246
+ | `HostLimitTimeout` | 等 per-host 配额超时 |
249
247
 
250
- 更多用法——代理、流式、断点续传、批量、异步、指定适配器——见
248
+ 代理、流式下载、断点续传、批量、异步和指定适配器的用法见
251
249
  [SDK 用法](https://github.com/yuanqimanong/IPClick/wiki/SDK-Usage)。
252
250
 
253
251
  ## 🖥️ Web 管理端
@@ -256,9 +254,9 @@ with Downloader() as downloader:
256
254
  ipclick run --web # 或 -w
257
255
  ```
258
256
 
259
- 启动时把访问地址、用户名和密码打到控制台。**没配密码就随机生成**(重启失效),而不是给个
260
- admin/admin 之类的默认值——默认弱口令是这类管理界面被打穿的头号原因。要固定下来用环境变量
261
- (推荐,密码不该进版本库),或写进 `.env`,或配 `[WEB].username` / `password`:
257
+ 启动时会输出访问地址和用户名。未配置密码时会随机生成并打印,重启后失效。
258
+ 固定凭据可通过环境变量、`.env` 或 `[WEB].username` / `password` 配置,已配置的密码不会回显。
259
+ 推荐将凭据放在环境变量或 `.env` 中。
262
260
 
263
261
  ```bash
264
262
  IPCLICK_WEB_USER=ops IPCLICK_WEB_PASSWORD=... ipclick run -w
@@ -273,25 +271,25 @@ IPCLICK_WEB_USER=ops IPCLICK_WEB_PASSWORD=... ipclick run -w
273
271
  | **配置** `/config` | 75 项配置分 10 组(可折叠),写回 `ipclick.toml`(保留注释、留 `.bak`);隧道代理整串粘贴自动拆分(地址进 toml、凭据进 `.env`);集群节点增删 |
274
272
  | **AI 接入** `/skill` | 技能包全文、安装命令与 `SKILL.md` 下载 |
275
273
 
276
- 「试一试」走的是本进程 `TaskService` 的**同一条**代码路径——SSRF 准入、限流、以及开了转发时
277
- 的分发全都照常生效,请求也会像真实请求一样出现在请求流里。
274
+ 「试一试」通过本进程的 `TaskService` 执行请求,应用相同的 SSRF 准入、限流和转发规则,
275
+ 执行记录也会出现在请求流中。
278
276
 
279
- **不能从界面改的**:`[SECURITY]` 全部、Web 自己的登录凭据、集群共享密钥与各节点 token、
277
+ 涉及安全边界的设置需在部署时配置,界面会限制编辑这些项目。
278
+ 包括 `[SECURITY]` 全部、Web 登录凭据、集群共享密钥与节点 token、
280
279
  `[BROWSER].allow_scripts`、`[BROWSER].executable_path`、`[CLUSTER].allow_remote_install`。
281
- 后三项不是"配置"是"授权"——它们能让调用方在服务端跑任意东西,不该和超时、线程数摆在同一个
282
- 表单里让人顺手划过去。
280
+ 后三项分别影响页内脚本执行、本机程序启动和远程装包权限。
283
281
 
284
282
  > ⚠️ 界面本身是**明文 HTTP**,默认只监听 `127.0.0.1`。要远程访问请用 SSH 隧道,或放在做了
285
283
  > TLS 终止的反向代理之后。局域网自用可以 `--web-lan`(等价于 `--web-host 0.0.0.0`),但开
286
284
  > 之前先把 `[SECURITY].auth_token` 配上——同网段的人绕过网页直连 gRPC 端口是更省事的那条路。
287
285
 
288
- 每个页面具体能干什么、写回配置的机制、前端的 CSP 与会话设计,见
286
+ 页面功能、配置写回机制、CSP 和会话设计见
289
287
  [Web 管理端](https://github.com/yuanqimanong/IPClick/wiki/Web-Console)。
290
288
 
291
289
  ## 🤖 命令行(给人,也给 AI)
292
290
 
293
- 部署那一组是四条:`init` / `run` / `health` / `config-info`,都是给人看的,**不支持
294
- `--json`**。另有一组**结构化输出**的命令供脚本和 AI 代理调用,全部支持 `-J/--json`:
291
+ `init` / `run` / `health` / `config-info` 用于部署和诊断,仅提供可读文本输出。
292
+ 供脚本和 AI 代理调用的命令支持 `-J/--json`,常用入口如下。
295
293
 
296
294
  ```bash
297
295
  ipclick status --json # 服务端在不在、这台机器能用哪些适配器
@@ -302,9 +300,8 @@ ipclick component list --json # 五个可选组件的安装状态
302
300
  ipclick config show --json # 生效配置(机密脱敏)
303
301
  ```
304
302
 
305
- **输出契约**:加 `--json` 后 **stdout 上有且只有一个 JSON 文档**,成功失败都是;日志与进度
306
- 走 stderr,所以 `ipclick ... --json | jq` 永远安全。每个文档都带 `ok` 和 `exit_code`,与进程
307
- 退出码一致。
303
+ 启用 `--json` 后,**stdout 仅输出一个 JSON 文档**,日志和进度写入 stderr,可使用
304
+ `ipclick ... --json | jq` 处理结果。文档包含 `ok` 和 `exit_code`,其中 `exit_code` 与进程退出码一致。
308
305
 
309
306
  | 退出码 | 含义 | 往哪儿查 |
310
307
  |---|---|---|
@@ -315,16 +312,14 @@ ipclick config show --json # 生效配置(机密脱敏)
315
312
  | 4 | 鉴权失败 | 令牌(`IPCLICK_AUTH_TOKEN`) |
316
313
  | 5 | 参数被服务端拒绝,或本地配置不合法 | 调用参数或 `ipclick.toml` |
317
314
 
318
- `status` 为 `-1` **或 `null`** 都表示没拿到 HTTP 响应,所以别拿 `d["status"] == -1` 当判据,
319
- 先读 `exit_code` / `ok`;要区分方向再看 `reached_server`。
315
+ `status` 为 `-1` **或 `null`** 时表示未收到 HTTP 响应。判断执行结果时先读取
316
+ `exit_code` / `ok`,再用 `reached_server` 区分请求是否到达 IPClick 服务端。
320
317
 
321
- `--json` 时 `body` 默认截断到 **65536 个字符**(不是字节——抓中文页面时字节数会大出两三倍),
322
- 并给出 `body_truncated: true`。`--max-body 0` 不截断,`-o <file>` 写文件也不截断。响应不是
323
- 合法 UTF-8(图片、压缩流)时 `body_encoding` 为 `base64`,超过上限的给空串——半截 base64 解
324
- 不出任何东西。
318
+ `--json` 输出的 `body` 默认最多保留 **65536 个字符**,超出时标记 `body_truncated: true`。
319
+ `--max-body 0` 可输出完整内容,`-o <file>` 写文件也保留完整响应体。图片、压缩流等非 UTF-8
320
+ 响应使用 `body_encoding` 为 `base64` 的表示,超过上限时返回空串,避免输出无法解码的片段。
325
321
 
326
- 给 AI 代理装技能包(一份随 wheel 分发的 Markdown,讲清楚何时该用 IPClick、上面这套输出契约、
327
- 以及几个最容易踩的坑):
322
+ 随 wheel 分发的技能包介绍适用场景、输出格式和调用注意事项。可用以下命令安装到 AI 代理目录。
328
323
 
329
324
  ```bash
330
325
  ipclick skill install # 写到 .claude/skills/ipclick/SKILL.md
@@ -343,11 +338,10 @@ docker run -d -p 9528:9528 -p 127.0.0.1:9527:9527 --name ipclick ipclick:latest
343
338
  ipclick run -w --web-lan
344
339
  ```
345
340
 
346
- 镜像基于 **python:3.14-slim**,多阶段构建、非 root 运行,并且**默认自带浏览器渲染
347
- 能力**——patchright 和 chromium 本体都打进去了,拉下来直接 `-a patchright` 就能渲染
348
- JS 页面,不用在容器里再下一遍。
341
+ 镜像基于 **python:3.14-slim**,采用多阶段构建并以非 root 用户运行。
342
+ 默认包含 patchright 和 Chromium 本体,使用 `-a patchright` 即可渲染 JS 页面。
349
343
 
350
- 不需要渲染就构建精简版,省 1.5 GB:
344
+ 纯 HTTP 场景可构建精简版,减少约 1.5 GB 的浏览器文件。
351
345
 
352
346
  ```bash
353
347
  docker build -f docker/Dockerfile --build-arg ENGINE=none -t ipclick:slim .
@@ -358,21 +352,20 @@ docker build -f docker/Dockerfile --build-arg ENGINE=none -t ipclick:slim .
358
352
 
359
353
  ## 🚧 已知限制
360
354
 
361
- 配置文件里出现、但改了不会有效果的项,这里如实列出。
355
+ 部署前需了解以下功能边界。
362
356
 
363
357
  - **服务发现只支持 `static` 与 `dns`**。etcd / Consul 的原生 API 没接(Consul 可以走它的
364
358
  DNS 接口)。
365
- - **分块下载与临时存储**未实现。流式通路已经就绪,这两项可以在其上做,目前还没做。
359
+ - **分块下载与临时存储**未实现。目前可使用流式下载处理大文件。
366
360
  - **集群流式只有建流这一步会故障转移**。流建立之后中途断掉不会自动重连——那需要 Range 请求
367
361
  才能不重复数据。批量请求整批发给同一个节点,不跨节点拆分。
368
362
  - **没有文件上传字段**。要发 multipart 就自己拼好请求体,用 `data=<bytes>` 加上
369
363
  `Content-Type: multipart/form-data; boundary=...` 发出去。`files=` 不在协议里,进程内直接
370
364
  传给适配器时会抛 `ValidationError`。
371
365
  - **查询参数原样传递。** `params={"start": "2024-01-01"}` 发出去就是 `start=2024-01-01`。
372
- - **两次请求之间不保持会话。** `curl_cffi` / `niquests` 适配器按(代理, 证书校验, 指纹)缓存
373
- Session,但**每次请求前都会清空这个 session 的 cookie jar**——服务端没有调用方会话的概念,
374
- 共用 session 却保留 cookie 等于跨调用方串号。单次请求内部(含重定向链)的 cookie 传递照常
375
- 由底层库处理。需要保持会话请在请求里显式传 `cookies=`。
366
+ - **两次请求之间不保持 Cookie 会话。** `curl_cffi` / `niquests` 会复用按连接属性缓存的
367
+ Session,并在每次请求前清空 cookie jar,避免不同调用方共用 Cookie。
368
+ 单次请求内部(含重定向链)的 Cookie 由底层库处理。需要携带会话状态时,请在请求中显式传入 `cookies=`。
376
369
 
377
370
  ## 🛠️ 开发
378
371
 
@@ -384,16 +377,17 @@ uv run ruff format src/ tests/ # 代码格式化
384
377
  uv run basedpyright src/ tests/ # 类型检查
385
378
  ```
386
379
 
387
- 修改 `src/ipclick/dto/proto/task.proto` 后需重新生成代码:
380
+ 修改 `src/ipclick/dto/proto/task.proto` 后需重新生成代码。
388
381
 
389
382
  ```bash
390
383
  uv run python src/ipclick/dto/proto/generate.py
391
384
  ```
392
385
 
393
- 依赖按用途分组(`test` / `lint` / `proto` / `release`,`dev` 是聚合组),
394
- CI 里每个 job 只装自己那一份。`[camoufox]` 与 `[playwright]` 在
395
- `[tool.uv].conflicts` 里声明为互斥(camoufox 钉着 `playwright<1.61`),
396
- 所以装可选适配器时要逐个 `--extra` 列出,不能用 `--all-extras`。
386
+ 依赖按用途分组(`test` / `lint` / `proto` / `release`,`dev` 是聚合组),CI 按任务安装依赖。
387
+ `[camoufox]` 与 `[playwright]`、`[linux]` 与 `[playwright]` 在 `[tool.uv].conflicts` 中分别声明为互斥。
388
+ 当前 Camoufox 0.5.9 要求 `playwright<1.63`,锁文件为 Camoufox 和独立 Playwright extra 分别解析依赖。
389
+ 安装可选适配器时,请逐项列出 `--extra`。
390
+ 同时选择互斥组合或使用 `--all-extras` 会导致解析失败。
397
391
 
398
392
  项目结构、门禁清单、提交规范与发布流程见 [开发](https://github.com/yuanqimanong/IPClick/wiki/Development)。
399
393