Menu
popagent
publicLatest change 7f0ff66d6d9fb6468416c58bee46bd3d08169501 - Checkpoint browser channels and memory work by AkurAI Build
import type { ChannelEvent } from "../api-types";
import { apiFetch } from "./api";
export function parseChannelEventBlock(block: string): ChannelEvent | undefined {
const data = block.split("\n")
.filter((line) => line.startsWith("data:"))
.map((line) => line.slice(5).trimStart())
.join("\n");
if (!data) return undefined;
try {
const event = JSON.parse(data) as ChannelEvent;
return event?.type && event.channelId ? event : undefined;
} catch {
return undefined;
}
}
export async function consumeChannelEvents(
channelId: string,
signal: AbortSignal,
onEvent: (event: ChannelEvent) => void,
onOpen?: () => void,
): Promise<void> {
const response = await apiFetch(`/api/channels/${encodeURIComponent(channelId)}/events`, { signal });
if (!response.ok || !response.body) throw new Error(`Channel stream failed: HTTP ${response.status}`);
onOpen?.();
const reader = response.body.pipeThrough(new TextDecoderStream()).getReader();
let buffer = "";
while (!signal.aborted) {
const { done, value } = await reader.read();
if (done) return;
buffer += value.replaceAll("\r\n", "\n");
let boundary = buffer.indexOf("\n\n");
while (boundary >= 0) {
const event = parseChannelEventBlock(buffer.slice(0, boundary));
if (event) onEvent(event);
buffer = buffer.slice(boundary + 2);
boundary = buffer.indexOf("\n\n");
}
}
}