diff --git a/public/css/styles.css b/public/css/styles.css index 3813402..e17ff79 100644 --- a/public/css/styles.css +++ b/public/css/styles.css @@ -3437,50 +3437,36 @@ tbody tr:hover td { white-space: nowrap; } -.history-card-status { - display: flex; - align-items: center; - gap: 10px; - flex-shrink: 0; -} - -.history-card-date { - font-size: 12px; - color: var(--text-muted); - white-space: nowrap; -} - -.history-card-meta { - display: flex; - align-items: center; - justify-content: space-between; - gap: 12px; -} - .history-card-task { font-size: 13px; color: var(--text-secondary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; - flex: 1; - min-width: 0; } +.history-card-info { + display: flex; + align-items: center; + gap: 16px; + font-size: 12px; + color: var(--text-muted); +} + +.history-card-date, .history-card-duration { display: flex; align-items: center; - gap: 4px; - font-size: 12px; - color: var(--text-muted); - flex-shrink: 0; + gap: 5px; white-space: nowrap; } +.history-card-date i, .history-card-duration i { - width: 12px; - height: 12px; + width: 13px; + height: 13px; flex-shrink: 0; + opacity: 0.6; } .history-card-actions { @@ -3986,12 +3972,6 @@ tbody tr:hover td { height: 12px; } -.history-card-duration-group { - display: flex; - align-items: center; - gap: 12px; -} - .pipeline-step-meta-group { display: flex; align-items: center; diff --git a/public/index.html b/public/index.html index a623da6..962d721 100644 --- a/public/index.html +++ b/public/index.html @@ -1118,6 +1118,7 @@ + diff --git a/public/js/app.js b/public/js/app.js index bd54933..0c0a6db 100644 --- a/public/js/app.js +++ b/public/js/app.js @@ -253,7 +253,7 @@ const App = {
Aprovação necessária - Passo ${stepIndex + 1} (${agentName || 'agente'}) aguardando autorização + Passo ${stepIndex + 1} (${Utils.escapeHtml(agentName) || 'agente'}) aguardando autorização
@@ -661,8 +661,8 @@ const App = { hidden.value = JSON.stringify(tags); chips.innerHTML = tags.map((t) => ` - ${t} - + ${Utils.escapeHtml(t)} + `).join(''); }; diff --git a/public/js/components/agents.js b/public/js/components/agents.js index 8f3580c..319f49e 100644 --- a/public/js/components/agents.js +++ b/public/js/components/agents.js @@ -76,7 +76,7 @@ const AgentsUI = { const model = (agent.config && agent.config.model) || agent.model || 'claude-sonnet-4-6'; const updatedAt = AgentsUI.formatDate(agent.updated_at || agent.updatedAt || agent.created_at || agent.createdAt); const tags = Array.isArray(agent.tags) && agent.tags.length > 0 - ? `
${agent.tags.map((t) => `${t}`).join('')}
` + ? `
${agent.tags.map((t) => `${Utils.escapeHtml(t)}`).join('')}
` : ''; return ` @@ -87,12 +87,12 @@ const AgentsUI = { ${initials}
-

${name}

+

${Utils.escapeHtml(name)}

${statusLabel}
- ${agent.description ? `

${agent.description}

` : ''} + ${agent.description ? `

${Utils.escapeHtml(agent.description)}

` : ''} ${tags}
@@ -279,7 +279,7 @@ const AgentsUI = { selectEl.innerHTML = '' + allAgents .filter((a) => a.status === 'active') - .map((a) => ``) + .map((a) => ``) .join(''); selectEl.value = agentId; @@ -311,7 +311,7 @@ const AgentsUI = { savedTaskSelect.innerHTML = '' + tasks.map((t) => { const label = t.category ? `[${t.category.toUpperCase()}] ${t.name}` : t.name; - return ``; + return ``; }).join(''); AgentsUI._savedTasksCache = tasks; } catch { diff --git a/public/js/components/dashboard.js b/public/js/components/dashboard.js index 2260f10..572afab 100644 --- a/public/js/components/dashboard.js +++ b/public/js/components/dashboard.js @@ -78,8 +78,8 @@ const DashboardUI = { list.innerHTML = executions.map((exec) => { const statusClass = DashboardUI._statusBadgeClass(exec.status); const statusLabel = DashboardUI._statusLabel(exec.status); - const name = exec.agentName || exec.pipelineName || exec.agentId || 'Execução'; - const taskText = exec.task || exec.input || ''; + const name = Utils.escapeHtml(exec.agentName || exec.pipelineName || exec.agentId || 'Execução'); + const taskText = Utils.escapeHtml(exec.task || exec.input || ''); const typeBadge = exec.type === 'pipeline' ? 'Pipeline ' : ''; diff --git a/public/js/components/history.js b/public/js/components/history.js index 56391bd..feb8799 100644 --- a/public/js/components/history.js +++ b/public/js/components/history.js @@ -70,22 +70,21 @@ const HistoryUI = {
${typeBadge} - ${HistoryUI._escapeHtml(name)} -
-
+ ${Utils.escapeHtml(name)} ${statusBadge} - ${date}
-
- ${HistoryUI._escapeHtml(task)} - - - - ${duration} - - ${costHtml} +
${Utils.escapeHtml(task)}
+
+ + + ${date} + + + ${duration} + + ${costHtml}
` : ''} ${step.result ? `
Resultado -
${HistoryUI._escapeHtml(step.result)}
+
${Utils.escapeHtml(step.result)}
` : ''} ${step.status === 'error' ? `
Passo falhou.
` : ''} @@ -316,7 +315,7 @@ const HistoryUI = {
Pipeline - ${HistoryUI._escapeHtml(exec.pipelineName || exec.pipelineId || '—')} + ${Utils.escapeHtml(exec.pipelineName || exec.pipelineId || '—')}
Status @@ -343,7 +342,7 @@ const HistoryUI = { ${exec.input ? `

Input Inicial

-

${HistoryUI._escapeHtml(exec.input)}

+

${Utils.escapeHtml(exec.input)}

` : ''} ${steps.length > 0 ? `
@@ -355,7 +354,7 @@ const HistoryUI = { ${exec.error ? `

Erro

-
${HistoryUI._escapeHtml(exec.error)}
+
${Utils.escapeHtml(exec.error)}
` : ''} `; }, @@ -435,15 +434,6 @@ const HistoryUI = { }); }, - _escapeHtml(str) { - if (!str) return ''; - return String(str) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"') - .replace(/'/g, '''); - }, }; window.HistoryUI = HistoryUI; diff --git a/public/js/components/pipelines.js b/public/js/components/pipelines.js index 91ab139..a1b6674 100644 --- a/public/js/components/pipelines.js +++ b/public/js/components/pipelines.js @@ -83,7 +83,7 @@ const PipelinesUI = { const stepCount = steps.length; const flowHtml = steps.map((step, index) => { - const agentName = step.agentName || step.agentId || 'Agente'; + const agentName = Utils.escapeHtml(step.agentName || step.agentId || 'Agente'); const isLast = index === steps.length - 1; const approvalIcon = step.requiresApproval && index > 0 ? ' ' @@ -102,12 +102,12 @@ const PipelinesUI = {
-

${pipeline.name || 'Sem nome'}

+

${Utils.escapeHtml(pipeline.name || 'Sem nome')}

${stepCount} ${stepCount === 1 ? 'passo' : 'passos'}
- ${pipeline.description ? `

${pipeline.description}

` : ''} + ${pipeline.description ? `

${Utils.escapeHtml(pipeline.description)}

` : ''}
${flowHtml || 'Nenhum passo configurado'} @@ -192,7 +192,7 @@ const PipelinesUI = { } const agentOptions = PipelinesUI.agents - .map((a) => ``) + .map((a) => ``) .join(''); container.innerHTML = PipelinesUI._steps.map((step, index) => { @@ -225,7 +225,7 @@ const PipelinesUI = { placeholder="{{input}} será substituído pelo output anterior" data-step-field="inputTemplate" data-step-index="${index}" - >${step.inputTemplate || ''} + >${Utils.escapeHtml(step.inputTemplate || '')} ${approvalHtml}
diff --git a/public/js/components/schedules.js b/public/js/components/schedules.js index 7c8a835..a0d6d4b 100644 --- a/public/js/components/schedules.js +++ b/public/js/components/schedules.js @@ -44,8 +44,8 @@ const SchedulesUI = { return ` - ${schedule.agentName || '—'} - ${schedule.taskDescription || '—'} + ${Utils.escapeHtml(schedule.agentName || '—')} + ${Utils.escapeHtml(schedule.taskDescription || '—')} ${cronExpr} @@ -106,7 +106,7 @@ const SchedulesUI = { select.innerHTML = '' + agents .filter((a) => a.status === 'active') - .map((a) => ``) + .map((a) => ``) .join(''); } @@ -233,12 +233,12 @@ const SchedulesUI = { const duration = SchedulesUI._formatDuration(exec.startedAt, exec.endedAt); const cost = exec.costUsd || exec.totalCostUsd || 0; const costStr = cost > 0 ? `$${cost.toFixed(4)}` : '—'; - const taskStr = SchedulesUI._escapeHtml(SchedulesUI._truncate(exec.task || '', 60)); + const taskStr = Utils.escapeHtml(Utils.truncate(exec.task || '', 60)); return ` - ${SchedulesUI._escapeHtml(exec.agentName || '—')} - ${taskStr} + ${Utils.escapeHtml(exec.agentName || '—')} + ${taskStr} ${status} ${date} ${duration} @@ -283,16 +283,6 @@ const SchedulesUI = { return `${minutes}m ${seconds}s`; }, - _escapeHtml(str) { - if (!str) return ''; - return String(str).replace(/&/g, '&').replace(//g, '>').replace(/"/g, '"'); - }, - - _truncate(str, max) { - if (!str || str.length <= max) return str; - return str.slice(0, max) + '…'; - }, - cronToHuman(expression) { if (!expression) return '—'; diff --git a/public/js/components/tasks.js b/public/js/components/tasks.js index 175e0b7..b0a2753 100644 --- a/public/js/components/tasks.js +++ b/public/js/components/tasks.js @@ -65,10 +65,10 @@ const TasksUI = { return `
-

${task.name}

- ${categoryLabel} +

${Utils.escapeHtml(task.name)}

+ ${Utils.escapeHtml(categoryLabel)}
- ${task.description ? `

${task.description}

` : ''} + ${task.description ? `

${Utils.escapeHtml(task.description)}

` : ''}