Corrigir E2BIG em pipelines, adicionar diretório de projeto e retomada
- Instalar Claude CLI no container Docker (npm install -g) - Pipar prompt via stdin ao invés de argumento -p (resolve E2BIG) - Adicionar campo workingDirectory na criação/edição de pipeline - Pre-preencher com /home/projetos/ como base path - Auto-criar diretório se não existir ao executar agente - Salvar failedAtStep e lastStepInput quando pipeline falha - Implementar retomada de pipeline a partir do passo que falhou - Adicionar botão Retomar no histórico para pipelines com erro - Configurar trust proxy para Express atrás de reverse proxy
This commit is contained in:
@@ -3489,6 +3489,25 @@ tbody tr:hover td {
|
||||
}
|
||||
}
|
||||
|
||||
.pipeline-workdir-badge {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 3px 10px;
|
||||
background-color: var(--bg-tertiary);
|
||||
border: 1px solid var(--border-primary);
|
||||
border-radius: 6px;
|
||||
font-size: 11px;
|
||||
color: var(--text-muted);
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.pipeline-workdir-badge code {
|
||||
font-family: 'JetBrains Mono', monospace;
|
||||
font-size: 11px;
|
||||
color: var(--text-secondary);
|
||||
}
|
||||
|
||||
.pipeline-flow {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -1138,6 +1138,14 @@
|
||||
<label class="form-label" for="pipeline-description">Descrição</label>
|
||||
<textarea class="textarea" id="pipeline-description" rows="2" placeholder="Descreva o objetivo do pipeline..."></textarea>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label" for="pipeline-workdir">
|
||||
<i data-lucide="folder" style="width:14px;height:14px"></i>
|
||||
Diretório do Projeto
|
||||
</label>
|
||||
<input class="input" type="text" id="pipeline-workdir" autocomplete="off">
|
||||
<p class="form-hint">Caminho onde o projeto será criado/trabalhado. Todos os passos usarão este diretório.</p>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="form-label">
|
||||
Passos do Pipeline
|
||||
@@ -1194,7 +1202,7 @@
|
||||
type="text"
|
||||
class="input"
|
||||
id="pipeline-execute-workdir"
|
||||
placeholder="/home/fred/projetos/meu-projeto"
|
||||
placeholder="/home/projetos/meu-projeto"
|
||||
autocomplete="off"
|
||||
/>
|
||||
<p class="form-hint">Se vazio, cada agente usa seu próprio diretório configurado.</p>
|
||||
|
||||
@@ -91,6 +91,7 @@ const API = {
|
||||
cancel(id) { return API.request('POST', `/pipelines/${id}/cancel`); },
|
||||
approve(id) { return API.request('POST', `/pipelines/${id}/approve`); },
|
||||
reject(id) { return API.request('POST', `/pipelines/${id}/reject`); },
|
||||
resume(executionId) { return API.request('POST', `/pipelines/resume/${executionId}`); },
|
||||
},
|
||||
|
||||
webhooks: {
|
||||
|
||||
@@ -276,6 +276,7 @@ const App = {
|
||||
|
||||
case 'pipeline_step_start':
|
||||
Terminal.stopProcessing();
|
||||
if (data.resumed) Terminal.addLine('(retomando execução anterior)', 'system');
|
||||
Terminal.addLine(`Pipeline passo ${data.stepIndex + 1}/${data.totalSteps}: Executando agente "${data.agentName}"...`, 'system');
|
||||
Terminal.startProcessing(data.agentName);
|
||||
break;
|
||||
@@ -732,6 +733,7 @@ const App = {
|
||||
case 'view-execution': HistoryUI.viewDetail(id); break;
|
||||
case 'delete-execution': HistoryUI.deleteExecution(id); break;
|
||||
case 'retry': HistoryUI.retryExecution(id); break;
|
||||
case 'resume-pipeline': HistoryUI.resumePipeline(id); break;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -102,6 +102,11 @@ const HistoryUI = {
|
||||
<i data-lucide="eye"></i>
|
||||
Ver detalhes
|
||||
</button>
|
||||
${(exec.status === 'error' && exec.type === 'pipeline' && exec.failedAtStep !== undefined) ? `
|
||||
<button class="btn btn-ghost btn-sm" data-action="resume-pipeline" data-id="${exec.id}" type="button" title="Retomar do passo ${(exec.failedAtStep || 0) + 1}">
|
||||
<i data-lucide="play"></i>
|
||||
Retomar
|
||||
</button>` : ''}
|
||||
${(exec.status === 'error' || exec.status === 'canceled') ? `
|
||||
<button class="btn btn-ghost btn-sm" data-action="retry" data-id="${exec.id}" type="button" title="Reexecutar">
|
||||
<i data-lucide="refresh-cw"></i>
|
||||
@@ -421,6 +426,16 @@ const HistoryUI = {
|
||||
Toast.success('Download iniciado');
|
||||
},
|
||||
|
||||
async resumePipeline(executionId) {
|
||||
try {
|
||||
await API.pipelines.resume(executionId);
|
||||
Toast.info('Pipeline retomado');
|
||||
App.navigateTo('terminal');
|
||||
} catch (err) {
|
||||
Toast.error(`Erro ao retomar pipeline: ${err.message}`);
|
||||
}
|
||||
},
|
||||
|
||||
async retryExecution(id) {
|
||||
try {
|
||||
await API.executions.retry(id);
|
||||
|
||||
@@ -107,6 +107,8 @@ const PipelinesUI = {
|
||||
|
||||
${pipeline.description ? `<p class="agent-description">${Utils.escapeHtml(pipeline.description)}</p>` : ''}
|
||||
|
||||
${pipeline.workingDirectory ? `<div class="pipeline-workdir-badge"><i data-lucide="folder" style="width:12px;height:12px"></i> <code>${Utils.escapeHtml(pipeline.workingDirectory)}</code></div>` : ''}
|
||||
|
||||
<div class="pipeline-flow">
|
||||
${flowHtml || '<span class="agent-description">Nenhum passo configurado</span>'}
|
||||
</div>
|
||||
@@ -133,6 +135,8 @@ const PipelinesUI = {
|
||||
`;
|
||||
},
|
||||
|
||||
_basePath: '/home/projetos/',
|
||||
|
||||
openCreateModal() {
|
||||
PipelinesUI._editingId = null;
|
||||
PipelinesUI._steps = [
|
||||
@@ -152,6 +156,9 @@ const PipelinesUI = {
|
||||
const descEl = document.getElementById('pipeline-description');
|
||||
if (descEl) descEl.value = '';
|
||||
|
||||
const workdirEl = document.getElementById('pipeline-workdir');
|
||||
if (workdirEl) workdirEl.value = PipelinesUI._basePath;
|
||||
|
||||
PipelinesUI.renderSteps();
|
||||
Modal.open('pipeline-modal-overlay');
|
||||
},
|
||||
@@ -183,6 +190,9 @@ const PipelinesUI = {
|
||||
const descEl = document.getElementById('pipeline-description');
|
||||
if (descEl) descEl.value = pipeline.description || '';
|
||||
|
||||
const workdirEl = document.getElementById('pipeline-workdir');
|
||||
if (workdirEl) workdirEl.value = pipeline.workingDirectory || PipelinesUI._basePath;
|
||||
|
||||
PipelinesUI.renderSteps();
|
||||
Modal.open('pipeline-modal-overlay');
|
||||
} catch (err) {
|
||||
@@ -391,9 +401,16 @@ const PipelinesUI = {
|
||||
return;
|
||||
}
|
||||
|
||||
const workingDirectory = document.getElementById('pipeline-workdir')?.value.trim() || '';
|
||||
if (workingDirectory && !workingDirectory.startsWith('/')) {
|
||||
Toast.warning('O diretório do projeto deve ser um caminho absoluto (começar com /)');
|
||||
return;
|
||||
}
|
||||
|
||||
const data = {
|
||||
name,
|
||||
description: document.getElementById('pipeline-description')?.value.trim() || '',
|
||||
workingDirectory,
|
||||
steps: PipelinesUI._steps.map((s, index) => {
|
||||
const isSimple = s.promptMode !== 'advanced';
|
||||
const inputTemplate = isSimple
|
||||
@@ -455,7 +472,7 @@ const PipelinesUI = {
|
||||
if (inputEl) inputEl.value = '';
|
||||
|
||||
const workdirEl = document.getElementById('pipeline-execute-workdir');
|
||||
if (workdirEl) workdirEl.value = '';
|
||||
if (workdirEl) workdirEl.value = (pipeline && pipeline.workingDirectory) || PipelinesUI._basePath;
|
||||
|
||||
if (App._pipelineDropzone) App._pipelineDropzone.reset();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user