Compare commits

...

2 Commits

Author SHA1 Message Date
7ec1906a20 feat(ms22-p2): add AgentTemplate admin CRUD endpoints
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
2026-03-04 20:27:32 -06:00
5bd08b0d0b fix(deps): update multer override to >=2.1.1 (#681)
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>
2026-03-05 02:27:08 +00:00
8 changed files with 187 additions and 9 deletions

View File

@@ -0,0 +1,47 @@
import {
Controller,
Get,
Post,
Patch,
Delete,
Body,
Param,
UseGuards,
ParseUUIDPipe,
} from "@nestjs/common";
import { AgentTemplateService } from "./agent-template.service";
import { CreateAgentTemplateDto } from "./dto/create-agent-template.dto";
import { UpdateAgentTemplateDto } from "./dto/update-agent-template.dto";
import { AuthGuard } from "../auth/guards/auth.guard";
import { AdminGuard } from "../auth/guards/admin.guard";
@Controller("admin/agent-templates")
@UseGuards(AuthGuard, AdminGuard)
export class AgentTemplateController {
constructor(private readonly agentTemplateService: AgentTemplateService) {}
@Get()
findAll() {
return this.agentTemplateService.findAll();
}
@Get(":id")
findOne(@Param("id", ParseUUIDPipe) id: string) {
return this.agentTemplateService.findOne(id);
}
@Post()
create(@Body() dto: CreateAgentTemplateDto) {
return this.agentTemplateService.create(dto);
}
@Patch(":id")
update(@Param("id", ParseUUIDPipe) id: string, @Body() dto: UpdateAgentTemplateDto) {
return this.agentTemplateService.update(id, dto);
}
@Delete(":id")
remove(@Param("id", ParseUUIDPipe) id: string) {
return this.agentTemplateService.remove(id);
}
}

View File

@@ -0,0 +1,12 @@
import { Module } from "@nestjs/common";
import { AgentTemplateService } from "./agent-template.service";
import { AgentTemplateController } from "./agent-template.controller";
import { PrismaModule } from "../prisma/prisma.module";
@Module({
imports: [PrismaModule],
controllers: [AgentTemplateController],
providers: [AgentTemplateService],
exports: [AgentTemplateService],
})
export class AgentTemplateModule {}

View File

@@ -0,0 +1,57 @@
import { Injectable, NotFoundException, ConflictException } from "@nestjs/common";
import { PrismaService } from "../prisma/prisma.service";
import { CreateAgentTemplateDto } from "./dto/create-agent-template.dto";
import { UpdateAgentTemplateDto } from "./dto/update-agent-template.dto";
@Injectable()
export class AgentTemplateService {
constructor(private readonly prisma: PrismaService) {}
async findAll() {
return this.prisma.agentTemplate.findMany({
orderBy: { createdAt: "asc" },
});
}
async findOne(id: string) {
const template = await this.prisma.agentTemplate.findUnique({ where: { id } });
if (!template) throw new NotFoundException(`AgentTemplate ${id} not found`);
return template;
}
async findByName(name: string) {
const template = await this.prisma.agentTemplate.findUnique({ where: { name } });
if (!template) throw new NotFoundException(`AgentTemplate "${name}" not found`);
return template;
}
async create(dto: CreateAgentTemplateDto) {
const existing = await this.prisma.agentTemplate.findUnique({ where: { name: dto.name } });
if (existing) throw new ConflictException(`AgentTemplate "${dto.name}" already exists`);
return this.prisma.agentTemplate.create({
data: {
name: dto.name,
displayName: dto.displayName,
role: dto.role,
personality: dto.personality,
primaryModel: dto.primaryModel,
fallbackModels: dto.fallbackModels ?? ([] as string[]),
toolPermissions: dto.toolPermissions ?? ([] as string[]),
...(dto.discordChannel !== undefined && { discordChannel: dto.discordChannel }),
isActive: dto.isActive ?? true,
isDefault: dto.isDefault ?? false,
},
});
}
async update(id: string, dto: UpdateAgentTemplateDto) {
await this.findOne(id);
return this.prisma.agentTemplate.update({ where: { id }, data: dto });
}
async remove(id: string) {
await this.findOne(id);
return this.prisma.agentTemplate.delete({ where: { id } });
}
}

View File

@@ -0,0 +1,43 @@
import { IsString, IsBoolean, IsOptional, IsArray, MinLength } from "class-validator";
export class CreateAgentTemplateDto {
@IsString()
@MinLength(1)
name!: string;
@IsString()
@MinLength(1)
displayName!: string;
@IsString()
@MinLength(1)
role!: string;
@IsString()
@MinLength(1)
personality!: string;
@IsString()
@MinLength(1)
primaryModel!: string;
@IsArray()
@IsOptional()
fallbackModels?: string[];
@IsArray()
@IsOptional()
toolPermissions?: string[];
@IsString()
@IsOptional()
discordChannel?: string;
@IsBoolean()
@IsOptional()
isActive?: boolean;
@IsBoolean()
@IsOptional()
isDefault?: boolean;
}

View File

@@ -0,0 +1,4 @@
import { PartialType } from "@nestjs/mapped-types";
import { CreateAgentTemplateDto } from "./create-agent-template.dto";
export class UpdateAgentTemplateDto extends PartialType(CreateAgentTemplateDto) {}

View File

@@ -48,6 +48,7 @@ import { TerminalModule } from "./terminal/terminal.module";
import { PersonalitiesModule } from "./personalities/personalities.module";
import { WorkspacesModule } from "./workspaces/workspaces.module";
import { AdminModule } from "./admin/admin.module";
import { AgentTemplateModule } from "./agent-template/agent-template.module";
import { TeamsModule } from "./teams/teams.module";
import { ImportModule } from "./import/import.module";
import { ConversationArchiveModule } from "./conversation-archive/conversation-archive.module";
@@ -129,6 +130,7 @@ import { OrchestratorModule } from "./orchestrator/orchestrator.module";
PersonalitiesModule,
WorkspacesModule,
AdminModule,
AgentTemplateModule,
TeamsModule,
ImportModule,
ConversationArchiveModule,

View File

@@ -76,7 +76,7 @@
"undici": ">=6.23.0",
"rollup": ">=4.59.0",
"serialize-javascript": ">=7.0.3",
"multer": ">=2.1.0"
"multer": ">=2.1.1"
}
}
}

