feat: make Kafka projector the Workbench realtime authority
This commit is contained in:
@@ -4,7 +4,7 @@
|
||||
export { fetchJson, fetchText, type ActivityRef, type ActivityRefSource, type ApiRequestOptions } from "./client";
|
||||
export { authAPI, HWLAB_WEB_SESSION_COOKIE } from "./auth";
|
||||
export { workbenchAPI } from "./workbench";
|
||||
export { workbenchDebugAPI, connectWorkbenchDebugFakeSse, connectWorkbenchKafkaSseDebug, type WorkbenchDebugFakeSseQueue, type WorkbenchDebugFakeSseResponse, type WorkbenchDebugFakeSseSequence, type WorkbenchDebugFakeSseStream, type WorkbenchKafkaSseDebugEvent, type WorkbenchKafkaSseDebugFilters } from "./workbench-debug";
|
||||
export { workbenchDebugAPI, connectWorkbenchDebugFakeSse, type WorkbenchDebugFakeSseQueue, type WorkbenchDebugFakeSseResponse, type WorkbenchDebugFakeSseSequence, type WorkbenchDebugFakeSseStream } from "./workbench-debug";
|
||||
export { connectWorkbenchEvents, type WorkbenchEventStream, type WorkbenchRealtimeEvent } from "./workbench-events";
|
||||
export { agentAPI } from "./agent";
|
||||
export { hwpodAPI } from "./hwpod";
|
||||
|
||||
@@ -50,39 +50,6 @@ export interface WorkbenchDebugFakeSseStream {
|
||||
close: () => void;
|
||||
}
|
||||
|
||||
export interface WorkbenchKafkaSseDebugFilters {
|
||||
traceId?: string;
|
||||
sessionId?: string;
|
||||
runId?: string;
|
||||
commandId?: string;
|
||||
}
|
||||
|
||||
export type WorkbenchKafkaSseDebugStreamName = "stdio" | "agentrun" | "hwlab";
|
||||
|
||||
export interface WorkbenchKafkaSseDebugEvent {
|
||||
ok?: boolean;
|
||||
contractVersion?: string;
|
||||
stream?: string;
|
||||
topic?: string;
|
||||
partition?: number;
|
||||
offset?: string;
|
||||
key?: string | null;
|
||||
timestamp?: string | null;
|
||||
value?: Record<string, unknown>;
|
||||
filters?: WorkbenchKafkaSseDebugFilters;
|
||||
serverSentAt?: string;
|
||||
error?: { code?: string; message?: string; [key: string]: unknown };
|
||||
valuesPrinted?: boolean;
|
||||
}
|
||||
|
||||
export interface WorkbenchKafkaSseDebugStreamOptions extends WorkbenchKafkaSseDebugFilters {
|
||||
stream?: WorkbenchKafkaSseDebugStreamName;
|
||||
fromBeginning?: boolean;
|
||||
onEvent: (event: WorkbenchKafkaSseDebugEvent, eventName: string) => void;
|
||||
onOpen?: () => void;
|
||||
onError?: (event: Event) => void;
|
||||
}
|
||||
|
||||
const DEBUG_FAKE_SSE_EVENTS = [
|
||||
"workbench.connected",
|
||||
"workbench.trace.snapshot",
|
||||
@@ -126,39 +93,6 @@ export function connectWorkbenchDebugFakeSse(options: WorkbenchDebugFakeSseStrea
|
||||
};
|
||||
}
|
||||
|
||||
export function connectWorkbenchKafkaSseDebug(options: WorkbenchKafkaSseDebugStreamOptions): WorkbenchDebugFakeSseStream | null {
|
||||
if (typeof EventSource === "undefined") return null;
|
||||
const source = new EventSource(kafkaDebugPath("/events", {
|
||||
stream: options.stream || "hwlab",
|
||||
traceId: options.traceId,
|
||||
sessionId: options.sessionId,
|
||||
runId: options.runId,
|
||||
commandId: options.commandId,
|
||||
fromBeginning: options.fromBeginning ? "true" : undefined
|
||||
}), { withCredentials: true });
|
||||
source.onopen = () => options.onOpen?.();
|
||||
source.onerror = (event) => options.onError?.(event);
|
||||
const names = ["hwlab.kafka.connected", "hwlab.kafka.event", "hwlab.kafka.error"];
|
||||
const listeners = names.map((name) => {
|
||||
const listener = (event: MessageEvent) => {
|
||||
const payload = parseKafkaEvent(event.data);
|
||||
if (payload) options.onEvent(payload, name);
|
||||
};
|
||||
source.addEventListener(name, listener);
|
||||
return { name, listener };
|
||||
});
|
||||
source.onmessage = (event) => {
|
||||
const payload = parseKafkaEvent(event.data);
|
||||
if (payload) options.onEvent(payload, "message");
|
||||
};
|
||||
return {
|
||||
close() {
|
||||
for (const { name, listener } of listeners) source.removeEventListener(name, listener);
|
||||
source.close();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
function debugPath(suffix: string, params: Record<string, string | null | undefined> = {}): string {
|
||||
const query = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
@@ -169,16 +103,6 @@ function debugPath(suffix: string, params: Record<string, string | null | undefi
|
||||
return `/v1/workbench/debug/fake-sse${suffix}${qs ? `?${qs}` : ""}`;
|
||||
}
|
||||
|
||||
function kafkaDebugPath(suffix: string, params: Record<string, string | null | undefined> = {}): string {
|
||||
const query = new URLSearchParams();
|
||||
for (const [key, value] of Object.entries(params)) {
|
||||
const text = typeof value === "string" ? value.trim() : "";
|
||||
if (text) query.set(key, text);
|
||||
}
|
||||
const qs = query.toString();
|
||||
return `/v1/workbench/debug/kafka-sse${suffix}${qs ? `?${qs}` : ""}`;
|
||||
}
|
||||
|
||||
function parseRealtimeEvent(raw: string): WorkbenchRealtimeEvent | null {
|
||||
try {
|
||||
const value = JSON.parse(raw);
|
||||
@@ -187,12 +111,3 @@ function parseRealtimeEvent(raw: string): WorkbenchRealtimeEvent | null {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
function parseKafkaEvent(raw: string): WorkbenchKafkaSseDebugEvent | null {
|
||||
try {
|
||||
const value = JSON.parse(raw);
|
||||
return value && typeof value === "object" ? value as WorkbenchKafkaSseDebugEvent : null;
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user