71 lines
1.8 KiB
TypeScript
71 lines
1.8 KiB
TypeScript
import { Injectable, Logger, OnModuleInit } from "@nestjs/common";
|
|
import { randomUUID } from "crypto";
|
|
import { ValkeyService } from "../../valkey/valkey.service";
|
|
import type { EventHandler, OrchestratorEvent } from "../../valkey/types";
|
|
|
|
type UnsubscribeFn = () => void;
|
|
|
|
@Injectable()
|
|
export class AgentEventsService implements OnModuleInit {
|
|
private readonly logger = new Logger(AgentEventsService.name);
|
|
private readonly subscribers = new Map<string, EventHandler>();
|
|
private connected = false;
|
|
|
|
constructor(private readonly valkeyService: ValkeyService) {}
|
|
|
|
async onModuleInit(): Promise<void> {
|
|
if (this.connected) return;
|
|
|
|
await this.valkeyService.subscribeToEvents(
|
|
(event) => {
|
|
this.subscribers.forEach((handler) => {
|
|
void handler(event);
|
|
});
|
|
},
|
|
(error, _raw, channel) => {
|
|
this.logger.warn(`Event stream parse/validation warning on ${channel}: ${error.message}`);
|
|
}
|
|
);
|
|
|
|
this.connected = true;
|
|
this.logger.log("Agent event stream subscription active");
|
|
}
|
|
|
|
subscribe(handler: EventHandler): UnsubscribeFn {
|
|
const id = randomUUID();
|
|
this.subscribers.set(id, handler);
|
|
return () => {
|
|
this.subscribers.delete(id);
|
|
};
|
|
}
|
|
|
|
async getInitialSnapshot(): Promise<{
|
|
type: "stream.snapshot";
|
|
timestamp: string;
|
|
agents: number;
|
|
tasks: number;
|
|
}> {
|
|
const [agents, tasks] = await Promise.all([
|
|
this.valkeyService.listAgents(),
|
|
this.valkeyService.listTasks(),
|
|
]);
|
|
|
|
return {
|
|
type: "stream.snapshot",
|
|
timestamp: new Date().toISOString(),
|
|
agents: agents.length,
|
|
tasks: tasks.length,
|
|
};
|
|
}
|
|
|
|
createHeartbeat(): OrchestratorEvent {
|
|
return {
|
|
type: "task.processing",
|
|
timestamp: new Date().toISOString(),
|
|
data: {
|
|
heartbeat: true,
|
|
},
|
|
};
|
|
}
|
|
}
|