29
pnpm-lock.yaml generated
View File

@@ -17,7 +17,7 @@ overrides:
undici: '>=6.23.0'
rollup: '>=4.59.0'
serialize-javascript: '>=7.0.3'
multer: '>=2.1.0'
multer: '>=2.1.1'
importers:
@@ -1616,6 +1616,7 @@ packages:
'@mosaicstack/telemetry-client@0.1.1':
resolution: {integrity: sha512-1udg6p4cs8rhQgQ2pKCfi7EpRlJieRRhA5CIqthRQ6HQZLgQ0wH+632jEulov3rlHSM1iplIQ+AAe5DWrvSkEA==, tarball: https://git.mosaicstack.dev/api/packages/mosaic/npm/%40mosaicstack%2Ftelemetry-client/-/0.1.1/telemetry-client-0.1.1.tgz}
engines: {node: '>=18'}
'@mrleebo/prisma-ast@0.13.1':
resolution: {integrity: sha512-XyroGQXcHrZdvmrGJvsA9KNeOOgGMg1Vg9OlheUsBOSKznLMDl+YChxbkboRHvtFYJEMRYmlV3uoo/njCw05iw==}
@@ -5863,8 +5864,8 @@ packages:
msgpackr@1.11.5:
resolution: {integrity: sha512-UjkUHN0yqp9RWKy0Lplhh+wlpdt9oQBYgULZOiFhV3VclSF1JnSQWZ5r9gORQlNYaUKQoR8itv7g7z1xDDuACA==}
multer@2.1.0:
resolution: {integrity: sha512-TBm6j41rxNohqawsxlsWsNNh/VdV4QFXcBvRcPhXaA05EZ79z0qJ2bQFpync6JBoHTeNY5Q1JpG7AlTjdlfAEA==}
multer@2.1.1:
resolution: {integrity: sha512-mo+QTzKlx8R7E5ylSXxWzGoXoZbOsRMpyitcht8By2KHvMbf3tjwosZ/Mu/XYU6UuJ3VZnODIrak5ZrPiPyB6A==}
engines: {node: '>= 10.16.0'}
mute-stream@2.0.0:
@@ -8004,7 +8005,7 @@ snapshots:
chalk: 5.6.2
commander: 12.1.0
dotenv: 17.2.4
drizzle-orm: 0.41.0(@opentelemetry/api@1.9.0)(@prisma/client@6.19.2(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.10)(pg@8.17.2)(postgres@3.4.8)(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))
drizzle-orm: 0.41.0(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.10)(pg@8.17.2)(postgres@3.4.8)(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))
open: 10.2.0
pg: 8.17.2
prettier: 3.8.1
@@ -8868,7 +8869,7 @@ snapshots:
'@nestjs/core': 11.1.12(@nestjs/common@11.1.12(class-transformer@0.5.1)(class-validator@0.14.3)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/platform-express@11.1.12)(@nestjs/websockets@11.1.12)(reflect-metadata@0.2.2)(rxjs@7.8.2)
cors: 2.8.5
express: 5.2.1
multer: 2.1.0
multer: 2.1.1
path-to-regexp: 8.3.0
tslib: 2.8.1
transitivePeerDependencies:
@@ -11344,7 +11345,7 @@ snapshots:
optionalDependencies:
'@prisma/client': 5.22.0(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))
better-sqlite3: 12.6.2
drizzle-orm: 0.41.0(@opentelemetry/api@1.9.0)(@prisma/client@6.19.2(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.10)(pg@8.17.2)(postgres@3.4.8)(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))
drizzle-orm: 0.41.0(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.10)(pg@8.17.2)(postgres@3.4.8)(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))
next: 16.1.6(@babel/core@7.28.6)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
pg: 8.17.2
prisma: 6.19.2(magicast@0.3.5)(typescript@5.9.3)
@@ -11369,7 +11370,7 @@ snapshots:
optionalDependencies:
'@prisma/client': 6.19.2(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))(typescript@5.9.3)
better-sqlite3: 12.6.2
drizzle-orm: 0.41.0(@opentelemetry/api@1.9.0)(@prisma/client@6.19.2(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.10)(pg@8.17.2)(postgres@3.4.8)(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))
drizzle-orm: 0.41.0(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.10)(pg@8.17.2)(postgres@3.4.8)(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))
next: 16.1.6(@babel/core@7.28.6)(@opentelemetry/api@1.9.0)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)
pg: 8.17.2
prisma: 6.19.2(magicast@0.3.5)(typescript@5.9.3)
@@ -12193,6 +12194,17 @@ snapshots:
dotenv@17.2.4: {}
drizzle-orm@0.41.0(@opentelemetry/api@1.9.0)(@prisma/client@5.22.0(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3)))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.10)(pg@8.17.2)(postgres@3.4.8)(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3)):
optionalDependencies:
'@opentelemetry/api': 1.9.0
'@prisma/client': 5.22.0(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))
'@types/pg': 8.16.0
better-sqlite3: 12.6.2
kysely: 0.28.10
pg: 8.17.2
postgres: 3.4.8
prisma: 6.19.2(magicast@0.3.5)(typescript@5.9.3)
drizzle-orm@0.41.0(@opentelemetry/api@1.9.0)(@prisma/client@6.19.2(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3))(typescript@5.9.3))(@types/pg@8.16.0)(better-sqlite3@12.6.2)(kysely@0.28.10)(pg@8.17.2)(postgres@3.4.8)(prisma@6.19.2(magicast@0.3.5)(typescript@5.9.3)):
optionalDependencies:
'@opentelemetry/api': 1.9.0
@@ -12203,6 +12215,7 @@ snapshots:
pg: 8.17.2
postgres: 3.4.8
prisma: 6.19.2(magicast@0.3.5)(typescript@5.9.3)
optional: true
dunder-proto@1.0.1:
dependencies:
@@ -13473,7 +13486,7 @@ snapshots:
optionalDependencies:
msgpackr-extract: 3.0.3
multer@2.1.0:
multer@2.1.1:
dependencies:
append-field: 1.0.0
busboy: 1.6.0