Implementação completa de funcionalidades pendentes
- Settings persistentes (modelo padrão, workdir, max concurrent) - Import/export de agentes via JSON - Agendamentos persistentes com restore no startup - Edição de agendamentos e tarefas existentes - Filtros e busca em todas as seções - Isolamento de WebSocket por clientId - Autenticação via AUTH_TOKEN e CORS configurável - Graceful shutdown com cancelamento de execuções - Correção: --max-tokens removido (flag inválida do CLI) - Correção: pipeline agora verifica exit code e propaga erros - Correção: streaming de output em pipelines via WebSocket - Permission mode bypassPermissions como padrão - Página de configurações do sistema - Contagem diária de execuções no dashboard - Histórico de execuções recentes
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
import { agentsStore } from '../store/db.js';
|
||||
import { agentsStore, schedulesStore } from '../store/db.js';
|
||||
import * as executor from './executor.js';
|
||||
import * as scheduler from './scheduler.js';
|
||||
|
||||
@@ -7,10 +7,32 @@ const DEFAULT_CONFIG = {
|
||||
model: 'claude-sonnet-4-6',
|
||||
systemPrompt: '',
|
||||
workingDirectory: '',
|
||||
maxTokens: 16000,
|
||||
temperature: 1,
|
||||
maxTurns: 0,
|
||||
permissionMode: 'bypassPermissions',
|
||||
allowedTools: '',
|
||||
};
|
||||
|
||||
let dailyExecutionCount = 0;
|
||||
let dailyCountDate = new Date().toDateString();
|
||||
|
||||
function incrementDailyCount() {
|
||||
const today = new Date().toDateString();
|
||||
if (today !== dailyCountDate) {
|
||||
dailyExecutionCount = 0;
|
||||
dailyCountDate = today;
|
||||
}
|
||||
dailyExecutionCount++;
|
||||
}
|
||||
|
||||
export function getDailyExecutionCount() {
|
||||
const today = new Date().toDateString();
|
||||
if (today !== dailyCountDate) {
|
||||
dailyExecutionCount = 0;
|
||||
dailyCountDate = today;
|
||||
}
|
||||
return dailyExecutionCount;
|
||||
}
|
||||
|
||||
function validateAgent(data) {
|
||||
const errors = [];
|
||||
if (!data.agent_name || typeof data.agent_name !== 'string') {
|
||||
@@ -22,6 +44,13 @@ function validateAgent(data) {
|
||||
return errors;
|
||||
}
|
||||
|
||||
function sanitizeTags(tags) {
|
||||
if (!Array.isArray(tags)) return [];
|
||||
return tags
|
||||
.filter((t) => typeof t === 'string' && t.length > 0 && t.length <= 50)
|
||||
.slice(0, 20);
|
||||
}
|
||||
|
||||
export function getAllAgents() {
|
||||
return agentsStore.getAll();
|
||||
}
|
||||
@@ -39,6 +68,7 @@ export function createAgent(data) {
|
||||
const agentData = {
|
||||
agent_name: data.agent_name,
|
||||
description: data.description || '',
|
||||
tags: sanitizeTags(data.tags),
|
||||
tasks: data.tasks || [],
|
||||
config: { ...DEFAULT_CONFIG, ...(data.config || {}) },
|
||||
status: data.status || 'active',
|
||||
@@ -56,6 +86,7 @@ export function updateAgent(id, data) {
|
||||
const updateData = {};
|
||||
if (data.agent_name !== undefined) updateData.agent_name = data.agent_name;
|
||||
if (data.description !== undefined) updateData.description = data.description;
|
||||
if (data.tags !== undefined) updateData.tags = sanitizeTags(data.tags);
|
||||
if (data.tasks !== undefined) updateData.tasks = data.tasks;
|
||||
if (data.status !== undefined) updateData.status = data.status;
|
||||
if (data.assigned_host !== undefined) updateData.assigned_host = data.assigned_host;
|
||||
@@ -78,6 +109,7 @@ export function executeTask(agentId, task, instructions, wsCallback) {
|
||||
const executionRecord = {
|
||||
executionId: null,
|
||||
agentId,
|
||||
agentName: agent.agent_name,
|
||||
task: typeof task === 'string' ? task : task.description,
|
||||
startedAt: new Date().toISOString(),
|
||||
status: 'running',
|
||||
@@ -122,7 +154,12 @@ export function executeTask(agentId, task, instructions, wsCallback) {
|
||||
}
|
||||
);
|
||||
|
||||
if (!executionId) {
|
||||
throw new Error('Limite de execuções simultâneas atingido');
|
||||
}
|
||||
|
||||
executionRecord.executionId = executionId;
|
||||
incrementDailyCount();
|
||||
|
||||
const updatedAgent = agentsStore.getById(agentId);
|
||||
const executions = [...(updatedAgent.executions || []), executionRecord];
|
||||
@@ -151,11 +188,49 @@ export function scheduleTask(agentId, taskDescription, cronExpression, wsCallbac
|
||||
|
||||
const scheduleId = uuidv4();
|
||||
|
||||
const items = schedulesStore.getAll();
|
||||
items.push({
|
||||
id: scheduleId,
|
||||
agentId,
|
||||
agentName: agent.agent_name,
|
||||
taskDescription,
|
||||
cronExpression,
|
||||
active: true,
|
||||
created_at: new Date().toISOString(),
|
||||
updated_at: new Date().toISOString(),
|
||||
});
|
||||
schedulesStore.save(items);
|
||||
|
||||
scheduler.schedule(scheduleId, cronExpression, () => {
|
||||
executeTask(agentId, taskDescription, null, wsCallback);
|
||||
}, false);
|
||||
|
||||
return { scheduleId, agentId, agentName: agent.agent_name, taskDescription, cronExpression };
|
||||
}
|
||||
|
||||
export function updateScheduleTask(scheduleId, data, wsCallback) {
|
||||
const stored = schedulesStore.getById(scheduleId);
|
||||
if (!stored) return null;
|
||||
|
||||
const agentId = data.agentId || stored.agentId;
|
||||
const agent = agentsStore.getById(agentId);
|
||||
if (!agent) throw new Error(`Agente ${agentId} não encontrado`);
|
||||
|
||||
const taskDescription = data.taskDescription || stored.taskDescription;
|
||||
const cronExpression = data.cronExpression || stored.cronExpression;
|
||||
|
||||
scheduler.updateSchedule(scheduleId, cronExpression, () => {
|
||||
executeTask(agentId, taskDescription, null, wsCallback);
|
||||
});
|
||||
|
||||
return { scheduleId, agentId, taskDescription, cronExpression };
|
||||
schedulesStore.update(scheduleId, {
|
||||
agentId,
|
||||
agentName: agent.agent_name,
|
||||
taskDescription,
|
||||
cronExpression,
|
||||
});
|
||||
|
||||
return schedulesStore.getById(scheduleId);
|
||||
}
|
||||
|
||||
export function cancelExecution(executionId) {
|
||||
@@ -166,20 +241,59 @@ export function getActiveExecutions() {
|
||||
return executor.getActiveExecutions();
|
||||
}
|
||||
|
||||
export function getRecentExecutions(limit = 20) {
|
||||
const agents = agentsStore.getAll();
|
||||
const all = agents.flatMap((a) =>
|
||||
(a.executions || []).map((e) => ({
|
||||
...e,
|
||||
agentName: a.agent_name,
|
||||
agentId: a.id,
|
||||
}))
|
||||
);
|
||||
all.sort((a, b) => new Date(b.startedAt) - new Date(a.startedAt));
|
||||
return all.slice(0, limit);
|
||||
}
|
||||
|
||||
export function exportAgent(agentId) {
|
||||
const agent = agentsStore.getById(agentId);
|
||||
if (!agent) return null;
|
||||
|
||||
return {
|
||||
id: agent.id,
|
||||
agent_name: agent.agent_name,
|
||||
description: agent.description,
|
||||
tags: agent.tags || [],
|
||||
tasks: agent.tasks,
|
||||
config: agent.config,
|
||||
status: agent.status,
|
||||
assigned_host: agent.assigned_host,
|
||||
created_at: agent.created_at,
|
||||
updated_at: agent.updated_at,
|
||||
executions: agent.executions || [],
|
||||
};
|
||||
}
|
||||
|
||||
export function importAgent(data) {
|
||||
if (!data.agent_name) {
|
||||
throw new Error('agent_name é obrigatório para importação');
|
||||
}
|
||||
|
||||
const agentData = {
|
||||
agent_name: data.agent_name,
|
||||
description: data.description || '',
|
||||
tags: sanitizeTags(data.tags),
|
||||
tasks: data.tasks || [],
|
||||
config: { ...DEFAULT_CONFIG, ...(data.config || {}) },
|
||||
status: data.status || 'active',
|
||||
assigned_host: data.assigned_host || 'localhost',
|
||||
executions: [],
|
||||
};
|
||||
|
||||
return agentsStore.create(agentData);
|
||||
}
|
||||
|
||||
export function restoreSchedules(wsCallback) {
|
||||
scheduler.restoreSchedules((agentId, taskDescription) => {
|
||||
try {
|
||||
executeTask(agentId, taskDescription, null, wsCallback);
|
||||
} catch (err) {
|
||||
console.log(`[manager] Erro ao executar tarefa agendada: ${err.message}`);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user