Files
phptits 851ccf1d7f v2 platform runtime (docs 22 Phase 2/4/7 + 17): Project/Environment + Config + Storage + Observability
- 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)
2026-09-11 21:11:07 +00:00

761 lines
10 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 动态 FPS 截图推送模块
## 1. 目标
现有 Browser SSE 已经支持 Screenshot 推送,本次只增加**动态 Screenshot 推送频率**。
核心规则:
- 无操作时:每 **2 秒** 推送一张 Screenshot
- 用户发生浏览器操作后:进入高频模式
- 高频模式:每 **500ms** 推送一张 Screenshot
- 高频模式持续 **5 秒**
- 5 秒的计算基于**最后一次浏览器操作**
- 新操作发生时,只延长高频模式时间
- 操作本身**不立即触发 Screenshot**
- 不使用真正的视频流
- 不使用 WebRTC
- 不使用 VNC
- Screenshot 仍然是独立 JPEG 图片
- Screenshot 仍然通过现有 SSE 推送
---
# 2. 配置
默认配置:
```ts
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
只有两种状态:
```ts
type ScreenshotMode = 'normal' | 'active'
```
### Normal
```text
interval = 2000ms
```
即:
```text
Screenshot
↓
等待 2 秒
↓
Screenshot
↓
等待 2 秒
↓
...
```
### Active
```text
interval = 500ms
```
即:
```text
Screenshot
↓
等待 500ms
↓
Screenshot
↓
等待 500ms
↓
...
```
---
# 4. 状态
Scheduler 至少维护:
```ts
interface ScreenshotSchedulerState {
mode: 'normal' | 'active'
lastActivityAt: number
activeUntil: number
running: boolean
}
```
说明:
### `mode`
当前截图模式:
```text
normal
active
```
### `lastActivityAt`
最后一次成功 Browser Action 的时间。
### `activeUntil`
Active Mode 的结束时间。
例如:
```text
当前时间:10:00:00
用户操作
↓
activeUntil = 10:00:05
```
---
# 5. 操作触发 Active Mode
任何**成功的浏览器操作**完成后调用:
```ts
notifyActivity()
```
实现:
```ts
function notifyActivity() {
const now = Date.now()
lastActivityAt = now
activeUntil = now + activeDuration
}
```
同时:
```ts
mode = 'active'
```
---
# 6. 操作不立即 Screenshot
这是必须遵守的规则。
错误:
```text
Browser Action
↓
Screenshot
↓
SSE
```
正确:
```text
Browser Action
↓
Action 执行成功
↓
notifyActivity()
↓
切换 Active Mode
↓
Scheduler 按 500ms 推送 Screenshot
```
也就是说:
> **Browser Action 只修改 Screenshot Scheduler 状态,不主动调用 Screenshot。**
---
# 7. Active Mode 生命周期
例如:
```text
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
```
恢复后:
```text
每 2000ms Screenshot
```
---
# 8. 连续操作
如果 Active Mode 中再次发生操作:
```text
00:00 click
↓
activeUntil = 00:05
00:02 fill
↓
activeUntil = 00:07
00:04 scroll
↓
activeUntil = 00:09
```
因此:
> **Active Mode 的 5 秒永远从最后一次操作重新计算。**
最终:
```text
00:04 最后一次操作
↓
保持 Active 到 00:09
↓
恢复 Normal
```
---
# 9. 多次操作不会创建多个 Timer
禁止:
```ts
setInterval(...)
setInterval(...)
setInterval(...)
```
也禁止每次操作创建一个新的 Screenshot Loop。
整个 Browser Session 只允许存在:
```text
1 个 Screenshot Scheduler
```
所有操作统一调用:
```ts
notifyActivity()
```
---
# 10. Scheduler
推荐采用单一循环。
示例:
```ts
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 每次执行:
```text
检查当前 Mode
↓
Screenshot
↓
SSE 推送
↓
根据当前 Mode 等待
↓
进入下一轮
```
例如 Normal:
```text
Screenshot
↓
wait 2000ms
↓
Screenshot
↓
wait 2000ms
```
Active:
```text
Screenshot
↓
wait 500ms
↓
Screenshot
↓
wait 500ms
```
---
# 12. Screenshot 不允许并发
必须保证同一 Browser Session:
```text
同时最多 1 个 Screenshot
```
禁止:
```text
Screenshot A
↓
还没完成
Screenshot B
↓
还没完成
Screenshot C
```
正确:
```text
Screenshot A
↓
完成
↓
等待 interval
↓
Screenshot B
```
原因:
- 避免 CPU 突增
- 避免内存占用增加
- 避免多个 Screenshot 同时进入 SSE
- 避免页面状态竞争
---
# 13. Browser Action 与 Scheduler 的关系
Browser Controller 在 Action 成功后:
```ts
await executeAction(action)
scheduler.notifyActivity()
```
不要:
```ts
await executeAction(action)
await screenshot()
```
也不要:
```ts
scheduler.start()
```
Browser Session 创建时只启动一次 Scheduler。
---
# 14. Action 失败
Action 失败:
```text
Browser Action
↓
失败
↓
不调用 notifyActivity()
```
因此不会进入 Active Mode。
例如:
```text
click element
↓
Element Not Found
↓
Action Failed
↓
保持 Normal Mode
```
---
# 15. Navigation Action
以下操作成功后同样进入 Active Mode:
```text
goto
reload
back
forward
```
例如:
```text
goto
↓
页面加载完成
↓
notifyActivity()
↓
Active Mode
↓
500ms Screenshot
```
---
# 16. Batch Action
Batch Action 只调用一次:
```ts
scheduler.notifyActivity()
```
例如:
```text
Action 1
↓
Action 2
↓
Action 3
↓
全部成功
↓
notifyActivity()
```
不要:
```text
Action 1 → notifyActivity()
Action 2 → notifyActivity()
Action 3 → notifyActivity()
```
Batch 本身只需要最终刷新一次 Active Mode 时间。
---
# 17. SSE
动态 FPS 不改变现有 SSE API。
仍然:
```http
GET /profiles/:profileId/browser/events
```
Screenshot Event 保持现有格式。
Scheduler 只决定:
> **什么时候生成下一张 Screenshot。**
SSE 只负责:
> **把 Screenshot 推送给前端。**
两者职责保持分离。
---
# 18. SSE State
建议在 `state` event 中增加:
```json
{
"mode": "active",
"normalInterval": 2000,
"activeInterval": 500,
"activeUntil": 1780000000000
}
```
前端可以知道当前 Screenshot Scheduler 状态。
如果现有 `state` 已经定义完成,也可以不修改协议,仅在服务端内部维护状态。
---
# 19. SSE Client 数量
如果当前没有 SSE Client:
```text
SSE Client = 0
```
可以暂停 Screenshot Scheduler。
重新建立 SSE:
```text
SSE Connected
↓
启动 Screenshot Scheduler
↓
发送当前 Screenshot
↓
进入 Normal Mode
```
如果当前项目已经有 Scheduler 生命周期管理,则只增加动态 interval,不修改现有连接管理。
---
# 20. Active Mode 不等于视频流
必须保持:
```text
Active Mode
↓
每 500ms
↓
生成一张 JPEG
```
而不是:
```text
Active Mode
↓
视频编码
↓
持续视频流
```
所以本功能仍然是:
> **Adaptive Screenshot Streaming**
而不是 Live View / Video Streaming。
---
# 21. 推荐默认参数
第一版固定默认:
```ts
{
quality: 30,
normalInterval: 2000,
activeInterval: 500,
activeDuration: 5000
}
```
即:
```text
无操作:
2 秒 / 张
操作后:
500ms / 张
最后一次操作后:
5 秒恢复正常
JPEG:
quality 30
```
后续如果需要,再将这些参数开放为系统配置。
---
# 22. 核心流程
```text
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
- [ ] 不实现视频流