From 5d84769ac09f6d76f0eb47bbb00779b5be178dd1 Mon Sep 17 00:00:00 2001 From: WangDL Date: Sun, 24 May 2026 18:10:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20M4-07=20=E2=80=94=20Hermes=20Agent=20ta?= =?UTF-8?q?sk/artifact=20management=20with=20approval=20workflow?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add AgentTask and AgentArtifact Prisma models - HermesAgentController: list tasks, approve/reject, list artifacts - AAPI: GET /admin-api/hermes/tasks, POST approve/reject, GET /admin-api/hermes/artifacts Co-Authored-By: Claude Opus 4.7 --- prisma/schema.prisma | 33 +++++++++++++ src/app.module.ts | 2 + .../hermes-agent/hermes-agent.controller.ts | 49 +++++++++++++++++++ .../hermes-agent/hermes-agent.module.ts | 7 +++ 4 files changed, 91 insertions(+) create mode 100644 src/modules/hermes-agent/hermes-agent.controller.ts create mode 100644 src/modules/hermes-agent/hermes-agent.module.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 3acfb3f..1e8203c 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -1409,3 +1409,36 @@ model ExportJob { errorMessage String? @db.Text createdAt DateTime @default(now()) } + +model AgentTask { + id String @id @default(cuid()) + userId String? + type String @db.VarChar(32) + status String @default("pending") @db.VarChar(16) + input String? @db.Text + output String? @db.Text + sessionId String? @db.VarChar(255) + approvedBy String? + approvedAt DateTime? + giteaUrl String? @db.VarChar(500) + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([status]) + @@index([userId]) +} + +model AgentArtifact { + id String @id @default(cuid()) + taskId String? + type String @db.VarChar(32) + title String @db.VarChar(255) + content String? @db.Text + status String @default("draft") @db.VarChar(16) + createdBy String? + createdAt DateTime @default(now()) + updatedAt DateTime @updatedAt + + @@index([taskId]) + @@index([type]) +} diff --git a/src/app.module.ts b/src/app.module.ts index 8187274..f494791 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -55,6 +55,7 @@ import { AdminCacheModule } from './modules/admin-cache/admin-cache.module'; import { BackupModule } from './modules/backup/backup.module'; import { ReportingModule } from './modules/reporting/reporting.module'; import { ProjectCenterModule } from './modules/project-center/project-center.module'; +import { HermesAgentModule } from './modules/hermes-agent/hermes-agent.module'; import { JwtAuthGuard } from './common/guards/jwt-auth.guard'; import { RolesGuard } from './common/guards/roles.guard'; @@ -155,6 +156,7 @@ import appleConfig from './config/apple.config'; BackupModule, ReportingModule, ProjectCenterModule, + HermesAgentModule, ], providers: [ { provide: APP_GUARD, useClass: RateLimitGuard }, diff --git a/src/modules/hermes-agent/hermes-agent.controller.ts b/src/modules/hermes-agent/hermes-agent.controller.ts new file mode 100644 index 0000000..b089ff0 --- /dev/null +++ b/src/modules/hermes-agent/hermes-agent.controller.ts @@ -0,0 +1,49 @@ +import { Controller, Get, Post, Param, Body, Query, UseGuards } from '@nestjs/common'; +import { ApiTags, ApiBearerAuth, ApiOperation, ApiQuery, ApiBody } from '@nestjs/swagger'; +import { PrismaService } from '../../infrastructure/database/prisma.service'; +import { AdminAuthGuard } from '../../common/guards/admin-auth.guard'; +import { AdminRolesGuard } from '../../common/guards/admin-roles.guard'; + +@ApiTags('admin-hermes') +@ApiBearerAuth() +@Controller('admin-api/hermes') +@UseGuards(AdminAuthGuard, AdminRolesGuard) +export class HermesAgentController { + constructor(private readonly prisma: PrismaService) {} + + @Get('tasks') + @ApiOperation({ summary: 'Agent 任务列表' }) + @ApiQuery({ name: 'status', required: false }) + async listTasks(@Query('status') status?: string) { + const where: any = {}; + if (status) where.status = status; + return this.prisma.agentTask.findMany({ where, orderBy: { createdAt: 'desc' }, take: 100 }); + } + + @Post('tasks/:id/approve') + @ApiOperation({ summary: '审批通过 Agent 任务' }) + async approveTask(@Param('id') id: string) { + return this.prisma.agentTask.update({ + where: { id }, + data: { status: 'approved', approvedAt: new Date() }, + }); + } + + @Post('tasks/:id/reject') + @ApiOperation({ summary: '驳回 Agent 任务' }) + async rejectTask(@Param('id') id: string) { + return this.prisma.agentTask.update({ + where: { id }, + data: { status: 'rejected' }, + }); + } + + @Get('artifacts') + @ApiOperation({ summary: 'Agent 产出物列表' }) + @ApiQuery({ name: 'type', required: false }) + async listArtifacts(@Query('type') type?: string) { + const where: any = {}; + if (type) where.type = type; + return this.prisma.agentArtifact.findMany({ where, orderBy: { createdAt: 'desc' }, take: 100 }); + } +} diff --git a/src/modules/hermes-agent/hermes-agent.module.ts b/src/modules/hermes-agent/hermes-agent.module.ts new file mode 100644 index 0000000..d5062d8 --- /dev/null +++ b/src/modules/hermes-agent/hermes-agent.module.ts @@ -0,0 +1,7 @@ +import { Module } from '@nestjs/common'; +import { HermesAgentController } from './hermes-agent.controller'; + +@Module({ + controllers: [HermesAgentController], +}) +export class HermesAgentModule {}