diff --git a/.aurelio/mcp_config.json b/.aurelio/mcp_config.json index 744fbb81..6c45672e 100644 --- a/.aurelio/mcp_config.json +++ b/.aurelio/mcp_config.json @@ -76,7 +76,7 @@ "_disabled": false }, "olhos-de-orpheu": { - "serverUrl": "https://mcp.portugalfuturista.org/sse", + "serverUrl": "http://192.168.0.16:8001/sse", "_disabled": false }, "memory": { diff --git a/extensions/aurelio-vscode/src/webview-ui/control-center-svelte/lib/messageBus.svelte.ts b/extensions/aurelio-vscode/src/webview-ui/control-center-svelte/lib/messageBus.svelte.ts index 669b6e72..f99b9cff 100644 --- a/extensions/aurelio-vscode/src/webview-ui/control-center-svelte/lib/messageBus.svelte.ts +++ b/extensions/aurelio-vscode/src/webview-ui/control-center-svelte/lib/messageBus.svelte.ts @@ -103,6 +103,23 @@ export function createDataStore() { }; break; + case 'mcp:result': + try { + if (msg.tool === 'list_printers') { + const parsed = JSON.parse(msg.result.content[0].text); + data = { ...data, printers: { ...(data.printers || {}), list: parsed } }; + } else if (msg.tool === 'list_print_jobs') { + const parsed = JSON.parse(msg.result.content[0].text); + data = { ...data, printers: { ...(data.printers || {}), jobs: parsed } }; + } + } catch (e) { + console.error("Failed to parse mcp:result for", msg.tool, e); + } + break; + case 'mcp:error': + console.error("MCP Tool Error:", msg.tool, msg.error); + break; + // ─── Antigravity (Proxmox CDP Bridge) ───────────────────── case 'antigravityStatus': data = { ...data, antigravity: { ...(data.antigravity || {}), status: msg.status } }; diff --git a/extensions/aurelio-vscode/src/webview-ui/control-center-svelte/tabs/PrintersTab.svelte b/extensions/aurelio-vscode/src/webview-ui/control-center-svelte/tabs/PrintersTab.svelte index df8355c6..98fe36df 100644 --- a/extensions/aurelio-vscode/src/webview-ui/control-center-svelte/tabs/PrintersTab.svelte +++ b/extensions/aurelio-vscode/src/webview-ui/control-center-svelte/tabs/PrintersTab.svelte @@ -15,12 +15,7 @@ setTimeout(() => { printerLoading = false; }, 2000); } - const printerFleet = [ - { name: 'HP Photosmart C4280', type: 'Photo Printer', icon: 'printer', status: 'Online' }, - { name: 'Brother QL-700', type: 'Label Printer', icon: 'layers', status: 'Online' }, - { name: 'POS-8030A', type: 'Thermal Receipt', icon: 'scroll', status: 'Online' }, - { name: 'Ricoh MP C2003', type: 'Office Copier', icon: 'server', status: 'Online' } - ]; +
@@ -39,7 +34,7 @@
- {#each printerFleet as printer} + {#each printerList as printer}
@@ -103,15 +98,26 @@ Configurações Rápidas
- - - diff --git a/extensions/aurelio-vscode/src/webview/controlCenterPanel.ts b/extensions/aurelio-vscode/src/webview/controlCenterPanel.ts index 75a80c23..c691f895 100644 --- a/extensions/aurelio-vscode/src/webview/controlCenterPanel.ts +++ b/extensions/aurelio-vscode/src/webview/controlCenterPanel.ts @@ -170,6 +170,16 @@ export class ControlCenterPanel { await ServiceRegistry.getInstance().get(McpServerManager).toggleTool(message.serverName, message.toolName, message.enabled); this._updateMcpStatus(); return; + case 'mcp:call': + try { + const manager = ServiceRegistry.getInstance().get(McpServerManager); + const result = await manager.callTool(message.server, message.tool, message.args); + this._panel.webview.postMessage({ command: 'mcp:result', tool: message.tool, result }); + } catch (e: any) { + this._panel.webview.postMessage({ command: 'mcp:error', tool: message.tool, error: e.message }); + vscode.window.showErrorMessage(`MCP call failed for ${message.tool}: ${e.message}`); + } + return; case 'openWorkflow': try { const doc = await vscode.workspace.openTextDocument(message.filePath); diff --git a/hardware_bridge_server.py b/hardware_bridge_server.py new file mode 100644 index 00000000..6952f66f --- /dev/null +++ b/hardware_bridge_server.py @@ -0,0 +1,82 @@ +import http.server +import socketserver +import json +import subprocess +import logging + +PORT = 8010 +logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') + +class HardwareBridgeHandler(http.server.SimpleHTTPRequestHandler): + def do_POST(self): + if self.path == '/api/printers/clean': + self.run_command(['hp-clean', '-i']) + elif self.path == '/api/printers/align': + self.run_command(['hp-align', '-i']) + elif self.path == '/api/printers/firmware': + self.run_command(['hp-firmware', '-n']) + elif self.path == '/api/printers/reset': + # Cancel all jobs + subprocess.run(['cancel', '-a'], check=False) + # Restart CUPS + subprocess.run(['systemctl', 'restart', 'cups'], check=False) + # Enable all printers (parsing lpstat -a) + try: + result = subprocess.run(['lpstat', '-a'], capture_output=True, text=True) + for line in result.stdout.splitlines(): + printer = line.split()[0] + subprocess.run(['cupsenable', printer], check=False) + subprocess.run(['cupsaccept', printer], check=False) + except Exception as e: + logging.error(f"Failed to enable printers: {e}") + self.send_response(200) + self.send_header('Content-type', 'application/json') + self.end_headers() + self.wfile.write(json.dumps({"status": "success", "message": "Hard reset completed"}).encode()) + else: + self.send_response(404) + self.end_headers() + + def run_command(self, cmd): + try: + logging.info(f"Executing: {cmd}") + # we use -i usually, but we need to run non-interactively if possible. + # actually hp-clean might need specific printer -p + content_length = int(self.headers.get('Content-Length', 0)) + post_data = b"" + if content_length > 0: + post_data = self.rfile.read(content_length) + + printer_id = None + if post_data: + try: + data = json.loads(post_data.decode('utf-8')) + printer_id = data.get('printer_id') + except: + pass + + if printer_id: + cmd.extend(['-p', printer_id]) + + result = subprocess.run(cmd, capture_output=True, text=True) + response = { + "status": "success" if result.returncode == 0 else "error", + "stdout": result.stdout, + "stderr": result.stderr, + "returncode": result.returncode + } + self.send_response(200 if result.returncode == 0 else 500) + self.send_header('Content-type', 'application/json') + self.end_headers() + self.wfile.write(json.dumps(response).encode()) + except Exception as e: + logging.error(f"Error executing {cmd}: {e}") + self.send_response(500) + self.send_header('Content-type', 'application/json') + self.end_headers() + self.wfile.write(json.dumps({"status": "error", "message": str(e)}).encode()) + +if __name__ == "__main__": + with socketserver.TCPServer(("", PORT), HardwareBridgeHandler) as httpd: + logging.info(f"Hardware Bridge Server listening on port {PORT}") + httpd.serve_forever() diff --git a/scripts/ocr_notebook.py b/scripts/ocr_notebook.py new file mode 100644 index 00000000..9c2cfee7 --- /dev/null +++ b/scripts/ocr_notebook.py @@ -0,0 +1,69 @@ +import os +import json +import base64 +from google import genai +from google.genai import types + +def ocr_scan_to_obsidian(image_path: str, obsidian_vault_path: str): + """Uses Gemini to OCR a scanned notebook page and save it to Obsidian.""" + print(f"Starting OCR on {image_path}...") + + # Initialize the client + client = genai.Client() + + # Read the image + with open(image_path, "rb") as image_file: + image_bytes = image_file.read() + + print("Uploading image to Gemini...") + + prompt = """ + You are an expert transcription assistant. + Carefully read the handwritten or printed notes in this scanned image. + Convert the contents to high-quality Markdown. + Preserve headings, bullet points, and any structural elements. + If there are diagrams, describe them briefly. + Output ONLY the markdown text. + """ + + response = client.models.generate_content( + model='gemini-2.5-pro', + contents=[ + prompt, + types.Part.from_bytes( + data=image_bytes, + mime_type='image/png', + ), + ] + ) + + markdown_content = response.text.strip() + + # Create the obsidian note + note_title = "Scanned Notebook Page.md" + note_path = os.path.join(obsidian_vault_path, note_title) + + print(f"Saving to {note_path}...") + + # Add metadata frontmatter + final_content = f"""--- +source: scan +lineage_image: {image_path} +tags: [scanned, notebook] +--- + +{markdown_content} +""" + + # Ensure vault exists + os.makedirs(obsidian_vault_path, exist_ok=True) + + with open(note_path, "w") as f: + f.write(final_content) + + print("Successfully ingested scanned notebook to Obsidian.") + +if __name__ == "__main__": + scan_image = "/tmp/scan.png" + vault = "/home/fcunha/desasossego" + ocr_scan_to_obsidian(scan_image, vault)