feat(activity): SRCP v1 step gates, pause/resume/stop control (v1.2.0)
All checks were successful
Publish Python Package to Gitea / publish (push) Successful in 20s
All checks were successful
Publish Python Package to Gitea / publish (push) Successful in 20s
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
94
README.md
94
README.md
@@ -87,7 +87,10 @@ send_prompt_via_composer(page, "第一行\n第二行")
|
||||
| `emit(text, ...)` | 写 Run Journal(`activity` / `progress` / `warn` / `debug`) |
|
||||
| `step(text)` | `emit(type='activity')` 别名 |
|
||||
| `finish(status=..., **fields)` | 写终态事件 + **唯一**向 stdout 输出单行 result JSON |
|
||||
| `rpa_step(name)` | 装饰器:进入/成功/失败自动 emit |
|
||||
| `rpa_step(name)` | 装饰器:闸门 + 进入/成功/失败自动 emit |
|
||||
| `checkpoint()` | consult control(不写步骤事件) |
|
||||
| `interruptible_sleep(sec)` | 可暂停/停止的 async sleep |
|
||||
| `JobStopped` | 宿主 stop 时抛出 |
|
||||
| `job_context(...)` | 可选包裹 main;异常时自动 `finish(failed)` |
|
||||
|
||||
### Run Journal 路径(宿主 tail 此文件)
|
||||
@@ -133,6 +136,95 @@ emit("手动步骤说明", skill="publish-toutiao", stage="rpa")
|
||||
|
||||
`RpaVideoSession.add_step()` 在录屏字幕之外默认同步 `emit(..., stage="rpa.video")`;可通过 `emit_activity=False` 关闭。
|
||||
|
||||
## Skill Runtime Control Protocol (SRCP) v1 — 1.2.0+
|
||||
|
||||
宿主通过 **control 文件**向技能进程下发暂停 / 继续 / 停止;platform-kit 在**步骤闸门**自动读取,技能作者无需手写控制逻辑。
|
||||
|
||||
### control.json(宿主写、kit 读)
|
||||
|
||||
```
|
||||
{JIANGCHANG_DATA_ROOT}/.jiangchang/runs/{job_id}.control.json
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"schema_version": 1,
|
||||
"command": "none",
|
||||
"reason": "user",
|
||||
"ts": 1730000000000,
|
||||
"nonce": 1
|
||||
}
|
||||
```
|
||||
|
||||
| command | 行为 |
|
||||
|---------|------|
|
||||
| `none` | 正常运行 |
|
||||
| `pause` | 下一步闸门进入暂停(阻塞直到 resume 或 stop) |
|
||||
| `resume` | 从暂停恢复 |
|
||||
| `stop` | 协作式停止,抛出 `JobStopped` |
|
||||
|
||||
宿主更新命令时应递增 `nonce`(kit 每次闸门重新读文件,不依赖缓存)。
|
||||
|
||||
### 步骤闸门(自动挂载)
|
||||
|
||||
| 调用点 | 闸门行为 |
|
||||
|--------|----------|
|
||||
| `emit()` / `step()` | 进入步骤前 gate + 写 Journal |
|
||||
| `@rpa_step` | 进入步骤前 gate,再写 ▶ / ✓ |
|
||||
| `RpaVideoSession.add_step()` | 经 `emit()` 自动 gate |
|
||||
| `interruptible_sleep()` | 每 200ms 切片 `checkpoint()`(可暂停/停止) |
|
||||
|
||||
暂停在**当前步骤边界**生效(与影刀「当前指令执行完后暂停」一致),不在 DOM 原子操作中途冻结。
|
||||
|
||||
### lifecycle Journal 事件
|
||||
|
||||
闸门写入 `type: lifecycle` 事件,供宿主任务中心显示运行 / 暂停 / 继续状态:
|
||||
|
||||
```json
|
||||
{"type":"lifecycle","status":"paused","text":"已暂停,等待继续","step_index":3,"step_name":"打开页面",...}
|
||||
```
|
||||
|
||||
### API
|
||||
|
||||
| API | 作用 |
|
||||
|-----|------|
|
||||
| `checkpoint()` | 仅 consult control(长等待循环内使用) |
|
||||
| `interruptible_sleep(sec)` | async 可中断 sleep |
|
||||
| `interruptible_sleep_sync(sec)` | sync 可中断 sleep |
|
||||
| `JobStopped` | 宿主 stop 时抛出;`job_context` 自动 `finish(..., error_code="JOB_STOPPED")` |
|
||||
| `get_control_path()` | 当前 job 的 control 文件路径(诊断) |
|
||||
|
||||
环境变量:
|
||||
|
||||
- `JIANGCHANG_CONTROL_POLL_SEC` — 暂停轮询间隔(默认 `0.2`)
|
||||
- `JIANGCHANG_CONTROL_DISABLED=1` — 禁用闸门(仅测试)
|
||||
|
||||
### RPA 示例(拆步 + 可中断等待)
|
||||
|
||||
```python
|
||||
from jiangchang_skill_core.activity import (
|
||||
emit,
|
||||
finish,
|
||||
job_context,
|
||||
interruptible_sleep,
|
||||
rpa_step,
|
||||
)
|
||||
|
||||
def cmd_run(...):
|
||||
with job_context(skill="demo-skill"):
|
||||
emit("开始任务", skill="demo-skill", stage="run")
|
||||
|
||||
@rpa_step("打开后台")
|
||||
async def open_admin(page):
|
||||
await page.goto("https://example.com")
|
||||
await interruptible_sleep(1.5)
|
||||
|
||||
await open_admin(page)
|
||||
finish(status="success", message="完成", skill="demo-skill")
|
||||
```
|
||||
|
||||
技能作者规范以 **skill-template** 为准;本 README 为实现细节参考。
|
||||
|
||||
### 兄弟技能 CLI 桥
|
||||
|
||||
```python
|
||||
|
||||
Reference in New Issue
Block a user