crawler4j-sdk 0.2.0__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.
- crawler4j_sdk-0.2.0/PKG-INFO +297 -0
- crawler4j_sdk-0.2.0/README.md +287 -0
- crawler4j_sdk-0.2.0/crawler4j_sdk.egg-info/PKG-INFO +297 -0
- crawler4j_sdk-0.2.0/crawler4j_sdk.egg-info/SOURCES.txt +21 -0
- crawler4j_sdk-0.2.0/crawler4j_sdk.egg-info/dependency_links.txt +1 -0
- crawler4j_sdk-0.2.0/crawler4j_sdk.egg-info/entry_points.txt +2 -0
- crawler4j_sdk-0.2.0/crawler4j_sdk.egg-info/requires.txt +3 -0
- crawler4j_sdk-0.2.0/crawler4j_sdk.egg-info/top_level.txt +1 -0
- crawler4j_sdk-0.2.0/pyproject.toml +29 -0
- crawler4j_sdk-0.2.0/setup.cfg +4 -0
- crawler4j_sdk-0.2.0/src/__init__.py +58 -0
- crawler4j_sdk-0.2.0/src/_version.py +48 -0
- crawler4j_sdk-0.2.0/src/assembler.py +216 -0
- crawler4j_sdk-0.2.0/src/base.py +90 -0
- crawler4j_sdk-0.2.0/src/cli/__init__.py +13 -0
- crawler4j_sdk-0.2.0/src/cli/commands.py +2084 -0
- crawler4j_sdk-0.2.0/src/cli/templates.py +426 -0
- crawler4j_sdk-0.2.0/src/context.py +39 -0
- crawler4j_sdk-0.2.0/src/env_selector.py +56 -0
- crawler4j_sdk-0.2.0/src/py.typed +0 -0
- crawler4j_sdk-0.2.0/src/result.py +5 -0
- crawler4j_sdk-0.2.0/src/signal.py +5 -0
- crawler4j_sdk-0.2.0/src/workflow.py +102 -0
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: crawler4j-sdk
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Crawler4j module SDK and CLI for building standard crawler4j modules
|
|
5
|
+
Requires-Python: >=3.12
|
|
6
|
+
Description-Content-Type: text/markdown
|
|
7
|
+
Requires-Dist: crawler4j-contracts<1.0.0,>=0.2.0
|
|
8
|
+
Requires-Dist: aiohttp>=3.13.0
|
|
9
|
+
Requires-Dist: pyyaml>=6.0.3
|
|
10
|
+
|
|
11
|
+
# Crawler4j SDK
|
|
12
|
+
|
|
13
|
+
用于构建 `crawler4j` 标准模块项目的 SDK 与 CLI。
|
|
14
|
+
|
|
15
|
+
当前源码与开发者文档统一以 `0.2.0` 为基线。注意:模块自己的 `module.yaml.version` 可以独立演进,因此示例 ZIP 名称里的 `0.1.0` 仅表示模块版本,不代表 SDK 仍停留在 `0.1.x`。
|
|
16
|
+
|
|
17
|
+
## 使用 CLI 的两种方式
|
|
18
|
+
|
|
19
|
+
方式 1:安装后长期使用
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
uv tool install crawler4j-sdk
|
|
23
|
+
crawler4j --help
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
方式 2:不安装,直接一次性运行
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
uvx --from crawler4j-sdk crawler4j --help
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
如果你已经创建了 model 项目,并且项目目录里执行过 `uv sync`,也可以直接在项目内使用:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
uv run crawler4j --help
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## 快速开始
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
from crawler4j_sdk import TaskContext, TaskResult, TaskScript
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class MyTask(TaskScript):
|
|
45
|
+
name = "my_task"
|
|
46
|
+
display_name = "我的任务"
|
|
47
|
+
description = "这是一个示例任务"
|
|
48
|
+
default_config = {"timeout": 30}
|
|
49
|
+
|
|
50
|
+
async def execute(self, ctx: TaskContext) -> TaskResult:
|
|
51
|
+
if not ctx.page:
|
|
52
|
+
return TaskResult.fail(message="当前运行环境没有可用的浏览器 Page")
|
|
53
|
+
|
|
54
|
+
await ctx.page.goto("https://example.com")
|
|
55
|
+
timeout = ctx.get_config("timeout", 30)
|
|
56
|
+
ctx.logger.info(f"任务执行中,timeout={timeout}")
|
|
57
|
+
|
|
58
|
+
if ctx.tools and ctx.tools.has_tool("db.get_state"):
|
|
59
|
+
cursor = ctx.tools.call("db.get_state", key="demo:cursor")
|
|
60
|
+
ctx.logger.info(f"当前游标: {cursor}")
|
|
61
|
+
|
|
62
|
+
return TaskResult.ok(message="完成")
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## 核心 API
|
|
66
|
+
|
|
67
|
+
### 稳定契约(同 MAJOR 版本内冻结)
|
|
68
|
+
|
|
69
|
+
| 类型 | 说明 |
|
|
70
|
+
|:---|:---|
|
|
71
|
+
| `TaskScript` | 原子任务基类 |
|
|
72
|
+
| `TaskFlow` | 工作流编排基类 |
|
|
73
|
+
| `ModuleAssembler` | 标准模块根入口组装器 |
|
|
74
|
+
| `env_selector` / `EnvSelectorInfo` | 环境选择器声明与元信息 |
|
|
75
|
+
| `TaskContext` | 任务执行上下文 |
|
|
76
|
+
| `TaskResult` | 任务结果模型 |
|
|
77
|
+
| `TaskSignal` | 模块通知 ATM 的流程控制信号 |
|
|
78
|
+
| `TaskSignalAction` | `TaskSignal` 的动作枚举 |
|
|
79
|
+
| `EnvAction` | 任务结束后 ATM 对运行环境执行的动作 |
|
|
80
|
+
| `ToolsCapability` | Core 注入的统一工具入口 |
|
|
81
|
+
| `ToolSpec` | Core 工具声明元数据(`name` / `description` / `is_async`) |
|
|
82
|
+
|
|
83
|
+
### TaskContext 常用能力
|
|
84
|
+
|
|
85
|
+
| 属性/方法 | 说明 |
|
|
86
|
+
|:---|:---|
|
|
87
|
+
| `ctx.page` | Playwright Page 对象 |
|
|
88
|
+
| `ctx.logger` | 日志记录器 |
|
|
89
|
+
| `ctx.http` | HTTP 客户端 |
|
|
90
|
+
| `ctx.config` | 宿主持久化后的模块/工作流配置视图 |
|
|
91
|
+
| `ctx.state` | 共享状态 |
|
|
92
|
+
| `ctx.runtime` | ATM/Debug 写入的执行态输入与元数据 |
|
|
93
|
+
| `ctx.run_subtask()` | 调用子任务 |
|
|
94
|
+
| `ctx.should_stop()` | 检查停止标志 |
|
|
95
|
+
| `ctx.screenshot()` | 截图 |
|
|
96
|
+
| `ctx.emit_signal()` | 向 ATM 发出流程控制信号 |
|
|
97
|
+
| `ctx.tools` | 宿主注入的统一扩展工具入口 |
|
|
98
|
+
|
|
99
|
+
固定边界如下:
|
|
100
|
+
|
|
101
|
+
- `ctx.get_config()` / `ctx.config` 只读取宿主持久化的模块级配置和工作流级覆盖
|
|
102
|
+
- `workflow`、`devel_mode`、`execution_params`、`job_params`、`params`、`creation_params` 统一读取 `ctx.runtime`
|
|
103
|
+
|
|
104
|
+
`ctx.run_subtask()` 的返回语义:
|
|
105
|
+
|
|
106
|
+
- 成功且子任务返回了 `TaskResult.data` 时,直接返回该 payload
|
|
107
|
+
- 成功但没有 payload 时,返回 `True`
|
|
108
|
+
- 失败时,返回一个 `dict` 风格但布尔值为 `False` 的失败结果,至少保留 `status=failed` 以及可用的 `message` / `error`
|
|
109
|
+
|
|
110
|
+
## Core 工具能力边界
|
|
111
|
+
|
|
112
|
+
模块侧只应通过 `ctx.tools.call(...)` 使用宿主注入的扩展能力。
|
|
113
|
+
|
|
114
|
+
当前内置工具名如下:
|
|
115
|
+
|
|
116
|
+
- `db.list_records`
|
|
117
|
+
- `db.replace_records`
|
|
118
|
+
- `db.acquire_lock`
|
|
119
|
+
- `db.release_lock`
|
|
120
|
+
- `db.is_locked`
|
|
121
|
+
- `db.get_state`
|
|
122
|
+
- `db.set_state`
|
|
123
|
+
- `db.exists_state`
|
|
124
|
+
- `ip_pool.pick_proxy`
|
|
125
|
+
- `env.set_proxy`
|
|
126
|
+
- `ui.declare_data_table`
|
|
127
|
+
- `ui.get_data_table`
|
|
128
|
+
- `captcha.match_slider`
|
|
129
|
+
- `captcha.match_click_targets`
|
|
130
|
+
|
|
131
|
+
如果你需要发现能力,而不是直接调用固定名字,可以使用:
|
|
132
|
+
|
|
133
|
+
- `ctx.tools.has_tool(name)`
|
|
134
|
+
- `ctx.tools.list_tools()`
|
|
135
|
+
|
|
136
|
+
`ctx.tools.list_tools()` 返回的每一项都是 `ToolSpec`,其中:
|
|
137
|
+
|
|
138
|
+
- `name` 是工具名
|
|
139
|
+
- `description` 是工具描述
|
|
140
|
+
- `is_async` 表示这个工具是否需要 `await ctx.tools.call(...)`
|
|
141
|
+
|
|
142
|
+
不要在模块里直接连接宿主数据库,也不要假设存在 ORM Session、原生 SQLite 连接或其他私有存储对象。
|
|
143
|
+
|
|
144
|
+
### 常见调用示例
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
if ctx.tools and ctx.tools.has_tool("db.list_records"):
|
|
148
|
+
records = ctx.tools.call("db.list_records", dataset="orders")
|
|
149
|
+
ctx.tools.call("db.replace_records", dataset="orders", records=records)
|
|
150
|
+
|
|
151
|
+
if ctx.tools and ctx.tools.has_tool("env.set_proxy"):
|
|
152
|
+
await ctx.tools.call("env.set_proxy", env_id=ctx.env_id, proxy_value="http://127.0.0.1:8888")
|
|
153
|
+
|
|
154
|
+
if ctx.tools and ctx.tools.has_tool("captcha.match_slider"):
|
|
155
|
+
result = ctx.tools.call(
|
|
156
|
+
"captcha.match_slider",
|
|
157
|
+
background_image=bg_bytes,
|
|
158
|
+
puzzle_piece_image=piece_bytes,
|
|
159
|
+
)
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
## CLI 命令
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
# 初始化模块项目:生成标准骨架、module.yaml、module_runtime.py
|
|
166
|
+
uvx --from crawler4j-sdk crawler4j module init my_model --repo owner/my_model
|
|
167
|
+
|
|
168
|
+
# 查看当前模块的版本、仓库、默认工作流、页面入口和数据表入口
|
|
169
|
+
uv run crawler4j module show
|
|
170
|
+
|
|
171
|
+
# 创建任务脚本:只写 tasks/<name>.py
|
|
172
|
+
uv run crawler4j task create login
|
|
173
|
+
|
|
174
|
+
# 创建工作流:写 workflows/<name>.py,并同步更新 module.yaml.workflows
|
|
175
|
+
uv run crawler4j workflow create sync_orders
|
|
176
|
+
|
|
177
|
+
# 创建代码型页面:写 ui/<name>.py,并设置 ui_extension.entry
|
|
178
|
+
uv run crawler4j page create dashboard
|
|
179
|
+
|
|
180
|
+
# 创建受控数据表:注册 core:data_table:<view_id>,并补 declare_ui 骨架
|
|
181
|
+
uv run crawler4j data-table create accounts
|
|
182
|
+
|
|
183
|
+
# 创建环境选择器:在 module_runtime.py 里追加 @env_selector(...) 函数
|
|
184
|
+
uv run crawler4j env-selector create pick_ready
|
|
185
|
+
|
|
186
|
+
# 设置默认配置模板
|
|
187
|
+
uv run crawler4j config set module --file defaults.yaml
|
|
188
|
+
|
|
189
|
+
# 发布前完整校验
|
|
190
|
+
uv run crawler4j check full
|
|
191
|
+
|
|
192
|
+
# 构建宿主可安装 ZIP
|
|
193
|
+
uv run crawler4j package build
|
|
194
|
+
|
|
195
|
+
# 发布本地 ZIP 到 GitHub Release
|
|
196
|
+
uv run crawler4j release publish --dry-run
|
|
197
|
+
|
|
198
|
+
# 通过 SDK CLI 桥接宿主能力
|
|
199
|
+
uv run crawler4j host devlink add /path/to/module
|
|
200
|
+
uv run crawler4j host install preview dist/my_model-0.1.0.zip --skip-remote-check
|
|
201
|
+
uv run crawler4j host upgrade check my_model
|
|
202
|
+
uv run crawler4j host debug config
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
当前命令树按“模块元素”和“生命周期动作”分组:
|
|
206
|
+
|
|
207
|
+
- `module`:初始化模块项目,或维护 `repo / version / default-workflow`
|
|
208
|
+
- `task`:管理 `tasks/` 里的 `TaskScript`
|
|
209
|
+
- `workflow`:管理 `workflows/` 和 `module.yaml.workflows`
|
|
210
|
+
- `page`:管理代码型页面和 `ui_extension.entry`
|
|
211
|
+
- `data-table`:管理受控 `core:data_table:<id>` 入口
|
|
212
|
+
- `env-selector`:管理 `module_runtime.py` 里的环境选择策略函数
|
|
213
|
+
- `config`:管理 `module.yaml.config_defaults`
|
|
214
|
+
- `package`:构建和校验安装 ZIP
|
|
215
|
+
- `release`:看本地发布状态、检查 GitHub Release 最新版本、发布 Release 资产
|
|
216
|
+
- `host`:通过 SDK CLI 桥接宿主的 DevLink、安装、升级和调试配置
|
|
217
|
+
- `check`:运行 `structure / release / full` 三档完整性校验
|
|
218
|
+
|
|
219
|
+
`module init` 会默认:
|
|
220
|
+
|
|
221
|
+
- 生成 `.gitignore`
|
|
222
|
+
- 生成 `.python-version`
|
|
223
|
+
- 生成 `module_runtime.py`
|
|
224
|
+
- 执行 `git init`
|
|
225
|
+
- 执行 `uv sync`
|
|
226
|
+
|
|
227
|
+
如果你在 CI 或脚本里使用 CLI,可以直接补齐 `--repo`、`--no-git`、`--no-install` 等参数。第一版命令树已经切到 `module / task / workflow / page / data-table / env-selector / config / package / release / host / check` 分组体系,不再兼容旧平铺命令。
|
|
228
|
+
|
|
229
|
+
调试主路径已经收敛到 Core 调试会话。旧的 `debug_runner.py` 辅助脚本已从宿主仓库移除,CLI 也不再生成任何本地调试壳脚本。
|
|
230
|
+
模块持久配置由宿主统一维护,`config_schema.json` / `strategy.yaml` 已不再受支持;详情页扩展数据表也应通过 `data-table create` 写入受控的 `core:data_table:<view_id>` 入口,而不是手改 `module.yaml`。
|
|
231
|
+
|
|
232
|
+
## 工作流示例
|
|
233
|
+
|
|
234
|
+
```python
|
|
235
|
+
from crawler4j_sdk import TaskContext, TaskFlow
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class MyWorkflow(TaskFlow):
|
|
239
|
+
name = "my_workflow"
|
|
240
|
+
|
|
241
|
+
async def run(self, ctx: TaskContext) -> None:
|
|
242
|
+
await ctx.run_subtask("login")
|
|
243
|
+
|
|
244
|
+
while not ctx.should_stop():
|
|
245
|
+
ctx.state["phase"] = "claim"
|
|
246
|
+
task = await ctx.run_subtask("claim_task")
|
|
247
|
+
if not task:
|
|
248
|
+
break
|
|
249
|
+
|
|
250
|
+
ctx.state["phase"] = "process"
|
|
251
|
+
await ctx.run_subtask("process", task=task)
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
## 运行期控制信号
|
|
255
|
+
|
|
256
|
+
模块如果需要让 ATM 接管流程动作,例如等待人工确认或在失败后销毁环境,应通过 `TaskSignal`,而不是直接操作宿主运行环境。
|
|
257
|
+
|
|
258
|
+
```python
|
|
259
|
+
from crawler4j_sdk import EnvAction, TaskResult, TaskSignal
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
return TaskResult.fail(
|
|
263
|
+
message="检测到黑号",
|
|
264
|
+
error="black_account",
|
|
265
|
+
signal=TaskSignal.fail(
|
|
266
|
+
message="检测到黑号",
|
|
267
|
+
error="black_account",
|
|
268
|
+
env_action=EnvAction.DESTROY,
|
|
269
|
+
),
|
|
270
|
+
)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
当前 `TaskScript` / `TaskFlow` 自身只有一个稳定入口方法:`execute(ctx)` 或 `run(ctx)`。
|
|
274
|
+
`module_runtime.py` 现在是标准模块文件,不再是可选扩展点。模块级生命周期统一在其中实现:
|
|
275
|
+
|
|
276
|
+
- `prepare_env`
|
|
277
|
+
- `declare_ui`
|
|
278
|
+
- `@env_selector(...)` 声明环境选择回调,供 ATM 的“选择环境”模式调用
|
|
279
|
+
- `init_env`
|
|
280
|
+
- `before_run`
|
|
281
|
+
- `on_success`
|
|
282
|
+
- `on_failure`
|
|
283
|
+
- `on_timeout`
|
|
284
|
+
- `on_cleanup`
|
|
285
|
+
|
|
286
|
+
脚手架默认会生成两个示例环境选择器:
|
|
287
|
+
|
|
288
|
+
- `return_none`:占位选择器,固定返回 `None`,用于提醒开发者必须替换成真实逻辑
|
|
289
|
+
- `random_ready`:从当前 `ready` 候选环境里随机选择一个
|
|
290
|
+
|
|
291
|
+
`on_cleanup` 发生在 ATM 完成环境动作之后。如果模块需要在环境已删除后清理自己的数据,应在 `on_cleanup` 中读取 `ctx.runtime["env_action"]`,而不是再增加一套额外的“环境删除 hook”。
|
|
292
|
+
|
|
293
|
+
## 版本兼容
|
|
294
|
+
|
|
295
|
+
- Python: `>= 3.12`
|
|
296
|
+
- 遵循语义化版本(SemVer)
|
|
297
|
+
- 当前源码包版本:`0.2.0`
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
# Crawler4j SDK
|
|
2
|
+
|
|
3
|
+
用于构建 `crawler4j` 标准模块项目的 SDK 与 CLI。
|
|
4
|
+
|
|
5
|
+
当前源码与开发者文档统一以 `0.2.0` 为基线。注意:模块自己的 `module.yaml.version` 可以独立演进,因此示例 ZIP 名称里的 `0.1.0` 仅表示模块版本,不代表 SDK 仍停留在 `0.1.x`。
|
|
6
|
+
|
|
7
|
+
## 使用 CLI 的两种方式
|
|
8
|
+
|
|
9
|
+
方式 1:安装后长期使用
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
uv tool install crawler4j-sdk
|
|
13
|
+
crawler4j --help
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
方式 2:不安装,直接一次性运行
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
uvx --from crawler4j-sdk crawler4j --help
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
如果你已经创建了 model 项目,并且项目目录里执行过 `uv sync`,也可以直接在项目内使用:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uv run crawler4j --help
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## 快速开始
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
from crawler4j_sdk import TaskContext, TaskResult, TaskScript
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class MyTask(TaskScript):
|
|
35
|
+
name = "my_task"
|
|
36
|
+
display_name = "我的任务"
|
|
37
|
+
description = "这是一个示例任务"
|
|
38
|
+
default_config = {"timeout": 30}
|
|
39
|
+
|
|
40
|
+
async def execute(self, ctx: TaskContext) -> TaskResult:
|
|
41
|
+
if not ctx.page:
|
|
42
|
+
return TaskResult.fail(message="当前运行环境没有可用的浏览器 Page")
|
|
43
|
+
|
|
44
|
+
await ctx.page.goto("https://example.com")
|
|
45
|
+
timeout = ctx.get_config("timeout", 30)
|
|
46
|
+
ctx.logger.info(f"任务执行中,timeout={timeout}")
|
|
47
|
+
|
|
48
|
+
if ctx.tools and ctx.tools.has_tool("db.get_state"):
|
|
49
|
+
cursor = ctx.tools.call("db.get_state", key="demo:cursor")
|
|
50
|
+
ctx.logger.info(f"当前游标: {cursor}")
|
|
51
|
+
|
|
52
|
+
return TaskResult.ok(message="完成")
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## 核心 API
|
|
56
|
+
|
|
57
|
+
### 稳定契约(同 MAJOR 版本内冻结)
|
|
58
|
+
|
|
59
|
+
| 类型 | 说明 |
|
|
60
|
+
|:---|:---|
|
|
61
|
+
| `TaskScript` | 原子任务基类 |
|
|
62
|
+
| `TaskFlow` | 工作流编排基类 |
|
|
63
|
+
| `ModuleAssembler` | 标准模块根入口组装器 |
|
|
64
|
+
| `env_selector` / `EnvSelectorInfo` | 环境选择器声明与元信息 |
|
|
65
|
+
| `TaskContext` | 任务执行上下文 |
|
|
66
|
+
| `TaskResult` | 任务结果模型 |
|
|
67
|
+
| `TaskSignal` | 模块通知 ATM 的流程控制信号 |
|
|
68
|
+
| `TaskSignalAction` | `TaskSignal` 的动作枚举 |
|
|
69
|
+
| `EnvAction` | 任务结束后 ATM 对运行环境执行的动作 |
|
|
70
|
+
| `ToolsCapability` | Core 注入的统一工具入口 |
|
|
71
|
+
| `ToolSpec` | Core 工具声明元数据(`name` / `description` / `is_async`) |
|
|
72
|
+
|
|
73
|
+
### TaskContext 常用能力
|
|
74
|
+
|
|
75
|
+
| 属性/方法 | 说明 |
|
|
76
|
+
|:---|:---|
|
|
77
|
+
| `ctx.page` | Playwright Page 对象 |
|
|
78
|
+
| `ctx.logger` | 日志记录器 |
|
|
79
|
+
| `ctx.http` | HTTP 客户端 |
|
|
80
|
+
| `ctx.config` | 宿主持久化后的模块/工作流配置视图 |
|
|
81
|
+
| `ctx.state` | 共享状态 |
|
|
82
|
+
| `ctx.runtime` | ATM/Debug 写入的执行态输入与元数据 |
|
|
83
|
+
| `ctx.run_subtask()` | 调用子任务 |
|
|
84
|
+
| `ctx.should_stop()` | 检查停止标志 |
|
|
85
|
+
| `ctx.screenshot()` | 截图 |
|
|
86
|
+
| `ctx.emit_signal()` | 向 ATM 发出流程控制信号 |
|
|
87
|
+
| `ctx.tools` | 宿主注入的统一扩展工具入口 |
|
|
88
|
+
|
|
89
|
+
固定边界如下:
|
|
90
|
+
|
|
91
|
+
- `ctx.get_config()` / `ctx.config` 只读取宿主持久化的模块级配置和工作流级覆盖
|
|
92
|
+
- `workflow`、`devel_mode`、`execution_params`、`job_params`、`params`、`creation_params` 统一读取 `ctx.runtime`
|
|
93
|
+
|
|
94
|
+
`ctx.run_subtask()` 的返回语义:
|
|
95
|
+
|
|
96
|
+
- 成功且子任务返回了 `TaskResult.data` 时,直接返回该 payload
|
|
97
|
+
- 成功但没有 payload 时,返回 `True`
|
|
98
|
+
- 失败时,返回一个 `dict` 风格但布尔值为 `False` 的失败结果,至少保留 `status=failed` 以及可用的 `message` / `error`
|
|
99
|
+
|
|
100
|
+
## Core 工具能力边界
|
|
101
|
+
|
|
102
|
+
模块侧只应通过 `ctx.tools.call(...)` 使用宿主注入的扩展能力。
|
|
103
|
+
|
|
104
|
+
当前内置工具名如下:
|
|
105
|
+
|
|
106
|
+
- `db.list_records`
|
|
107
|
+
- `db.replace_records`
|
|
108
|
+
- `db.acquire_lock`
|
|
109
|
+
- `db.release_lock`
|
|
110
|
+
- `db.is_locked`
|
|
111
|
+
- `db.get_state`
|
|
112
|
+
- `db.set_state`
|
|
113
|
+
- `db.exists_state`
|
|
114
|
+
- `ip_pool.pick_proxy`
|
|
115
|
+
- `env.set_proxy`
|
|
116
|
+
- `ui.declare_data_table`
|
|
117
|
+
- `ui.get_data_table`
|
|
118
|
+
- `captcha.match_slider`
|
|
119
|
+
- `captcha.match_click_targets`
|
|
120
|
+
|
|
121
|
+
如果你需要发现能力,而不是直接调用固定名字,可以使用:
|
|
122
|
+
|
|
123
|
+
- `ctx.tools.has_tool(name)`
|
|
124
|
+
- `ctx.tools.list_tools()`
|
|
125
|
+
|
|
126
|
+
`ctx.tools.list_tools()` 返回的每一项都是 `ToolSpec`,其中:
|
|
127
|
+
|
|
128
|
+
- `name` 是工具名
|
|
129
|
+
- `description` 是工具描述
|
|
130
|
+
- `is_async` 表示这个工具是否需要 `await ctx.tools.call(...)`
|
|
131
|
+
|
|
132
|
+
不要在模块里直接连接宿主数据库,也不要假设存在 ORM Session、原生 SQLite 连接或其他私有存储对象。
|
|
133
|
+
|
|
134
|
+
### 常见调用示例
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
if ctx.tools and ctx.tools.has_tool("db.list_records"):
|
|
138
|
+
records = ctx.tools.call("db.list_records", dataset="orders")
|
|
139
|
+
ctx.tools.call("db.replace_records", dataset="orders", records=records)
|
|
140
|
+
|
|
141
|
+
if ctx.tools and ctx.tools.has_tool("env.set_proxy"):
|
|
142
|
+
await ctx.tools.call("env.set_proxy", env_id=ctx.env_id, proxy_value="http://127.0.0.1:8888")
|
|
143
|
+
|
|
144
|
+
if ctx.tools and ctx.tools.has_tool("captcha.match_slider"):
|
|
145
|
+
result = ctx.tools.call(
|
|
146
|
+
"captcha.match_slider",
|
|
147
|
+
background_image=bg_bytes,
|
|
148
|
+
puzzle_piece_image=piece_bytes,
|
|
149
|
+
)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## CLI 命令
|
|
153
|
+
|
|
154
|
+
```bash
|
|
155
|
+
# 初始化模块项目:生成标准骨架、module.yaml、module_runtime.py
|
|
156
|
+
uvx --from crawler4j-sdk crawler4j module init my_model --repo owner/my_model
|
|
157
|
+
|
|
158
|
+
# 查看当前模块的版本、仓库、默认工作流、页面入口和数据表入口
|
|
159
|
+
uv run crawler4j module show
|
|
160
|
+
|
|
161
|
+
# 创建任务脚本:只写 tasks/<name>.py
|
|
162
|
+
uv run crawler4j task create login
|
|
163
|
+
|
|
164
|
+
# 创建工作流:写 workflows/<name>.py,并同步更新 module.yaml.workflows
|
|
165
|
+
uv run crawler4j workflow create sync_orders
|
|
166
|
+
|
|
167
|
+
# 创建代码型页面:写 ui/<name>.py,并设置 ui_extension.entry
|
|
168
|
+
uv run crawler4j page create dashboard
|
|
169
|
+
|
|
170
|
+
# 创建受控数据表:注册 core:data_table:<view_id>,并补 declare_ui 骨架
|
|
171
|
+
uv run crawler4j data-table create accounts
|
|
172
|
+
|
|
173
|
+
# 创建环境选择器:在 module_runtime.py 里追加 @env_selector(...) 函数
|
|
174
|
+
uv run crawler4j env-selector create pick_ready
|
|
175
|
+
|
|
176
|
+
# 设置默认配置模板
|
|
177
|
+
uv run crawler4j config set module --file defaults.yaml
|
|
178
|
+
|
|
179
|
+
# 发布前完整校验
|
|
180
|
+
uv run crawler4j check full
|
|
181
|
+
|
|
182
|
+
# 构建宿主可安装 ZIP
|
|
183
|
+
uv run crawler4j package build
|
|
184
|
+
|
|
185
|
+
# 发布本地 ZIP 到 GitHub Release
|
|
186
|
+
uv run crawler4j release publish --dry-run
|
|
187
|
+
|
|
188
|
+
# 通过 SDK CLI 桥接宿主能力
|
|
189
|
+
uv run crawler4j host devlink add /path/to/module
|
|
190
|
+
uv run crawler4j host install preview dist/my_model-0.1.0.zip --skip-remote-check
|
|
191
|
+
uv run crawler4j host upgrade check my_model
|
|
192
|
+
uv run crawler4j host debug config
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
当前命令树按“模块元素”和“生命周期动作”分组:
|
|
196
|
+
|
|
197
|
+
- `module`:初始化模块项目,或维护 `repo / version / default-workflow`
|
|
198
|
+
- `task`:管理 `tasks/` 里的 `TaskScript`
|
|
199
|
+
- `workflow`:管理 `workflows/` 和 `module.yaml.workflows`
|
|
200
|
+
- `page`:管理代码型页面和 `ui_extension.entry`
|
|
201
|
+
- `data-table`:管理受控 `core:data_table:<id>` 入口
|
|
202
|
+
- `env-selector`:管理 `module_runtime.py` 里的环境选择策略函数
|
|
203
|
+
- `config`:管理 `module.yaml.config_defaults`
|
|
204
|
+
- `package`:构建和校验安装 ZIP
|
|
205
|
+
- `release`:看本地发布状态、检查 GitHub Release 最新版本、发布 Release 资产
|
|
206
|
+
- `host`:通过 SDK CLI 桥接宿主的 DevLink、安装、升级和调试配置
|
|
207
|
+
- `check`:运行 `structure / release / full` 三档完整性校验
|
|
208
|
+
|
|
209
|
+
`module init` 会默认:
|
|
210
|
+
|
|
211
|
+
- 生成 `.gitignore`
|
|
212
|
+
- 生成 `.python-version`
|
|
213
|
+
- 生成 `module_runtime.py`
|
|
214
|
+
- 执行 `git init`
|
|
215
|
+
- 执行 `uv sync`
|
|
216
|
+
|
|
217
|
+
如果你在 CI 或脚本里使用 CLI,可以直接补齐 `--repo`、`--no-git`、`--no-install` 等参数。第一版命令树已经切到 `module / task / workflow / page / data-table / env-selector / config / package / release / host / check` 分组体系,不再兼容旧平铺命令。
|
|
218
|
+
|
|
219
|
+
调试主路径已经收敛到 Core 调试会话。旧的 `debug_runner.py` 辅助脚本已从宿主仓库移除,CLI 也不再生成任何本地调试壳脚本。
|
|
220
|
+
模块持久配置由宿主统一维护,`config_schema.json` / `strategy.yaml` 已不再受支持;详情页扩展数据表也应通过 `data-table create` 写入受控的 `core:data_table:<view_id>` 入口,而不是手改 `module.yaml`。
|
|
221
|
+
|
|
222
|
+
## 工作流示例
|
|
223
|
+
|
|
224
|
+
```python
|
|
225
|
+
from crawler4j_sdk import TaskContext, TaskFlow
|
|
226
|
+
|
|
227
|
+
|
|
228
|
+
class MyWorkflow(TaskFlow):
|
|
229
|
+
name = "my_workflow"
|
|
230
|
+
|
|
231
|
+
async def run(self, ctx: TaskContext) -> None:
|
|
232
|
+
await ctx.run_subtask("login")
|
|
233
|
+
|
|
234
|
+
while not ctx.should_stop():
|
|
235
|
+
ctx.state["phase"] = "claim"
|
|
236
|
+
task = await ctx.run_subtask("claim_task")
|
|
237
|
+
if not task:
|
|
238
|
+
break
|
|
239
|
+
|
|
240
|
+
ctx.state["phase"] = "process"
|
|
241
|
+
await ctx.run_subtask("process", task=task)
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## 运行期控制信号
|
|
245
|
+
|
|
246
|
+
模块如果需要让 ATM 接管流程动作,例如等待人工确认或在失败后销毁环境,应通过 `TaskSignal`,而不是直接操作宿主运行环境。
|
|
247
|
+
|
|
248
|
+
```python
|
|
249
|
+
from crawler4j_sdk import EnvAction, TaskResult, TaskSignal
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
return TaskResult.fail(
|
|
253
|
+
message="检测到黑号",
|
|
254
|
+
error="black_account",
|
|
255
|
+
signal=TaskSignal.fail(
|
|
256
|
+
message="检测到黑号",
|
|
257
|
+
error="black_account",
|
|
258
|
+
env_action=EnvAction.DESTROY,
|
|
259
|
+
),
|
|
260
|
+
)
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
当前 `TaskScript` / `TaskFlow` 自身只有一个稳定入口方法:`execute(ctx)` 或 `run(ctx)`。
|
|
264
|
+
`module_runtime.py` 现在是标准模块文件,不再是可选扩展点。模块级生命周期统一在其中实现:
|
|
265
|
+
|
|
266
|
+
- `prepare_env`
|
|
267
|
+
- `declare_ui`
|
|
268
|
+
- `@env_selector(...)` 声明环境选择回调,供 ATM 的“选择环境”模式调用
|
|
269
|
+
- `init_env`
|
|
270
|
+
- `before_run`
|
|
271
|
+
- `on_success`
|
|
272
|
+
- `on_failure`
|
|
273
|
+
- `on_timeout`
|
|
274
|
+
- `on_cleanup`
|
|
275
|
+
|
|
276
|
+
脚手架默认会生成两个示例环境选择器:
|
|
277
|
+
|
|
278
|
+
- `return_none`:占位选择器,固定返回 `None`,用于提醒开发者必须替换成真实逻辑
|
|
279
|
+
- `random_ready`:从当前 `ready` 候选环境里随机选择一个
|
|
280
|
+
|
|
281
|
+
`on_cleanup` 发生在 ATM 完成环境动作之后。如果模块需要在环境已删除后清理自己的数据,应在 `on_cleanup` 中读取 `ctx.runtime["env_action"]`,而不是再增加一套额外的“环境删除 hook”。
|
|
282
|
+
|
|
283
|
+
## 版本兼容
|
|
284
|
+
|
|
285
|
+
- Python: `>= 3.12`
|
|
286
|
+
- 遵循语义化版本(SemVer)
|
|
287
|
+
- 当前源码包版本:`0.2.0`
|