n8n
Version:
n8n Workflow Automation Tool
179 lines • 8.61 kB
JavaScript
;
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
var __metadata = (this && this.__metadata) || function (k, v) {
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.AgentSkillsService = void 0;
const api_types_1 = require("@n8n/api-types");
const backend_common_1 = require("@n8n/backend-common");
const di_1 = require("@n8n/di");
const n8n_workflow_1 = require("n8n-workflow");
const not_found_error_1 = require("../../errors/response-errors/not-found.error");
const agent_draft_utils_1 = require("./utils/agent-draft.utils");
const agent_repository_1 = require("./repositories/agent.repository");
const agent_resource_id_1 = require("./utils/agent-resource-id");
let AgentSkillsService = class AgentSkillsService {
constructor(logger, agentRepository) {
this.logger = logger;
this.agentRepository = agentRepository;
}
async listSkills(agentId, projectId) {
const entity = await this.agentRepository.findByIdAndProjectId(agentId, projectId);
if (!entity)
throw new not_found_error_1.NotFoundError('Agent not found');
return entity.skills ?? {};
}
async getSkill(agentId, projectId, skillId) {
const skills = await this.listSkills(agentId, projectId);
const skill = skills[skillId];
if (!skill)
throw new not_found_error_1.NotFoundError('Skill not found');
return skill;
}
async createSkill(agentId, projectId, skill) {
const [result] = await this.createSkillsBatch(agentId, projectId, [skill], false);
return result;
}
async createSkills(agentId, projectId, skills) {
return await this.createSkillsBatch(agentId, projectId, skills, false);
}
async createAndAttachSkill(agentId, projectId, skill) {
const [result] = await this.createSkillsBatch(agentId, projectId, [skill], true);
return result;
}
async createSkillsBatch(agentId, projectId, skills, attach) {
if (skills.length === 0) {
throw new n8n_workflow_1.UserError('At least one skill is required.');
}
const entity = await this.agentRepository.findByIdAndProjectId(agentId, projectId);
if (!entity)
throw new not_found_error_1.NotFoundError('Agent not found');
if (attach && !entity.schema)
throw new n8n_workflow_1.UserError('Agent has no JSON config yet.');
for (const skill of skills) {
this.validateSkill(skill);
}
this.assertBatchSkillNamesAreUnique(entity.skills ?? {}, skills);
const results = skills.map((skill) => ({ id: this.addSkill(entity, skill), skill }));
if (attach) {
for (const { id } of results)
this.attachSkillRef(entity, id);
}
(0, agent_draft_utils_1.markAgentDraftDirty)(entity);
const saved = await this.agentRepository.save(entity);
await this.clearRuntimes(agentId);
this.logger.debug(attach ? 'Created and attached agent skill' : 'Created agent skills', {
agentId,
projectId,
skillIds: results.map((r) => r.id),
});
return results.map((r) => ({ ...r, versionId: saved.versionId }));
}
async updateSkill(agentId, projectId, skillId, updates) {
const entity = await this.agentRepository.findByIdAndProjectId(agentId, projectId);
if (!entity)
throw new not_found_error_1.NotFoundError('Agent not found');
const existing = entity.skills?.[skillId];
if (!existing)
throw new not_found_error_1.NotFoundError('Skill not found');
const updated = { ...existing, ...updates };
this.validateSkill(updated);
this.assertSkillNameIsUnique(entity.skills ?? {}, updated.name, skillId);
entity.skills = {
...(entity.skills ?? {}),
[skillId]: updated,
};
(0, agent_draft_utils_1.markAgentDraftDirty)(entity);
const saved = await this.agentRepository.save(entity);
await this.clearRuntimes(agentId);
this.logger.debug('Updated agent skill', { agentId, projectId, skillId });
return { id: skillId, skill: updated, versionId: saved.versionId };
}
async deleteSkill(agentId, projectId, skillId) {
const entity = await this.agentRepository.findByIdAndProjectId(agentId, projectId);
if (!entity)
throw new not_found_error_1.NotFoundError('Agent not found');
const skills = { ...(entity.skills ?? {}) };
if (!skills[skillId])
throw new not_found_error_1.NotFoundError('Skill not found');
delete skills[skillId];
entity.skills = skills;
if (entity.schema?.skills) {
entity.schema.skills = entity.schema.skills.filter((t) => t.id !== skillId);
}
(0, agent_draft_utils_1.markAgentDraftDirty)(entity);
await this.agentRepository.save(entity);
await this.clearRuntimes(agentId);
this.logger.debug('Deleted agent skill', { agentId, projectId, skillId });
}
removeUnreferencedSkills(entity, config) {
const referencedSkillIds = new Set((config.skills ?? []).map((t) => t.id));
const orphanSkillIds = Object.keys(entity.skills ?? {}).filter((id) => !referencedSkillIds.has(id));
if (orphanSkillIds.length === 0)
return;
const skills = { ...(entity.skills ?? {}) };
for (const id of orphanSkillIds) {
delete skills[id];
}
entity.skills = skills;
}
validateSkill(skill) {
const result = api_types_1.agentSkillSchema.safeParse(skill);
if (!result.success) {
throw new n8n_workflow_1.UserError(`Invalid agent skill: ${result.error.issues[0]?.message ?? 'Invalid skill'}`);
}
}
addSkill(entity, skill) {
const skillId = (0, agent_resource_id_1.generateAgentResourceId)('skill', Object.keys(entity.skills ?? {}));
entity.skills = {
...(entity.skills ?? {}),
[skillId]: skill,
};
return skillId;
}
assertSkillNameIsUnique(existing, name, currentSkillId) {
const normalizedName = this.normalizeSkillName(name);
const duplicate = Object.entries(existing ?? {}).find(([id, skill]) => id !== currentSkillId && this.normalizeSkillName(skill.name) === normalizedName);
if (duplicate) {
throw new n8n_workflow_1.UserError(`Agent already has a skill named "${name.trim()}".`);
}
}
normalizeSkillName(name) {
return name.trim().toLowerCase();
}
assertBatchSkillNamesAreUnique(existing, skills) {
const seenNames = new Set();
for (const skill of skills) {
this.assertSkillNameIsUnique(existing, skill.name);
const normalizedName = this.normalizeSkillName(skill.name);
if (seenNames.has(normalizedName)) {
throw new n8n_workflow_1.UserError(`Duplicate skill name in batch: "${skill.name.trim()}".`);
}
seenNames.add(normalizedName);
}
}
attachSkillRef(entity, skillId) {
if (!entity.schema)
throw new n8n_workflow_1.UserError('Agent has no JSON config yet.');
entity.schema.skills = [
...(entity.schema.skills ?? []).filter((ref) => ref.id !== skillId),
{ type: 'skill', id: skillId },
];
}
async clearRuntimes(agentId) {
const { AgentRuntimeCacheService } = await import('./agent-runtime-cache.service.js');
di_1.Container.get(AgentRuntimeCacheService).clearRuntimes(agentId);
}
};
exports.AgentSkillsService = AgentSkillsService;
exports.AgentSkillsService = AgentSkillsService = __decorate([
(0, di_1.Service)(),
__metadata("design:paramtypes", [backend_common_1.Logger, agent_repository_1.AgentRepository])
], AgentSkillsService);
//# sourceMappingURL=agent-skills.service.js.map