feat(gateway): CommandRegistryService + CommandExecutorService (P8-010) (#178)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful

Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
This commit was merged in pull request #178.
This commit is contained in:
2026-03-16 02:10:31 +00:00
committed by jason.woltje
parent 7f6464bbda
commit a4bb563779
8 changed files with 460 additions and 1 deletions

View File

@@ -12,9 +12,11 @@ import {
import { Server, Socket } from 'socket.io';
import type { AgentSessionEvent } from '@mariozechner/pi-coding-agent';
import type { Auth } from '@mosaic/auth';
import type { SetThinkingPayload } from '@mosaic/types';
import type { SetThinkingPayload, SlashCommandPayload } from '@mosaic/types';
import { AgentService } from '../agent/agent.service.js';
import { AUTH } from '../auth/auth.tokens.js';
import { CommandRegistryService } from '../commands/command-registry.service.js';
import { CommandExecutorService } from '../commands/command-executor.service.js';
import { v4 as uuid } from 'uuid';
import { ChatSocketMessageDto } from './chat.dto.js';
import { validateSocketSession } from './chat.gateway-auth.js';
@@ -38,6 +40,8 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
constructor(
@Inject(AgentService) private readonly agentService: AgentService,
@Inject(AUTH) private readonly auth: Auth,
@Inject(CommandRegistryService) private readonly commandRegistry: CommandRegistryService,
@Inject(CommandExecutorService) private readonly commandExecutor: CommandExecutorService,
) {}
afterInit(): void {
@@ -55,6 +59,9 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
client.data.user = session.user;
client.data.session = session.session;
this.logger.log(`Client connected: ${client.id}`);
// Broadcast command manifest to the newly connected client
client.emit('commands:manifest', { manifest: this.commandRegistry.getManifest() });
}
handleDisconnect(client: Socket): void {
@@ -184,6 +191,16 @@ export class ChatGateway implements OnGatewayInit, OnGatewayConnection, OnGatewa
});
}
@SubscribeMessage('command:execute')
async handleCommandExecute(
@ConnectedSocket() client: Socket,
@MessageBody() payload: SlashCommandPayload,
): Promise<void> {
const userId = (client.data.user as { id: string } | undefined)?.id ?? 'unknown';
const result = await this.commandExecutor.execute(payload, userId);
client.emit('command:result', result);
}
private relayEvent(client: Socket, conversationId: string, event: AgentSessionEvent): void {
if (!client.connected) {
this.logger.warn(

View File

@@ -1,8 +1,10 @@
import { Module } from '@nestjs/common';
import { CommandsModule } from '../commands/commands.module.js';
import { ChatGateway } from './chat.gateway.js';
import { ChatController } from './chat.controller.js';
@Module({
imports: [CommandsModule],
controllers: [ChatController],
providers: [ChatGateway],
})