feat: add runtime event handling for providers in ProvidersSection feat: update routing to include Channels and Agents pages feat: extend route types and navigation items for Channels and Agents feat: implement agents store for managing agent data and interactions fix: update chat store to utilize agents store for agent-related functionality chore: export agents store from index fix: enhance runtime types for better event handling fix: update Vite config to handle dev server URL correctly
134 lines
4.2 KiB
TypeScript
134 lines
4.2 KiB
TypeScript
import type { HostApiContext } from '../context';
|
|
import type { NormalizedHostApiRequest } from '../route-utils';
|
|
import { fail, ok, parseJsonBody } from '../route-utils';
|
|
import {
|
|
assignChannelToAgent,
|
|
clearChannelBinding,
|
|
listAgentsSnapshot,
|
|
} from '../../utils/agent-config';
|
|
import {
|
|
listSelectedChannelAccountGroups,
|
|
listSelectedChannelTargets,
|
|
} from '../../utils/channels';
|
|
|
|
function getProviderSnapshot(ctx: HostApiContext) {
|
|
const accounts = ctx.providerApiService
|
|
.getAccounts()
|
|
.filter((account) => account.enabled !== false);
|
|
const defaultAccountId = ctx.providerApiService.getDefault().accountId;
|
|
return { accounts, defaultAccountId };
|
|
}
|
|
|
|
export async function handleChannelRoutes(
|
|
request: NormalizedHostApiRequest,
|
|
ctx: HostApiContext,
|
|
) {
|
|
const { pathname, method } = request;
|
|
const { accounts, defaultAccountId } = getProviderSnapshot(ctx);
|
|
const snapshot = listAgentsSnapshot(accounts, defaultAccountId);
|
|
|
|
if (pathname === '/api/channels/accounts' && method === 'GET') {
|
|
return ok({
|
|
success: true,
|
|
channels: listSelectedChannelAccountGroups(snapshot),
|
|
});
|
|
}
|
|
|
|
if (pathname === '/api/channels/targets' && method === 'GET') {
|
|
const channelType = request.url.searchParams.get('channelType')?.trim() || '';
|
|
const accountId = request.url.searchParams.get('accountId')?.trim() || null;
|
|
const query = request.url.searchParams.get('query')?.trim() || null;
|
|
|
|
if (!channelType) {
|
|
return fail(400, 'channelType is required');
|
|
}
|
|
|
|
return ok({
|
|
success: true,
|
|
targets: listSelectedChannelTargets(channelType, accountId, query),
|
|
});
|
|
}
|
|
|
|
if (pathname === '/api/channels/binding' && method === 'PUT') {
|
|
try {
|
|
const body = parseJsonBody<{
|
|
channelType?: string;
|
|
accountId?: string | null;
|
|
agentId?: string;
|
|
}>(request.body);
|
|
const channelType = String(body?.channelType ?? '').trim();
|
|
const accountId = String(body?.accountId ?? '').trim();
|
|
const agentId = String(body?.agentId ?? '').trim();
|
|
|
|
if (!channelType) {
|
|
return fail(400, 'channelType is required');
|
|
}
|
|
if (!accountId) {
|
|
return fail(400, 'accountId is required');
|
|
}
|
|
if (!agentId) {
|
|
return fail(400, 'agentId is required');
|
|
}
|
|
if (!snapshot.agents.some((agent) => agent.id === agentId)) {
|
|
return fail(404, `Agent "${agentId}" not found`);
|
|
}
|
|
|
|
const groupedChannels = listSelectedChannelAccountGroups(snapshot);
|
|
const group = groupedChannels.find((entry) => entry.channelType === channelType);
|
|
if (!group || !group.accounts.some((entry) => entry.accountId === accountId)) {
|
|
return fail(404, `Channel account "${channelType}:${accountId}" not found`);
|
|
}
|
|
|
|
const result = assignChannelToAgent(agentId, channelType, accountId, accounts, defaultAccountId);
|
|
ctx.gatewayManager.notifyRuntimeChanged({
|
|
topics: ['agents', 'channels', 'channel-targets'],
|
|
reason: 'channels:binding-updated',
|
|
channelType,
|
|
accountId,
|
|
});
|
|
|
|
return ok({
|
|
success: true,
|
|
...result,
|
|
});
|
|
} catch (error) {
|
|
return fail(500, error instanceof Error ? error.message : String(error));
|
|
}
|
|
}
|
|
|
|
if (pathname === '/api/channels/binding' && method === 'DELETE') {
|
|
try {
|
|
const body = parseJsonBody<{
|
|
channelType?: string;
|
|
accountId?: string | null;
|
|
}>(request.body);
|
|
const channelType = String(body?.channelType ?? '').trim();
|
|
const accountId = String(body?.accountId ?? '').trim();
|
|
|
|
if (!channelType) {
|
|
return fail(400, 'channelType is required');
|
|
}
|
|
if (!accountId) {
|
|
return fail(400, 'accountId is required');
|
|
}
|
|
|
|
const result = clearChannelBinding(channelType, accountId, accounts, defaultAccountId);
|
|
ctx.gatewayManager.notifyRuntimeChanged({
|
|
topics: ['agents', 'channels', 'channel-targets'],
|
|
reason: 'channels:binding-cleared',
|
|
channelType,
|
|
accountId,
|
|
});
|
|
|
|
return ok({
|
|
success: true,
|
|
...result,
|
|
});
|
|
} catch (error) {
|
|
return fail(500, error instanceof Error ? error.message : String(error));
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|