- Project/Environment model with default environments (dev/staging/prod) - Config Runtime: cascade resolution (platform→org→project→env), secrets via vault - Storage Runtime: local provider, sha256 dedup, integrity check, file list/upload/delete - Observability: execution query, per-action summary (success rate, avg, p95), health - Realtime SSE: workspace event stream endpoint (GET /api/v1/v2/realtime/events) - Phase 1 acceptance: core.health.check, core.user.create, core.user.list - Actions: core.project.*, core.config.*, core.file.*, core.user.* (62 total)
10 KiB
动态 FPS 截图推送模块
1. 目标
现有 Browser SSE 已经支持 Screenshot 推送,本次只增加动态 Screenshot 推送频率。
核心规则:
- 无操作时:每 2 秒 推送一张 Screenshot
- 用户发生浏览器操作后:进入高频模式
- 高频模式:每 500ms 推送一张 Screenshot
- 高频模式持续 5 秒
- 5 秒的计算基于最后一次浏览器操作
- 新操作发生时,只延长高频模式时间
- 操作本身不立即触发 Screenshot
- 不使用真正的视频流
- 不使用 WebRTC
- 不使用 VNC
- Screenshot 仍然是独立 JPEG 图片
- Screenshot 仍然通过现有 SSE 推送
2. 配置
默认配置:
const screenshotConfig = {
quality: 30,
normalInterval: 2000,
activeInterval: 500,
activeDuration: 5000
}
参数:
| 参数 | 默认值 | 说明 |
|---|---|---|
quality |
30 |
JPEG 质量 |
normalInterval |
2000 |
普通模式截图间隔,单位 ms |
activeInterval |
500 |
活跃模式截图间隔,单位 ms |
activeDuration |
5000 |
最后一次操作后保持活跃模式的时间,单位 ms |
所有时间参数统一使用 ms。
3. Screenshot Mode
只有两种状态:
type ScreenshotMode = 'normal' | 'active'
Normal
interval = 2000ms
即:
Screenshot
↓
等待 2 秒
↓
Screenshot
↓
等待 2 秒
↓
...
Active
interval = 500ms
即:
Screenshot
↓
等待 500ms
↓
Screenshot
↓
等待 500ms
↓
...
4. 状态
Scheduler 至少维护:
interface ScreenshotSchedulerState {
mode: 'normal' | 'active'
lastActivityAt: number
activeUntil: number
running: boolean
}
说明:
mode
当前截图模式:
normal
active
lastActivityAt
最后一次成功 Browser Action 的时间。
activeUntil
Active Mode 的结束时间。
例如:
当前时间:10:00:00
用户操作
↓
activeUntil = 10:00:05
5. 操作触发 Active Mode
任何成功的浏览器操作完成后调用:
notifyActivity()
实现:
function notifyActivity() {
const now = Date.now()
lastActivityAt = now
activeUntil = now + activeDuration
}
同时:
mode = 'active'
6. 操作不立即 Screenshot
这是必须遵守的规则。
错误:
Browser Action
↓
Screenshot
↓
SSE
正确:
Browser Action
↓
Action 执行成功
↓
notifyActivity()
↓
切换 Active Mode
↓
Scheduler 按 500ms 推送 Screenshot
也就是说:
Browser Action 只修改 Screenshot Scheduler 状态,不主动调用 Screenshot。
7. Active Mode 生命周期
例如:
00:00
用户 click
↓
Active Until = 00:05
00:00.5 Screenshot
00:01.0 Screenshot
00:01.5 Screenshot
00:02.0 Screenshot
...
00:05.0
↓
恢复 Normal
恢复后:
每 2000ms Screenshot
8. 连续操作
如果 Active Mode 中再次发生操作:
00:00 click
↓
activeUntil = 00:05
00:02 fill
↓
activeUntil = 00:07
00:04 scroll
↓
activeUntil = 00:09
因此:
Active Mode 的 5 秒永远从最后一次操作重新计算。
最终:
00:04 最后一次操作
↓
保持 Active 到 00:09
↓
恢复 Normal
9. 多次操作不会创建多个 Timer
禁止:
setInterval(...)
setInterval(...)
setInterval(...)
也禁止每次操作创建一个新的 Screenshot Loop。
整个 Browser Session 只允许存在:
1 个 Screenshot Scheduler
所有操作统一调用:
notifyActivity()
10. Scheduler
推荐采用单一循环。
示例:
class ScreenshotScheduler {
private running = false
private mode: 'normal' | 'active' = 'normal'
private activeUntil = 0
async start() {
if (this.running) {
return
}
this.running = true
while (this.running) {
const now = Date.now()
this.mode =
now < this.activeUntil
? 'active'
: 'normal'
await this.capture()
const interval =
this.mode === 'active'
? this.config.activeInterval
: this.config.normalInterval
await sleep(interval)
}
}
notifyActivity() {
const now = Date.now()
this.mode = 'active'
this.activeUntil =
now + this.config.activeDuration
}
stop() {
this.running = false
}
}
实际代码可以根据项目结构调整,但必须保持相同的行为。
11. Screenshot 执行顺序
Scheduler 每次执行:
检查当前 Mode
↓
Screenshot
↓
SSE 推送
↓
根据当前 Mode 等待
↓
进入下一轮
例如 Normal:
Screenshot
↓
wait 2000ms
↓
Screenshot
↓
wait 2000ms
Active:
Screenshot
↓
wait 500ms
↓
Screenshot
↓
wait 500ms
12. Screenshot 不允许并发
必须保证同一 Browser Session:
同时最多 1 个 Screenshot
禁止:
Screenshot A
↓
还没完成
Screenshot B
↓
还没完成
Screenshot C
正确:
Screenshot A
↓
完成
↓
等待 interval
↓
Screenshot B
原因:
- 避免 CPU 突增
- 避免内存占用增加
- 避免多个 Screenshot 同时进入 SSE
- 避免页面状态竞争
13. Browser Action 与 Scheduler 的关系
Browser Controller 在 Action 成功后:
await executeAction(action)
scheduler.notifyActivity()
不要:
await executeAction(action)
await screenshot()
也不要:
scheduler.start()
Browser Session 创建时只启动一次 Scheduler。
14. Action 失败
Action 失败:
Browser Action
↓
失败
↓
不调用 notifyActivity()
因此不会进入 Active Mode。
例如:
click element
↓
Element Not Found
↓
Action Failed
↓
保持 Normal Mode
15. Navigation Action
以下操作成功后同样进入 Active Mode:
goto
reload
back
forward
例如:
goto
↓
页面加载完成
↓
notifyActivity()
↓
Active Mode
↓
500ms Screenshot
16. Batch Action
Batch Action 只调用一次:
scheduler.notifyActivity()
例如:
Action 1
↓
Action 2
↓
Action 3
↓
全部成功
↓
notifyActivity()
不要:
Action 1 → notifyActivity()
Action 2 → notifyActivity()
Action 3 → notifyActivity()
Batch 本身只需要最终刷新一次 Active Mode 时间。
17. SSE
动态 FPS 不改变现有 SSE API。
仍然:
GET /profiles/:profileId/browser/events
Screenshot Event 保持现有格式。
Scheduler 只决定:
什么时候生成下一张 Screenshot。
SSE 只负责:
把 Screenshot 推送给前端。
两者职责保持分离。
18. SSE State
建议在 state event 中增加:
{
"mode": "active",
"normalInterval": 2000,
"activeInterval": 500,
"activeUntil": 1780000000000
}
前端可以知道当前 Screenshot Scheduler 状态。
如果现有 state 已经定义完成,也可以不修改协议,仅在服务端内部维护状态。
19. SSE Client 数量
如果当前没有 SSE Client:
SSE Client = 0
可以暂停 Screenshot Scheduler。
重新建立 SSE:
SSE Connected
↓
启动 Screenshot Scheduler
↓
发送当前 Screenshot
↓
进入 Normal Mode
如果当前项目已经有 Scheduler 生命周期管理,则只增加动态 interval,不修改现有连接管理。
20. Active Mode 不等于视频流
必须保持:
Active Mode
↓
每 500ms
↓
生成一张 JPEG
而不是:
Active Mode
↓
视频编码
↓
持续视频流
所以本功能仍然是:
Adaptive Screenshot Streaming
而不是 Live View / Video Streaming。
21. 推荐默认参数
第一版固定默认:
{
quality: 30,
normalInterval: 2000,
activeInterval: 500,
activeDuration: 5000
}
即:
无操作:
2 秒 / 张
操作后:
500ms / 张
最后一次操作后:
5 秒恢复正常
JPEG:
quality 30
后续如果需要,再将这些参数开放为系统配置。
22. 核心流程
Browser Action
│
▼
Playwright
│
操作成功
│
▼
notifyActivity()
│
▼
Active Mode
interval=500ms
│
▼
Screenshot
│
▼
SSE
│
▼
Console
│
│
500ms 后
│
▼
Screenshot
│
...
│
最后操作 + 5 秒
│
▼
Normal Mode
interval=2000ms
│
▼
Screenshot
│
2 秒后
│
▼
Screenshot
23. 验收标准
- Normal Mode 默认 2000ms
- Active Mode 默认 500ms
- Active Duration 默认 5000ms
- Browser Action 成功后进入 Active Mode
- Browser Action 不立即触发 Screenshot
- Action 失败不进入 Active Mode
- Active Mode 从最后一次操作重新计时
- 连续操作不会创建多个 Timer
- 每个 Browser Session 只有一个 Screenshot Scheduler
- Screenshot 不允许并发执行
- Active Mode 结束后自动恢复 Normal Mode
- SSE 接口保持不变
- Screenshot 仍然使用 JPEG
- Screenshot quality 默认 30
- 不引入 WebSocket
- 不引入 WebRTC
- 不引入 VNC
- 不引入 CDP
- 不实现视频流