---
name: comfyui_fetch_output_mesh
kind: function
lang: py
domain: ml
version: "1.0.0"
purity: impure
signature: "def comfyui_fetch_output_mesh(prompt_id: str, *, server: str = \"127.0.0.1:8188\", dest: str | None = None, timeout: float = 120.0) -> dict"
description: "Localiza y descarga la malla 3D producida por un workflow ComfyUI a disco local. Hermana de comfyui_fetch_output_image pero para mallas: el nodo SaveGLB expone su salida en GET /history/{prompt_id} bajo la clave '3d' (no 'images'). Localiza el primer .glb/.obj/.ply/.gltf/.fbx/.stl, lo baja via GET /view y opcionalmente lo escribe en dest. Impura: HTTP GET + escritura en disco, solo stdlib."
tags: [comfyui, ml, img-to-3d, hunyuan3d, mesh, download, workflow]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: error_go_core
imports: []
params:
  - name: prompt_id
    desc: "id devuelto por comfyui_submit_workflow, de un workflow cuyo nodo SaveGLB ya termino (usa comfyui_wait_result antes si dudas)."
  - name: server
    desc: "host:port del servidor ComfyUI sin esquema. keyword-only."
  - name: dest
    desc: "Ruta destino. Si None, escribe el basename de la malla en el cwd. Si es un directorio (o termina en separador), escribe el basename dentro. Si es una ruta de archivo, escribe ahi. keyword-only."
  - name: timeout
    desc: "Timeout de cada peticion HTTP en segundos. keyword-only."
output: "dict {ok, path, format, bytes, error}. path = ruta local del archivo de malla guardado, format = extension sin punto (ej. 'glb'), bytes = bytes descargados. Si falla, ok=False y error explica (sin malla en history, HTTP, conexion o escritura)."
tested: false
tests: []
test_file_path: ""
file_path: "python/functions/ml/comfyui_fetch_output_mesh.py"
---

## Ejemplo

```python
import sys, os
sys.path.insert(0, os.path.join(os.environ["HOME"], "fn_registry", "python", "functions"))
from ml.comfyui_fetch_output_mesh import comfyui_fetch_output_mesh

# Tras comfyui_submit_workflow + comfyui_wait_result de un workflow imagen->3D,
# baja el .glb al disco (el SaveGLB lo expone en /history bajo la clave "3d").
res = comfyui_fetch_output_mesh("2817f111-e21b-4672-95e7-5bec4314c4a7", dest="/tmp/meshes")
# res == {"ok": True, "path": "/tmp/meshes/3d_robot_mesh_00001_.glb",
#         "format": "glb", "bytes": 60051544, "error": ""}
```

Lánzalo con el python del venv (import de arriba o heredoc). Nota: `./fn run` directo no aplica porque la firma usa `*` (keyword-only), no soportado por el generador de runner de `fn run`.

## Cuando usarla

Despues de reconstruir una malla 3D (submit + wait de un workflow Hunyuan3D),
cuando necesites el archivo .glb/.obj/.ply real en disco (no solo su nombre): para
abrirlo en un visor, post-procesarlo (decimar, recolorear) o moverlo a un vault.
Para el flujo completo desde una imagen en disco usa el pipeline
`comfyui_image_to_3d_oneshot`, que ya llama a esta funcion al final.

## Gotchas

- Impura: hace HTTP GET a /history y /view y escribe en disco. Requiere el server
  vivo y que el prompt YA haya terminado (usa `comfyui_wait_result` antes).
- El SaveGLB expone la malla bajo la clave `"3d"` en los outputs, NO bajo
  `"images"` — por eso `comfyui_fetch_output_image` no sirve para mallas.
- El history se purga al reiniciar el server: si el prompt ya no esta, devuelve
  `ok=False` con "no esta en /history". No reintenta; reintenta tu desde el caller.
- Toma el PRIMER archivo de malla que encuentra (prioriza la clave "3d"). Si un
  workflow exporta varios formatos, baja solo uno; para los demas, llama otra vez
  o usa GET /view con el filename concreto.
- `dest` se interpreta: None -> cwd; directorio -> dentro; archivo -> esa ruta.
