Ejecutar código
Usa esta acción cuando un paso necesite lógica personalizada que CEL no puede expresar — reformatear datos, calcular valores, procesar archivos o llamar a una API con código. Una sola acción cubre ambos lenguajes: elige JavaScript o Python en el campo Lenguaje.
Mejor para
- Transformar JSON de pasos anteriores exactamente a la forma que un paso posterior necesita
- Cálculos, parsing y validaciones más allá de lo que pueden hacer las expresiones CEL
- Pequeñas integraciones usando
fetch(JavaScript) cuando la acción Solicitud HTTP no es lo bastante flexible - Trabajo con documentos y archivos usando las bibliotecas incluidas de Python — hojas de cálculo, PDFs, imágenes, DOCX/PPTX
Elegir el lenguaje
| JavaScript | Python | |
|---|---|---|
| Runtime | Sandbox V8 aislado (sin APIs de Node.js) | Python 3.12 en un contenedor desechable |
| Red | fetch con protección SSRF | Ninguna — por diseño |
| Tiempo límite (ms) | 100–30.000, por defecto 5.000 | 1.000–120.000, por defecto 30.000 |
| Memoria (MB) | 8–256, por defecto 128 | 128–2.048, por defecto 512 |
| Bibliotecas | Recursos estándar de JavaScript | openpyxl, python-docx, python-pptx, pdfplumber, pypdf, pandas, Pillow, reportlab, markitdown — sin pip en tiempo de ejecución |
Ambos lenguajes comparten las mismas entradas y salidas: Datos de entrada estructurados, Archivos de entrada, un resultado estructurado, archivos de salida y logs en un formato unificado.
Campos principales
| Campo | Qué hace |
|---|---|
| Lenguaje | javascript (por defecto) o python |
| Código | El código a ejecutar. JavaScript se envuelve en una IIFE asíncrona, así que puedes usar await en el nivel superior y terminar con return <valor>. Python se ejecuta como un script normal; escribe el resultado en /work/out/output.json |
| Datos de entrada | Expresión CEL opcional evaluada antes de la ejecución. JavaScript ve el valor en la variable input; Python lo ve en la variable INPUT |
| Archivos de entrada | Expresión CEL opcional que evalúa a un array de referencias de storage [{bucket, fullPath, name?}] — por ejemplo step(0).files o un input de workflow de tipo archivo. El contenido de los archivos se entrega al sandbox; el código nunca accede al storage directamente |
| Propósito | Etiqueta opcional de lo que hace el código, mostrada en los logs |
| Tiempo límite (ms) | Límite de tiempo de ejecución en milisegundos, acotado por lenguaje (ver la tabla de arriba). El código que lo supera — incluidos los bucles infinitos — se termina |
| Límite de memoria (MB) | Límite de memoria del sandbox, acotado por lenguaje |
Archivos de entrada y de salida
Ambas direcciones comparten los mismos límites: como máximo 10 archivos, 20 MiB por archivo, 24 MiB en total. Los nombres de archivo deben empezar con letra o dígito y pueden contener letras, dígitos, ., _, - y espacios (hasta 128 caracteres). Los nombres input.json y output.json están reservados — son los canales de datos estructurados.
Leer archivos de entrada:
- JavaScript —
files.readText(name)devuelve el contenido como string de texto (decodificado como UTF-8);files.read(name)lo devuelve como string base64, para datos binarios - Python — los archivos aparecen en
/work/in/<nombre>(solo lectura)
Escribir archivos de salida:
- JavaScript —
files.writeText(name, text, {contentType?})para texto;files.write(name, contentBase64, {contentType?})para binario.contentTypese infiere de la extensión cuando se omite - Python — escribe los archivos en
/work/out/
Para trabajar con texto — CSV, JSON, Markdown — usa readText / writeText y olvídate del base64:
const rows = files.readText("data.csv").split("\n").map((line) => line.split(","));
files.writeText("summary.txt", `${rows.length} filas`);
El par base64 existe para archivos binarios (imágenes, PDFs, hojas de cálculo), normalmente para pasar los bytes sin cambios o entregarlos a fetch.
Los archivos de salida se suben al storage de tu empresa después de la ejecución y aparecen como referencias de storage en step(N).files. Los pasos posteriores pueden encadenarlos — por ejemplo, un paso Python cuyos Archivos de entrada sean step(0).files lee todo lo que produjo el paso de código anterior.
El sandbox JavaScript
El código se ejecuta en un sandbox V8 aislado en un servicio de ejecución separado. No hay acceso a APIs de Node.js (require, process, sistema de archivos) ni a APIs de navegador — solo los recursos estándar de JavaScript más estos puentes:
input— el valor evaluado de Datos de entrada (nullcuando está ausente)console.log/info/warn/error— capturados y devueltos en la salidalogsfetch(url, options)— HTTP con protección SSRF (las direcciones privadas e internas están bloqueadas) que devuelve{ok, status, statusText, body}, conbodyinterpretado como JSON cuando es posible y devuelto como texto en caso contrario. Los cuerpos de respuesta están limitados a 10 MiB: una respuesta mayor hace fallar el paso en lugar de almacenarse en memoriafiles.read/files.readText/files.write/files.writeText— ver arribaatob(base64)/btoa(binaryString)— el codec base64 estándar, para convertir los valores con los que trabajanfiles.readyfiles.write.btoasigue la regla de la web de rechazar caracteres por encima deU+00FF, así que usafiles.writeTextpara texto UTF-8 en lugar debtoa
No existe Buffer, ni TextDecoder/TextEncoder, ni require — atob/btoa y los helpers de texto de files son la forma soportada de convertir entre bytes y strings.
El sandbox Python
El código se ejecuta como python3 job.py en un contenedor nuevo que se destruye después de la ejecución:
- Sin acceso a la red — deliberado. Usa el lenguaje JavaScript para llamadas HTTP
INPUT— el valor evaluado de Datos de entrada, cargado desde/work/in/input.json(Nonecuando está ausente)- Archivos de entrada en
/work/in/(solo lectura), archivos de salida en/work/out/ - Escribe el resultado estructurado del paso como JSON en
/work/out/output.json— eso es lo que recibestep(N).result.print()va a la salidalogs, no al resultado. Un job que no escribeoutput.jsonrecibestep(N).result=null - Solo las bibliotecas incluidas listadas arriba son importables — no hay
pipen tiempo de ejecución - Solo un job Python se ejecuta a la vez; un runner ocupado hace fallar el paso con un error reintentable (ver abajo)
Qué pueden usar los pasos posteriores
step(N).result— el resultado estructurado (valor dereturnen JavaScript /output.jsonen Python), limitado a 2 MiB — escribe los datos voluminosos como archivos.nullcuando el código no devolvió nada / no escribióoutput.jsonstep(N).files— referencias de storage de los archivos que el código escribió:[{name, bucket, fullPath, contentType, size}]step(N).logs— salida capturada como entradas[{level, message}](niveles de consola en JavaScript;stdout/stderren Python), hasta 200 entradas de 2.000 caracteres cada unastep(N).executionTimeMs— cuánto tiempo se ejecutó el código
Las versiones anteriores exponían el valor de retorno de JavaScript como
step(N).data. Ahora esstep(N).result, para ambos lenguajes.
Errores
| Código | Significado |
|---|---|
code_execution_failed | El código lanzó una excepción — revisa step(N).logs |
code_execution_timeout | El código superó el tiempo límite (ambos lenguajes) |
code_executor_busy | El único slot del runner Python está ocupado — reintentable; volver a ejecutar el paso es seguro |
code_result_too_large | El resultado serializado supera los 2 MiB — devuelve un resumen pequeño y escribe los datos voluminosos como archivos |
code_result_invalid_json | Python escribió /work/out/output.json, pero no es JSON válido |
code_input_files_invalid | Archivos de entrada no evaluó a referencias válidas, superó los límites o usó un nombre reservado |
code_input_file_not_accessible | Un archivo de entrada no se encontró o no es accesible desde este workspace |
code_input_file_unavailable | Fallo transitorio de storage al leer un archivo de entrada — reintentable |
python_unavailable | El lenguaje Python no está habilitado en este entorno |
Consejos
- Siempre produce un resultado —
returnen JavaScript,output.jsonen Python — y mantenlo compatible con JSON. - Usa
console.log/print()mientras construyes el paso; la salidalogses la forma más fácil de depurar. - Cada llamada a
fetchtambién está limitada por el tiempo del paso, así que mantén las llamadas a terceros bien dentro del presupuesto de tiempo. - Los archivos son el canal correcto para cualquier cosa grande: el resultado está limitado a 2 MiB, mientras que los archivos llevan hasta 24 MiB por ejecución.