Files
EJClaw/src/dashboard.ts
2026-03-23 07:00:22 +09:00

99 lines
3.1 KiB
TypeScript

import { STATUS_SHOW_ROOMS } from './config.js';
import {
composeDashboardContent,
type DashboardRoomLine,
getStatusLabel,
renderCategorizedRoomSections,
} from './dashboard-render.js';
import type { GroupQueue } from './group-queue.js';
import { startUnifiedDashboard } from './unified-dashboard.js';
import type { AgentType, Channel, RegisteredGroup } from './types.js';
export interface DashboardOptions {
assistantName: string;
channels: Channel[];
getSessions: () => Record<string, string>;
queue: GroupQueue;
registeredGroups: () => Record<string, RegisteredGroup>;
statusChannelId: string;
statusUpdateInterval: number;
usageUpdateInterval: number;
serviceAgentType?: AgentType;
}
function getSessionLabel(sessionId: string | undefined): string {
if (!sessionId) return '세션 없음';
const shortId = sessionId.length > 8 ? sessionId.slice(-8) : sessionId;
return `세션 ${shortId}`;
}
export function buildStatusContent(opts: DashboardOptions): string {
if (!STATUS_SHOW_ROOMS) return '';
const sessions = opts.getSessions();
const groups = opts.registeredGroups();
const statuses = opts.queue.getStatuses(Object.keys(groups));
let totalActive = 0;
let totalWaiting = 0;
const roomLines: DashboardRoomLine[] = statuses
.map((status) => {
const group = groups[status.jid];
if (!group) return null;
if (status.status === 'processing') totalActive += 1;
if (status.status === 'waiting') totalWaiting += 1;
return {
category: '기타',
categoryPosition: 999,
position: 999,
line: ` **${group.name}** — ${getStatusLabel(status)} · ${getSessionLabel(sessions[group.folder])}`,
};
})
.filter((line): line is DashboardRoomLine => Boolean(line));
return composeDashboardContent([
`**에이전트 상태** (${opts.assistantName}) — 활성 ${totalActive} | 큐대기 ${totalWaiting} | 전체 ${roomLines.length}\n\n${renderCategorizedRoomSections(
{
lines: roomLines,
showCategoryHeaders: false,
},
)}`,
]);
}
export async function purgeDashboardChannel(
opts: Pick<DashboardOptions, 'channels' | 'statusChannelId'>,
): Promise<void> {
if (!opts.statusChannelId) return;
const statusJid = `dc:${opts.statusChannelId}`;
const channel = opts.channels.find(
(item) =>
item.name.startsWith('discord') &&
item.isConnected() &&
item.purgeChannel,
);
if (channel?.purgeChannel) {
await channel.purgeChannel(statusJid);
}
}
export async function startStatusDashboard(
opts: DashboardOptions,
): Promise<void> {
await startUnifiedDashboard({
assistantName: opts.assistantName,
serviceAgentType: opts.serviceAgentType || 'claude-code',
statusChannelId: opts.statusChannelId,
statusUpdateInterval: opts.statusUpdateInterval,
usageUpdateInterval: opts.usageUpdateInterval,
channels: opts.channels,
queue: opts.queue,
registeredGroups: opts.registeredGroups,
});
}
export async function startUsageDashboard(): Promise<void> {
// Usage dashboard is integrated into startStatusDashboard via unified dashboard.
}