Context
Pinchy is building Scheduled Briefings on top of OpenClaw Cron (heypinchy/pinchy#138). Milestone 1 (PR heypinchy/pinchy#151) ships thin Pinchy-side wrappers around getOpenClawClient().request("cron.*", ...). M3 ("Runtime Capture") needs a persistent sessions.subscribe() socket consumer for cron and session.tool events.
Both shapes currently require the consumer to use client.request() raw, with hand-rolled types and no subscription helper. That works but bleeds protocol shape across the call sites and blocks M3 from landing as a clean change.
Ask
Add typed helpers to openclaw-node:
Cron helpers
client.cron.list(opts?: { namePrefix?: string }): Promise<CronJob[]>
client.cron.add(input: AddCronJobInput): Promise<{ id: string }>
client.cron.update(input: UpdateCronJobInput): Promise<void>
client.cron.remove(opts: { id: string }): Promise<void>
client.cron.run(opts: { id: string; mode: "force" | "normal" }): Promise<{ runId: string }>
client.cron.runs(opts: ListCronRunsOpts): Promise<CronRunEntry[]>
client.cron.status(): Promise<CronStatus>
The Pinchy-side wrappers in packages/web/src/server/openclaw-cron.ts already pin down the input/output shapes Pinchy needs (CronJob, CronRunEntry with full usage, CronStatus). Those types should move upstream into this package; Pinchy should then consume them and shed the local copies.
Sessions subscription helper
```ts
client.sessions.subscribe(handlers: {
onCron?: (e: CronEvent) => void
onSessionTool?: (e: SessionToolEvent) => void
onReconnect?: () => void
}): Subscription
```
Auto-reconnect with exponential backoff, heartbeat/ping to detect dead sockets, and idempotent delivery (replays via `cron.runs({fromMs})` are Pinchy's job, not the helper's). One process holds one socket — multiplexing happens on the consumer side.
Why now
M3 is blocked on this. Without it Pinchy either ships a hand-rolled WS subscriber (duplication that other openclaw-node consumers will redo) or merges raw `client.request("sessions.subscribe", ...)` plus a custom event loop into Pinchy's server bundle. Neither is the right place for it.
Acceptance
Related
Context
Pinchy is building Scheduled Briefings on top of OpenClaw Cron (heypinchy/pinchy#138). Milestone 1 (PR heypinchy/pinchy#151) ships thin Pinchy-side wrappers around
getOpenClawClient().request("cron.*", ...). M3 ("Runtime Capture") needs a persistentsessions.subscribe()socket consumer forcronandsession.toolevents.Both shapes currently require the consumer to use
client.request()raw, with hand-rolled types and no subscription helper. That works but bleeds protocol shape across the call sites and blocks M3 from landing as a clean change.Ask
Add typed helpers to
openclaw-node:Cron helpers
The Pinchy-side wrappers in
packages/web/src/server/openclaw-cron.tsalready pin down the input/output shapes Pinchy needs (CronJob,CronRunEntrywith fullusage,CronStatus). Those types should move upstream into this package; Pinchy should then consume them and shed the local copies.Sessions subscription helper
```ts
client.sessions.subscribe(handlers: {
onCron?: (e: CronEvent) => void
onSessionTool?: (e: SessionToolEvent) => void
onReconnect?: () => void
}): Subscription
```
Auto-reconnect with exponential backoff, heartbeat/ping to detect dead sockets, and idempotent delivery (replays via `cron.runs({fromMs})` are Pinchy's job, not the helper's). One process holds one socket — multiplexing happens on the consumer side.
Why now
M3 is blocked on this. Without it Pinchy either ships a hand-rolled WS subscriber (duplication that other openclaw-node consumers will redo) or merges raw `client.request("sessions.subscribe", ...)` plus a custom event loop into Pinchy's server bundle. Neither is the right place for it.
Acceptance
Related