RFC: Pulseflare — Pulse on Cloudflare Stack #5

Open
opened 2026-04-18 01:12:57 +00:00 by xiaomo · 2 comments
Owner

动机

各设备各跑独立 Pulse,互不知道对方。需要一个在线版 Pulse 感知全队共享 events/projections,跨设备调度。

OGraph 名不副实(没有图),核心价值(Reaction/Projection/Event schema)直接融入 Pulse。

核心思路

Pulse = 一套代码,两个 runtime。

本地 CF
Runtime Bun Workers
存储 bun:sqlite D1
定时 setInterval DO alarm
文件 fs R2/KV

核心代码(Rule、Effect、compose、tick 逻辑)完全复用,只有 Store 和 Runtime 适配层不同。

包结构

packages/
  pulse/           # 核心(runtime 无关)
  pulse-bun/       # Bun runtime adapter(bun:sqlite)
  pulseflare/      # CF runtime adapter(D1 + DO alarm)
  pulse-cursor/    # Cursor executor
  upulse/          # CLI

实施步骤

Phase 1: Store 抽象

  1. store.ts 提取 PulseStore 接口(appendEvent, queryByKind, queryEvents 等)
  2. 重构现有 bun:sqlite 实现为 pulse-bun/
  3. 确保本地 Pulse 不受影响(所有测试通过)

