Integrar repositórios Git na execução de agentes e pipelines
- Módulo git-integration: clone/pull, commit/push automático, listagem de repos - Seletor de repositório nos modais de execução (agente e pipeline) - Seletor de branch carregado dinamicamente ao escolher repo - Campo de diretório escondido quando repositório selecionado - Auto-commit e push ao final da execução com mensagem descritiva - Instrução injetada para agentes não fazerem operações git - Rotas API: GET /repos, GET /repos/:name/branches - Pipeline: commit automático ao final de todos os steps
This commit is contained in:
@@ -38,9 +38,10 @@ const API = {
|
||||
create(data) { return API.request('POST', '/agents', data); },
|
||||
update(id, data) { return API.request('PUT', `/agents/${id}`, data); },
|
||||
delete(id) { return API.request('DELETE', `/agents/${id}`); },
|
||||
execute(id, task, instructions, contextFiles, workingDirectory) {
|
||||
execute(id, task, instructions, contextFiles, workingDirectory, repoName, repoBranch) {
|
||||
const body = { task, instructions };
|
||||
if (workingDirectory) body.workingDirectory = workingDirectory;
|
||||
if (repoName) { body.repoName = repoName; if (repoBranch) body.repoBranch = repoBranch; }
|
||||
else if (workingDirectory) body.workingDirectory = workingDirectory;
|
||||
if (contextFiles && contextFiles.length > 0) body.contextFiles = contextFiles;
|
||||
return API.request('POST', `/agents/${id}/execute`, body);
|
||||
},
|
||||
@@ -83,9 +84,10 @@ const API = {
|
||||
create(data) { return API.request('POST', '/pipelines', data); },
|
||||
update(id, data) { return API.request('PUT', `/pipelines/${id}`, data); },
|
||||
delete(id) { return API.request('DELETE', `/pipelines/${id}`); },
|
||||
execute(id, input, workingDirectory, contextFiles) {
|
||||
execute(id, input, workingDirectory, contextFiles, repoName, repoBranch) {
|
||||
const body = { input };
|
||||
if (workingDirectory) body.workingDirectory = workingDirectory;
|
||||
if (repoName) { body.repoName = repoName; if (repoBranch) body.repoBranch = repoBranch; }
|
||||
else if (workingDirectory) body.workingDirectory = workingDirectory;
|
||||
if (contextFiles && contextFiles.length > 0) body.contextFiles = contextFiles;
|
||||
return API.request('POST', `/pipelines/${id}/execute`, body);
|
||||
},
|
||||
@@ -142,6 +144,11 @@ const API = {
|
||||
},
|
||||
},
|
||||
|
||||
repos: {
|
||||
list() { return API.request('GET', '/repos'); },
|
||||
branches(name) { return API.request('GET', `/repos/${encodeURIComponent(name)}/branches`); },
|
||||
},
|
||||
|
||||
files: {
|
||||
list(path) { return API.request('GET', `/files${path ? '?path=' + encodeURIComponent(path) : ''}`); },
|
||||
delete(path) { return API.request('DELETE', `/files?path=${encodeURIComponent(path)}`); },
|
||||
|
||||
@@ -37,6 +37,7 @@ const App = {
|
||||
|
||||
App._executeDropzone = Utils.initDropzone('execute-dropzone', 'execute-files', 'execute-file-list');
|
||||
App._pipelineDropzone = Utils.initDropzone('pipeline-execute-dropzone', 'pipeline-execute-files', 'pipeline-execute-file-list');
|
||||
App._initRepoSelectors();
|
||||
|
||||
const initialSection = location.hash.replace('#', '') || 'dashboard';
|
||||
App.navigateTo(App.sections.includes(initialSection) ? initialSection : 'dashboard');
|
||||
@@ -860,6 +861,61 @@ const App = {
|
||||
});
|
||||
},
|
||||
|
||||
_reposCache: null,
|
||||
|
||||
async _loadRepos(selectId) {
|
||||
const select = document.getElementById(selectId);
|
||||
if (!select) return;
|
||||
try {
|
||||
if (!App._reposCache) App._reposCache = await API.repos.list();
|
||||
const current = select.value;
|
||||
select.innerHTML = '<option value="">Nenhum (usar diretório manual)</option>';
|
||||
App._reposCache.forEach(r => {
|
||||
select.insertAdjacentHTML('beforeend',
|
||||
`<option value="${Utils.escapeHtml(r.name)}">${Utils.escapeHtml(r.name)}${r.description ? ' — ' + Utils.escapeHtml(r.description.slice(0, 40)) : ''}</option>`
|
||||
);
|
||||
});
|
||||
if (current) select.value = current;
|
||||
} catch { }
|
||||
},
|
||||
|
||||
_initRepoSelectors() {
|
||||
const pairs = [
|
||||
['execute-repo', 'execute-repo-branch', 'execute-workdir-group'],
|
||||
['pipeline-execute-repo', 'pipeline-execute-repo-branch', 'pipeline-execute-workdir-group'],
|
||||
];
|
||||
pairs.forEach(([repoId, branchId, workdirGroupId]) => {
|
||||
const repoSelect = document.getElementById(repoId);
|
||||
const branchSelect = document.getElementById(branchId);
|
||||
const workdirGroup = document.getElementById(workdirGroupId);
|
||||
if (!repoSelect) return;
|
||||
|
||||
repoSelect.addEventListener('change', async () => {
|
||||
const repoName = repoSelect.value;
|
||||
if (repoName) {
|
||||
if (workdirGroup) workdirGroup.style.display = 'none';
|
||||
if (branchSelect) {
|
||||
branchSelect.style.display = '';
|
||||
branchSelect.innerHTML = '<option value="">Branch padrão</option>';
|
||||
try {
|
||||
const branches = await API.repos.branches(repoName);
|
||||
branches.forEach(b => {
|
||||
branchSelect.insertAdjacentHTML('beforeend', `<option value="${Utils.escapeHtml(b)}">${Utils.escapeHtml(b)}</option>`);
|
||||
});
|
||||
} catch { }
|
||||
}
|
||||
} else {
|
||||
if (workdirGroup) workdirGroup.style.display = '';
|
||||
if (branchSelect) branchSelect.style.display = 'none';
|
||||
}
|
||||
});
|
||||
|
||||
repoSelect.addEventListener('focus', () => {
|
||||
if (repoSelect.options.length <= 1) App._loadRepos(repoId);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
async _handleExecute() {
|
||||
const agentId = document.getElementById('execute-agent-select')?.value
|
||||
|| document.getElementById('execute-agent-id')?.value;
|
||||
@@ -877,6 +933,8 @@ const App = {
|
||||
|
||||
const instructions = document.getElementById('execute-instructions')?.value.trim() || '';
|
||||
const workingDirectory = document.getElementById('execute-workdir')?.value.trim() || '';
|
||||
const repoName = document.getElementById('execute-repo')?.value || '';
|
||||
const repoBranch = document.getElementById('execute-repo-branch')?.value || '';
|
||||
|
||||
try {
|
||||
const selectEl = document.getElementById('execute-agent-select');
|
||||
@@ -893,7 +951,7 @@ const App = {
|
||||
Terminal.disableChat();
|
||||
App._lastAgentName = agentName;
|
||||
|
||||
await API.agents.execute(agentId, task, instructions, contextFiles, workingDirectory);
|
||||
await API.agents.execute(agentId, task, instructions, contextFiles, workingDirectory, repoName, repoBranch);
|
||||
|
||||
if (dropzone) dropzone.reset();
|
||||
Modal.close('execute-modal-overlay');
|
||||
|
||||
@@ -374,6 +374,11 @@ const AgentsUI = {
|
||||
|
||||
AgentsUI._loadSavedTasks();
|
||||
|
||||
const repoSelect = document.getElementById('execute-repo');
|
||||
if (repoSelect) { repoSelect.value = ''; repoSelect.dispatchEvent(new Event('change')); }
|
||||
App._reposCache = null;
|
||||
App._loadRepos('execute-repo');
|
||||
|
||||
Modal.open('execute-modal-overlay');
|
||||
} catch (err) {
|
||||
Toast.error(`Erro ao abrir modal de execução: ${err.message}`);
|
||||
|
||||
@@ -476,6 +476,11 @@ const PipelinesUI = {
|
||||
|
||||
if (App._pipelineDropzone) App._pipelineDropzone.reset();
|
||||
|
||||
const repoSelect = document.getElementById('pipeline-execute-repo');
|
||||
if (repoSelect) { repoSelect.value = ''; repoSelect.dispatchEvent(new Event('change')); }
|
||||
App._reposCache = null;
|
||||
App._loadRepos('pipeline-execute-repo');
|
||||
|
||||
Modal.open('pipeline-execute-modal-overlay');
|
||||
},
|
||||
|
||||
@@ -503,7 +508,9 @@ const PipelinesUI = {
|
||||
contextFiles = uploadResult.files;
|
||||
}
|
||||
|
||||
await API.pipelines.execute(pipelineId, input, workingDirectory, contextFiles);
|
||||
const repoName = document.getElementById('pipeline-execute-repo')?.value || '';
|
||||
const repoBranch = document.getElementById('pipeline-execute-repo-branch')?.value || '';
|
||||
await API.pipelines.execute(pipelineId, input, workingDirectory, contextFiles, repoName, repoBranch);
|
||||
if (dropzone) dropzone.reset();
|
||||
Modal.close('pipeline-execute-modal-overlay');
|
||||
App.navigateTo('terminal');
|
||||
|
||||
Reference in New Issue
Block a user