diff --git a/templates/agent_executor.py b/templates/agent_executor.py index ad292a8..ef78115 100644 --- a/templates/agent_executor.py +++ b/templates/agent_executor.py @@ -1,107 +1,151 @@ -import os -import json -import redis -import argparse -import subprocess +import os, json, redis, subprocess, base64 +from urllib import request, parse from anthropic import Anthropic -from key_manager import KeyManager - -# Configurações de Caminho -BASE_DIR = "/home/deploy/projetos" -INSTRUCOES_DIR = "/home/deploy/instrucoes" class SwarmAgent: def __init__(self, task_data): self.data = task_data self.repo_name = task_data.get('repository') self.repo_url = f"http://10.99.0.3:3000/{self.repo_name}.git" - self.local_path = os.path.join(BASE_DIR, self.repo_name.split('/')[-1]) - self.km = KeyManager() + self.local_path = os.path.join("/home/deploy/projetos", self.repo_name.split('/')[-1]) + # Conexão Redis para pegar chaves + self.r = redis.Redis(host='10.99.0.3', port=6379, password='clube67_dragonfly_pass_9903', db=0, decode_responses=True) + + def get_api_key(self): + pool = json.loads(self.r.get('swarm:keys:pool')) + return random.choice([k['key'] for k in pool['anthropic'] if k['status'] == 'active']) + def setup_workspace(self): - """Garante que o código e as instruções estão atualizados""" - print(f" [>] Sincronizando repositório: {self.repo_name}") - os.makedirs(BASE_DIR, exist_ok=True) - + os.makedirs("/home/deploy/projetos", exist_ok=True) if not os.path.exists(self.local_path): subprocess.run(["git", "clone", self.repo_url, self.local_path]) else: subprocess.run(["git", "-C", self.local_path, "pull"]) - - # Sincroniza instruções globais - subprocess.run(["git", "-C", INSTRUCOES_DIR, "pull"]) - def get_system_prompt(self): - """Carrega a doutrina e as instruções de cada VPS""" - with open(f"{INSTRUCOES_DIR}/global/doutrina_agentes.md", "r") as f: - doutrina = f.read() - with open(f"{INSTRUCOES_DIR}/global/claude.md", "r") as f: - especifico = f.read() - - ip_vps = subprocess.getoutput("hostname -I").split()[0] - - return f""" - {doutrina} - {especifico} - - CONTEXTO ATUAL: - - Você está na VPS com IP: {ip_vps} - - Projeto: {self.repo_name} - - Tarefa (Issue): {json.dumps(self.data.get('data', {}).get('issue', {}))} - """ + def run_bash(self, command): + """Ferramenta para a IA rodar comandos na VPS 4""" + result = subprocess.run(command, shell=True, capture_output=True, text=True, cwd=self.local_path) + return f"STDOUT: {result.stdout}\nSTDERR: {result.stderr}" def post_comment(self, message): - """Posta um comentário de feedback na Issue do Gitea usando urllib (zero-dependency)""" issue_id = self.data.get('data', {}).get('issue', {}).get('number') - if not issue_id: - return - + if not issue_id: return url = f"http://10.99.0.3:3000/api/v1/repos/{self.repo_name}/issues/{issue_id}/comments" - - import base64 - from urllib import request, parse - - # Auth Basic auth_str = 'ruicesar:h$tg@g5aga$ra1E3$C-yHW$-BA@DF2@Grfa!3#' - auth_bytes = auth_str.encode('ascii') - base64_auth = base64.b64encode(auth_bytes).decode('ascii') - - data = json.dumps({"body": message}).encode('utf-8') - req = request.Request(url, data=data, method='POST') + base64_auth = base64.b64encode(auth_str.encode('ascii')).decode('ascii') + req = request.Request(url, data=json.dumps({"body": message}).encode('utf-8'), method='POST') req.add_header('Content-Type', 'application/json') req.add_header('Authorization', f'Basic {base64_auth}') - try: - with request.urlopen(req) as response: - if response.status == 201: - print(f" [✓] Comentário postado na Issue #{issue_id}") - else: - print(f" [X] Erro ao postar comentário: {response.status}") - except Exception as e: - print(f" [E] Falha na comunicação com Gitea via urllib: {e}") + with request.urlopen(req) as res: pass + except: pass + + def run_command(self, command): + """Executa comandos shell na VPS 4""" + print(f" [Exec] Rodando: {command}") + result = subprocess.run(command, shell=True, capture_output=True, text=True, cwd=self.local_path) + return {"stdout": result.stdout, "stderr": result.stderr} + + def write_file(self, path, content): + """Escreve/Edita arquivos no projeto""" + full_path = os.path.join(self.local_path, path) + os.makedirs(os.path.dirname(full_path), exist_ok=True) + with open(full_path, "w") as f: + f.write(content) + return f"Arquivo {path} escrito com sucesso." + + def read_file(self, path): + """Lê arquivos do projeto""" + full_path = os.path.join(self.local_path, path) + if os.path.exists(full_path): + with open(full_path, "r") as f: + return f.read() + return "Erro: Arquivo não encontrado." def execute(self): self.setup_workspace() - api_key = self.km.get_key('anthropic') - if not api_key: - return - + # Identificação de Localização Automática + vps_ip = subprocess.getoutput("hostname -I").split()[0] + vps_name = subprocess.getoutput("hostname") + + with open("/home/deploy/instrucoes/global/doutrina_agentes.md", "r") as f: doutrina = f.read() + + # Consciência Situacional Injetada + situational_awareness = f"\n\n[CONSCIÊNCIA SITUACIONAL]: Você está rodando na VPS: {vps_name} (IP: {vps_ip})\n" + + api_key = self.get_api_key() client = Anthropic(api_key=api_key) - prompt = self.get_system_prompt() + + print(f" [!] Agente iniciando na {vps_name} ({vps_ip})...") + + task = self.data.get('data', {}).get('issue', {}).get('body', 'Sem descrição') + + # O Agente agora tem um loop de pensamento e ferramentas + # Para este MVP, vamos permitir que ele faça uma análise e execute uma rodada de ferramentas + tools = [ + { + "name": "read_file", + "description": "Lê o conteúdo de um arquivo do projeto", + "input_schema": { + "type": "object", + "properties": {"path": {"type": "string"}}, + "required": ["path"] + } + }, + { + "name": "write_file", + "description": "Escreve ou edita um arquivo no projeto", + "input_schema": { + "type": "object", + "properties": { + "path": {"type": "string"}, + "content": {"type": "string"} + }, + "required": ["path", "content"] + } + }, + { + "name": "run_command", + "description": "Executa um comando shell (ex: docker compose up)", + "input_schema": { + "type": "object", + "properties": {"command": {"type": "string"}}, + "required": ["command"] + } + } + ] - print(" [!] IA Pensando na solução...") + response = client.messages.create( + model="claude-3-5-sonnet-20240620", + max_tokens=4000, + system=doutrina + situational_awareness, + tools=tools, + messages=[{"role": "user", "content": f"Tarefa: {task}\nAnalise o diretório {self.local_path} e resolva o problema."}] + ) + + # Processamento simples de ferramentas (para este estágio) + final_feedback = f"### 🤖 [VPS 4 - EXECUTOR]:\n\n{response.content[0].text}\n\n" - # Simulação de resposta da IA para o teste - feedback = "### 🤖 [VPS 4 - LABORATÓRIO]:\n* **Status**: Tarefa recebida e ambiente preparado.\n* **Ação**: Sincronização de arquivos concluída.\n* **Próximo**: Iniciando correção do erro de regressão de versão." + for content in response.content: + if content.type == "tool_use": + if content.name == "read_file": + res = self.read_file(content.input["path"]) + final_feedback += f"* **Lendo**: {content.input['path']}\n" + elif content.name == "write_file": + res = self.write_file(content.input["path"], content.input["content"]) + final_feedback += f"* **Escrevendo**: {content.input['path']}\n" + elif content.name == "run_command": + res = self.run_command(content.input["command"]) + final_feedback += f"* **Rodando**: `{content.input['command']}`\n" - self.post_comment(feedback) + self.post_comment(final_feedback) if __name__ == "__main__": + import argparse, random parser = argparse.ArgumentParser() - parser.add_argument("--data", help="Dados do Webhook JSON") + parser.add_argument("--data") args = parser.parse_args() - if args.data: - agent = SwarmAgent(json.loads(args.data)) - agent.execute() + SwarmAgent(json.loads(args.data)).execute() diff --git a/templates/swarm_listener.py b/templates/swarm_listener.py index a9e4e35..25d4621 100644 --- a/templates/swarm_listener.py +++ b/templates/swarm_listener.py @@ -13,7 +13,7 @@ r = redis.Redis(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASS, db=0) pubsub = r.pubsub() pubsub.subscribe(*CHANNELS) -print(f" [*] Aguardando eventos nos canais {CHANNELS}...") +print(f" [*] Swarm Listener Ativo - Aguardando eventos nos canais {CHANNELS}...") for message in pubsub.listen(): if message['type'] == 'message': @@ -21,13 +21,18 @@ for message in pubsub.listen(): payload = json.loads(message['data']) event = payload.get('event') repo = payload.get('repository') + channel = message['channel'].decode('utf-8') if isinstance(message['channel'], bytes) else message['channel'] - print(f" [!] Novo evento recebido: {event} no repo {repo}") - - # Aqui entrará a lógica de disparar o agente de IA específico - # Por enquanto, apenas registramos o recebimento. - # Exemplo de comando que seria rodado: - # subprocess.run(["python3", "agent_executor.py", "--event", event, "--data", json.dumps(payload)]) + print(f" [!] Novo evento recebido no canal {channel}: {event} no repo {repo}") + # Se for uma tarefa de desenvolvimento (tasks), dispara o executor inteligente + if channel == 'swarm:tasks': + executor_path = "/home/deploy/instrucoes/templates/agent_executor.py" + if os.path.exists(executor_path): + print(f" [>] Disparando executor inteligente: {executor_path}") + subprocess.run(["python3", executor_path, "--data", json.dumps(payload)]) + else: + print(f" [E] Executor não encontrado em {executor_path}") + except Exception as e: print(f" [E] Erro ao processar mensagem: {e}")