sse.ts 1.3 KB

123456789101112131415161718192021222324252627282930313233343536373839
  1. // SSE:补发 seq > lastId 的事件,然后实时推送(docs/design/04-api.md §5)
  2. import type { FastifyReply, FastifyRequest } from 'fastify'
  3. import type { AgentEvent } from '@agent-server/shared'
  4. import type { App } from '../app'
  5. const PING_MS = 15_000
  6. export function streamSessionEvents(app: App, req: FastifyRequest, reply: FastifyReply, sessionId: string, lastId: number) {
  7. reply.hijack()
  8. const res = reply.raw
  9. res.writeHead(200, {
  10. 'Content-Type': 'text/event-stream; charset=utf-8',
  11. 'Cache-Control': 'no-cache, no-transform',
  12. Connection: 'keep-alive',
  13. 'X-Accel-Buffering': 'no',
  14. })
  15. res.write('retry: 2000\n\n')
  16. let sent = lastId
  17. const write = (e: AgentEvent) => {
  18. if (e.seq <= sent) return
  19. sent = e.seq
  20. res.write(`id: ${e.seq}\nevent: ${e.type}\ndata: ${JSON.stringify(e)}\n\n`)
  21. }
  22. // 先订阅再补发,补发与实时之间的事件靠 seq 去重
  23. const pending: AgentEvent[] = []
  24. let replaying = true
  25. const unsubscribe = app.events.subscribe(sessionId, e => (replaying ? pending.push(e) : write(e)))
  26. for (const e of app.events.list(sessionId, lastId)) write(e)
  27. replaying = false
  28. for (const e of pending) write(e)
  29. const ping = setInterval(() => res.write(': ping\n\n'), PING_MS)
  30. req.raw.on('close', () => {
  31. clearInterval(ping)
  32. unsubscribe()
  33. })
  34. }