Phase 2: Pulseflare

  1. D1 Store 实现(pulseflare/src/d1-store.ts
  2. CF Worker 入口 + Durable Object alarm 驱动 tick
  3. wrangler.toml + D1 binding 配置
  4. 部署验证:tick 能跑、事件能写

Phase 3: 事件同步

  1. 本地 Pulse → Pulseflare emit 协议(HTTP POST)
  2. Pulseflare → 本地 Pulse subscribe/webhook 协议
  3. Pulseflare Executor:通过 A2A/webhook 派任务到各设备

Phase 4: OGraph 迁移

  1. OGraph 的 Projection/Reaction 逻辑迁入 Pulse Rule
  2. OGraph Dispatcher 迁入 Pulseflare
  3. 废弃 OGraph 仓库

设计约束

  • Rule 签名 (prev, curr, inner) → [effects, tickMs] 不变
  • Event/Effect 三原语不变
  • 不引入新概念,只是同一个 Pulse 跑在不同 runtime
  • 向后兼容:本地 Pulse 行为不变

关联

  • OGraph 仓库: xiaoju/ograph(将被替代)
  • 命名灵感: Gitflare → Pulseflare
## 动机 各设备各跑独立 Pulse,互不知道对方。需要一个在线版 Pulse 感知全队共享 events/projections,跨设备调度。 OGraph 名不副实(没有图),核心价值(Reaction/Projection/Event schema)直接融入 Pulse。 ## 核心思路 **Pulse = 一套代码,两个 runtime。** | | 本地 | CF | |---|---|---| | Runtime | Bun | Workers | | 存储 | bun:sqlite | D1 | | 定时 | setInterval | DO alarm | | 文件 | fs | R2/KV | 核心代码(Rule、Effect、compose、tick 逻辑)完全复用,只有 Store 和 Runtime 适配层不同。 ## 包结构 ``` packages/ pulse/ # 核心(runtime 无关) pulse-bun/ # Bun runtime adapter(bun:sqlite) pulseflare/ # CF runtime adapter(D1 + DO alarm) pulse-cursor/ # Cursor executor upulse/ # CLI ``` ## 实施步骤 ### Phase 1: Store 抽象 1. [ ] 从 `store.ts` 提取 `PulseStore` 接口(appendEvent, queryByKind, queryEvents 等) 2. [ ] 重构现有 bun:sqlite 实现为 `pulse-bun/` 包 3. [ ] 确保本地 Pulse 不受影响(所有测试通过) ### Phase 2: Pulseflare 4. [ ] D1 Store 实现(`pulseflare/src/d1-store.ts`) 5. [ ] CF Worker 入口 + Durable Object alarm 驱动 tick 6. [ ] wrangler.toml + D1 binding 配置 7. [ ] 部署验证:tick 能跑、事件能写 ### Phase 3: 事件同步 8. [ ] 本地 Pulse → Pulseflare emit 协议(HTTP POST) 9. [ ] Pulseflare → 本地 Pulse subscribe/webhook 协议 10. [ ] Pulseflare Executor:通过 A2A/webhook 派任务到各设备 ### Phase 4: OGraph 迁移 11. [ ] OGraph 的 Projection/Reaction 逻辑迁入 Pulse Rule 12. [ ] OGraph Dispatcher 迁入 Pulseflare 13. [ ] 废弃 OGraph 仓库 ## 设计约束 - Rule 签名 `(prev, curr, inner) → [effects, tickMs]` 不变 - Event/Effect 三原语不变 - 不引入新概念,只是同一个 Pulse 跑在不同 runtime - 向后兼容:本地 Pulse 行为不变 ## 关联 - OGraph 仓库: xiaoju/ograph(将被替代) - 命名灵感: Gitflare → Pulseflare
Author
Owner

Cursor 完成了系统分析,报告已提交:PULSEFLARE-ANALYSIS.md(commit 80eeac0)

核心发现:

  1. PulseStore/ScopedStore 已是纯接口,核心循环逻辑 runtime 无关
  2. ⚠️ defs.ts/projection-engine.ts 所有函数直接接受 bun:sqlite Database 类型
  3. 🔴 PulseStore 接口是同步的,CF D1 是全异步 — 最大 breaking change
  4. CAS (objects/) 基于本地 fs,CF 需用 R2 替代

建议 4 Phase 迁移路径:

  • Phase 1: PulseDatabase 抽象接口
  • Phase 2: Bun 实现拆到 pulse-bun
  • Phase 3: 接口异步化(major 版本)
  • Phase 4: 实现 pulseflare(D1 + R2 + DO)

请 review 报告,看方案是否合理。—— 小墨 🖊️

Cursor 完成了系统分析,报告已提交:`PULSEFLARE-ANALYSIS.md`(commit 80eeac0) **核心发现:** 1. ✅ PulseStore/ScopedStore 已是纯接口,核心循环逻辑 runtime 无关 2. ⚠️ defs.ts/projection-engine.ts 所有函数直接接受 bun:sqlite Database 类型 3. 🔴 PulseStore 接口是同步的,CF D1 是全异步 — 最大 breaking change 4. CAS (objects/) 基于本地 fs,CF 需用 R2 替代 **建议 4 Phase 迁移路径:** - Phase 1: PulseDatabase 抽象接口 - Phase 2: Bun 实现拆到 pulse-bun - Phase 3: 接口异步化(major 版本) - Phase 4: 实现 pulseflare(D1 + R2 + DO) 请 review 报告,看方案是否合理。—— 小墨 🖊️
Owner

Executor 架构讨论结论

和主人讨论后,Pulseflare 的 executor 方案已明确:

两条路径,不共用接口

1. Executor(限时,tick 内同步)= Sigil

// Pulseflare tick 内直接调 Sigil
const result = await sigil.invoke("llm-qwen-plus", promptString);
// → { status: 200, payload: "..." }
  • LLM 调用、数据转换、通知发送
  • string → Promise<{ status, payload }> — 跟 HTTP handler 一样
  • 不需要新建 executor 抽象,Sigil capability 就是 executor
  • 需要新能力?sigil.deploy() 动态注册,不改 Pulseflare 代码

2. Device Effect(不限时,tick 之间异步)= Event 驱动

tick → 产出 device-effect event → tick 结束 idle
设备上线 → 拉 pending events → 执行 → 写 device-result event
下次 tick → 看到 result → 继续 workflow
  • Cursor 编码、Shell、测试、文件操作
  • 设备什么时候上线不可控,所以不走 executor
  • Workflow adapter 天然支持:没 device-result 就 idle

否决的方案

  • Sandbox(E2B/Fly Machines):高不成低不就。简单任务开 sandbox 太重,复杂任务性能不如真设备
  • 统一泛型 Executor 接口:device 任务不可控时间,不该和限时 executor 共用接口

架构图

Pulseflare (DO + D1)
  ├── tick 逻辑:复用 Pulse 核心
  ├── Executor:Sigil invoke(Dynamic Worker)
  └── Device effect:event → 设备拉取 → event

Sigil(已有)
  └── capability = executor 池

Sigil 从「Uncaged 能力注册表」升级为「Pulse executor 运行时」。Uncaged + Pulse 共用同一套 capability 基础设施。

—— 小橘 🍊(NEKO Team)

## Executor 架构讨论结论 和主人讨论后,Pulseflare 的 executor 方案已明确: ### 两条路径,不共用接口 **1. Executor(限时,tick 内同步)= Sigil** ```typescript // Pulseflare tick 内直接调 Sigil const result = await sigil.invoke("llm-qwen-plus", promptString); // → { status: 200, payload: "..." } ``` - LLM 调用、数据转换、通知发送 - `string → Promise<{ status, payload }>` — 跟 HTTP handler 一样 - **不需要新建 executor 抽象,Sigil capability 就是 executor** - 需要新能力?`sigil.deploy()` 动态注册,不改 Pulseflare 代码 **2. Device Effect(不限时,tick 之间异步)= Event 驱动** ``` tick → 产出 device-effect event → tick 结束 idle 设备上线 → 拉 pending events → 执行 → 写 device-result event 下次 tick → 看到 result → 继续 workflow ``` - Cursor 编码、Shell、测试、文件操作 - 设备什么时候上线不可控,所以不走 executor - Workflow adapter 天然支持:没 device-result 就 idle ### ❌ 否决的方案 - **Sandbox(E2B/Fly Machines)**:高不成低不就。简单任务开 sandbox 太重,复杂任务性能不如真设备 - **统一泛型 Executor 接口**:device 任务不可控时间,不该和限时 executor 共用接口 ### 架构图 ``` Pulseflare (DO + D1) ├── tick 逻辑:复用 Pulse 核心 ├── Executor:Sigil invoke(Dynamic Worker) └── Device effect:event → 设备拉取 → event Sigil(已有) └── capability = executor 池 ``` Sigil 从「Uncaged 能力注册表」升级为「Pulse executor 运行时」。Uncaged + Pulse 共用同一套 capability 基础设施。 —— 小橘 🍊(NEKO Team)
This repo is archived. You cannot comment on issues.
No Label
2 Participants
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: uncaged/pulse#5