chore: auto-commit (286 archivos)
- .claude/agents/fn-orquestador/SKILL.md - .claude/commands/fn_claude.md - .claude/rules/INDEX.md - .claude/rules/cpp_apps.md - .claude/rules/ids_naming.md - CHANGELOG.md - apps/dag_engine/README.md - apps/dag_engine/api.go - apps/dag_engine/dags_migrated/example.yaml - apps/dag_engine/dags_migrated/example_lineage_tracking.yaml - ... Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -31,6 +31,12 @@ Indice de grupos de capacidades del registry. Cada grupo agrupa >=3 funciones qu
|
||||
| [playwright](playwright.md) | 6 | E2E browser: launch chromium, login kanban, drag dnd-kit, keyboard sequence, wait predicate, assert class |
|
||||
| [cpp-tables](tql.md) | 9 | Table Query Language C++ puro: filter, group, agg, sort, join, stats, formulas Lua, round-trip emit/apply |
|
||||
| [data-table-renderers](data_table_renderers.md) | 1 | API declarativa de cell renderers para data_table: Badge, Progress, Duration, Icon via TableInput.column_specs |
|
||||
| [scheduler](scheduler.md) | 4 | Cron expression parsing, matching, next-run y traduccion humana (consume `apps/dag_engine`) |
|
||||
| [extractor](extractor.md) | 15 | Funciones que leen datos de fuentes externas (BD, API, archivos, web). Nodos input de `data_factory` |
|
||||
| [transformer](transformer.md) | 15 | Funciones que clean/dedup/aggregate/feature-engineer datos. Nodos intermedios de `data_factory` |
|
||||
| [sink](sink.md) | 11 | Funciones que escriben datos a destino externo (BD, dashboard, alerta, email). Nodos output |
|
||||
| [validator](validator.md) | 6 | Funciones que verifican datos/config contra reglas. Pre-flight de sinks y gates en DAGs |
|
||||
| [navegator](navegator.md) | 4 | Automatización de browser via CDP + AX tree + LLM: obtener, limpiar, chunkear AX tree y llamar a Claude CLI |
|
||||
|
||||
## Como anadir grupo
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Capability: android
|
||||
|
||||
_(Descripcion del grupo — editar a mano)_
|
||||
Toolbelt Android operable desde WSL2. Cubre: ADB (`adb_wsl`, conexion al daemon Windows), AVD emulator management (list/start/stop/wait, geo-fix), APK lifecycle (`android_apk_install`, `android_app_clear`, `android_app_launch`, `android_uninstall`), Capacitor build pipelines (`capacitor_build_apk`, `deploy_capacitor_to_emulator`), logcat streaming. WSL2 -> Windows adb daemon, no requiere Android Studio.
|
||||
|
||||
## Funciones
|
||||
|
||||
@@ -47,8 +47,31 @@ _(Descripcion del grupo — editar a mano)_
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
_(Anadir 1-2 bloques de codigo end-to-end)_
|
||||
### Arrancar emulator + deploy Capacitor + logcat
|
||||
|
||||
```bash
|
||||
./fn run android_emulator_list
|
||||
./fn run android_emulator_start --avd Pixel_7_API_34 --wait
|
||||
|
||||
./fn run deploy_capacitor_to_emulator \
|
||||
--app-dir ~/projects/myapp \
|
||||
--avd Pixel_7_API_34 \
|
||||
--package com.example.myapp
|
||||
|
||||
./fn run adb_wsl -- logcat -v time | grep -E "(myapp|FATAL|AndroidRuntime)"
|
||||
```
|
||||
|
||||
### APK install/uninstall manual
|
||||
|
||||
```bash
|
||||
./fn run android_apk_install --apk ~/myapp.apk --device emulator-5554
|
||||
./fn run android_app_launch --package com.example.myapp
|
||||
./fn run android_app_clear --package com.example.myapp # limpia datos sin uninstall
|
||||
```
|
||||
|
||||
## Fronteras
|
||||
|
||||
_(Que NO cubre este grupo)_
|
||||
- **NO compila APK desde Gradle nativo**. Solo Capacitor build. Para Gradle puro, ver issue 0076 (`gradle_run`).
|
||||
- **NO instala Android SDK**. Asume `ANDROID_HOME` apuntando al SDK Windows accessible via WSL.
|
||||
- **NO maneja iOS**. Solo Android. Para iOS, ver issue 0072h (gamedev roadmap).
|
||||
- **NO depura nativo NDK**. Para LLDB-stage debugging, attach manual via Android Studio.
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
# Capability: bigquery
|
||||
|
||||
_(Descripcion del grupo — editar a mano)_
|
||||
Operar Google BigQuery via SDK Python `google-cloud-bigquery` y Go `fetch_data_frame`. Cubre: auth (`bq_auth`), queries con caching, gestion de datasets/tables (create/copy/delete/get/list), jobs (cancel, wait), schemas, exports a GCS, y carga a DataFrames (`fetch_data_frame`, polars).
|
||||
|
||||
## Funciones
|
||||
|
||||
@@ -36,8 +36,35 @@ _(Descripcion del grupo — editar a mano)_
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
_(Anadir 1-2 bloques de codigo end-to-end)_
|
||||
### Query + cargar a polars
|
||||
|
||||
```python
|
||||
import os, sys
|
||||
sys.path.insert(0, os.path.join(os.environ["FN_REGISTRY_ROOT"], "python", "functions"))
|
||||
from infra import bq_auth, bq_query
|
||||
|
||||
client = bq_auth(project_id="my-gcp-project")
|
||||
df = bq_query(client, "SELECT * FROM `my-gcp-project.dataset.table` LIMIT 1000")
|
||||
print(df.shape)
|
||||
```
|
||||
|
||||
### Gestion de tablas
|
||||
|
||||
```bash
|
||||
./fn run bq_list_tables --dataset analytics
|
||||
./fn run bq_create_table --dataset analytics --table events --schema-file schema.json
|
||||
./fn run bq_copy_table --src analytics.events --dst analytics.events_backup
|
||||
```
|
||||
|
||||
### Carga a DataFrame Go (polars/pandas-go)
|
||||
|
||||
```bash
|
||||
./fn run fetch_data_frame --query "SELECT date, sales FROM analytics.daily" --output sales.parquet
|
||||
```
|
||||
|
||||
## Fronteras
|
||||
|
||||
_(Que NO cubre este grupo)_
|
||||
- **NO maneja Auth ADC interactivo**. Asume credenciales via service account JSON o `gcloud auth application-default login` ya ejecutado.
|
||||
- **NO billing analytics**. Para coste/quota analysis, BigQuery UI o Cloud Console.
|
||||
- **NO orquesta Dataflow ni Dataproc**. Solo SQL queries + table ops.
|
||||
- Comparte funciones con tags `gcp` y `google-cloud` (mismas funciones, multi-tag).
|
||||
|
||||
@@ -12,7 +12,9 @@ Operar apps C++ del registry en Windows desde WSL2: compilar, desplegar, lanzar,
|
||||
| `is_cpp_app_running_windows_bash_infra` | `is_cpp_app_running_windows(app_name)` | Exit 0 si el proceso esta vivo (tasklist.exe), stdout: `RUNNING: PID=N MEM=...K` |
|
||||
| `launch_cpp_app_windows_bash_infra` | `launch_cpp_app_windows(app_name, [desktop_dir])` | Lanza .exe en Windows via cmd.exe /c start, retorna inmediatamente |
|
||||
| `e2e_run_cpp_windows_bash_infra` | `e2e_run_cpp_windows(target, [--no-build], [--no-deploy])` | Build + deploy + run headless de app C++ (tests e2e tipo altsnap) |
|
||||
| `redeploy_cpp_app_windows_bash_pipelines` | `redeploy_cpp_app_windows(app_name, app_dir, [--build])` | Pipeline completo: build? + deploy + launch + verify en un comando |
|
||||
| `redeploy_cpp_app_windows_bash_pipelines` | `redeploy_cpp_app_windows(app_name, app_dir, [--build])` | Pipeline completo (UNA app): build? + deploy + launch + verify |
|
||||
| `redeploy_all_cpp_apps_bash_pipelines` | `redeploy_all_cpp_apps([filter])` | Bulk: compila TODO el arbol cpp/ en un cmake pass + despliega cada `.exe`. Filtro opcional por substring de nombre. Tolerante a fallos por app. |
|
||||
| `resolve_cpp_app_dir_bash_infra` | `resolve_cpp_app_dir([app_name])` | Resuelve `<name>\t<abs_dir>`. Busca en `apps/`, `cpp/apps/` (legacy), `projects/*/apps/`. Sin arg deduce desde CWD. |
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
@@ -78,3 +80,23 @@ fi
|
||||
El flujo estandar es: `build_cpp_windows` → `deploy_cpp_exe_to_windows` → `launch_cpp_app_windows` → `is_cpp_app_running_windows`. El pipeline `redeploy_cpp_app_windows` encapsula los pasos 2-4 (con 1 opcional via `--build`).
|
||||
|
||||
`deploy_cpp_exe_to_windows` ya incluye un `taskkill.exe` interno — no es necesario matar el proceso manualmente antes de llamarlo. `is_cpp_app_running_windows` es util cuando se quiere tomar la decision de matar/no matar de forma explicita antes del deploy.
|
||||
|
||||
### Bulk redeploy (2026-05-16)
|
||||
|
||||
Tras cambios en `cpp/framework/app_base.*` o cualquier funcion linkada por muchos apps, usar:
|
||||
|
||||
```bash
|
||||
./fn run redeploy_all_cpp_apps # todas las apps
|
||||
./fn run redeploy_all_cpp_apps graph # solo apps con "graph" en el nombre
|
||||
```
|
||||
|
||||
Build best-effort: si test targets rotos (`test_llm_anthropic`, `test_graph_icons` usan `setenv()` no disponible en mingw), el pipeline avisa pero sigue desplegando los `.exe` que SI se construyeron. Resumen final con OK/SKIPPED/FAILED por app.
|
||||
|
||||
### Layouts soportados
|
||||
|
||||
`resolve_cpp_app_dir` (y por tanto `compile_cpp_app`) busca apps en este orden:
|
||||
1. `apps/<X>/` — canonical issue 0096.
|
||||
2. `cpp/apps/<X>/` — legacy.
|
||||
3. `projects/*/apps/<X>/` — apps de proyectos.
|
||||
|
||||
Tambien deduce desde CWD si la sesion esta dentro de cualquiera de las tres. Fix retroactivo: antes del 2026-05-16 `resolve_cpp_app_dir` no veia `apps/<X>/` y `./fn run compile_cpp_app <name>` fallaba para apps en ese layout.
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
# Capability: deploy
|
||||
|
||||
Deploy completo de apps Go/C++/Capacitor a destinos diversos: VPS remotos via SSH+systemd+rsync, VPS con Docker+Traefik+Coolify, Windows Desktop via cross-compile WSL2->mingw-w64, emuladores Android. Cubre setup inicial (vps_setup_app, generate_dockerfile, traefik dynamic), deploys continuos (rsync_deploy + systemd_restart + wait_for_http), webhooks Gitea, y health checks.
|
||||
|
||||
## Funciones
|
||||
|
||||
| ID | Firma | Que hace |
|
||||
|---|---|---|
|
||||
| `compile_cpp_app_bash_pipelines` | `compile_cpp_app(app_name?: string) -> void` | Pipeline que resuelve la app C++ desde el nombre o CWD, la cross-compila para Windows con mingw-w64, y despliega el .exe al escritorio de Windows. Composicion de resolve_cpp_app_dir + build_cpp_windows + deploy_cpp_exe_to_windows. |
|
||||
| `deploy_app_go_infra` | `func DeployApp(appDir string, imageName string, port int, envVars map[string]string) (string, error)` | Orquesta el deploy completo de una app Go en Docker. Pasos: genera Dockerfile, lo escribe a disco, construye la imagen y lanza el contenedor en modo detach con port mapping. Retorna el container ID. |
|
||||
| `deploy_app_remote_go_infra` | `func DeployAppRemote(conn SSHConn, cfg DeployConfig) error` | Orquesta el deploy continuo de una app a un VPS: verifica SSH, compila localmente, sube binario, reinicia systemd y hace health check. |
|
||||
| `deploy_capacitor_to_emulator_bash_pipelines` | `deploy_capacitor_to_emulator(app_dir: string, avd_name?: string, package_name?: string) -> void` | Pipeline end-to-end: build Capacitor APK + arranca AVD + instala + opcionalmente lanza la app. Valida que el AVD existe, construye el APK con capacitor_build_apk, arranca el emulador de forma idempotente, instala el APK y lanza la app si se da package_name. Imprime comando logcat sugerido al final. |
|
||||
| `deploy_cpp_exe_to_windows_bash_infra` | `deploy_cpp_exe_to_windows(app_name: string, app_dir: string) -> void` | Copia el .exe de Windows (compilado por build_cpp_windows) y sus assets al escritorio de Windows /mnt/c/Users/lucas/Desktop/apps/<APP>/. Mata el proceso si esta corriendo (taskkill.exe pre-autorizado), copia DLLs, sincroniza assets/ y enrichers/ con rsync, maneja runtime Python embebido si python_runtime: true en app.md, y copia extras gx-cli. Preserva siempre local_files/ (estado del usuario). |
|
||||
| `docker_compose_remote_deploy_bash_infra` | `docker_compose_remote_deploy(host: string, remote_dir: string, branch: string, compose_files: string) -> json` | Despliega un stack Docker Compose en un host remoto via SSH. Verifica conectividad, hace git pull del branch indicado, actualiza imagenes con docker-compose pull y levanta/recrea los servicios modificados con docker-compose up -d. Soporta compose files adicionales. Retorna JSON con status, containers corriendo y duracion. |
|
||||
| `dockerize_app_bash_pipelines` | `dockerize_app(app_name: string, [--domain DOMAIN], [--port PORT], [--ssh-host HOST], [--remote-dir DIR], [--basic-auth USER:PASS], [--no-auth], [--no-gzip], [--env KEY=VAL]..., [--volume NAME], [--build-cmd CMD], [--standalone], [--dry-run]) -> json` | Empaqueta una app Go del registry para deploy a VPS organic-machine via Docker + Traefik + Coolify. Genera Dockerfile multi-stage, docker-compose.yml, traefik-dynamic.yml con basicAuth opcional y gzip, sube via rsync al VPS y arranca el stack remoto. Replica el patron de apps/registry_api/. |
|
||||
| `generate_compose_traefik_go_infra` | `func GenerateComposeTraefik(cfg ComposeTraefikConfig) string` | Genera el texto YAML de un docker-compose.yml para una app Go desplegada behind Traefik + Coolify. Replica el patron de apps/registry_api/docker-compose.yml. Determinista: orden de EnvVars sigue el orden de entrada. |
|
||||
| `generate_dockerfile_go_infra` | `func GenerateDockerfile(binaryName string, port int, envVars map[string]string) string` | Genera el texto de un Dockerfile multi-stage para una app Go. Stage build con golang:1.23-alpine, stage final con alpine:latest. Incluye ENV vars del map con orden determinista. Funcion pura sin I/O. |
|
||||
| `generate_traefik_dynamic_go_infra` | `func GenerateTraefikDynamic(cfg TraefikDynamicConfig) string` | Genera el texto YAML de un traefik-dynamic.yml para el file provider de Traefik (Coolify). Replica el patron de apps/registry_api/traefik-dynamic.yml con routers HTTP/HTTPS, redirect, basicAuth opcional y gzip opcional. |
|
||||
| `gitea_create_webhook_bash_infra` | `gitea_create_webhook(owner: string, repo: string, target_url: string, secret?: string) -> json` | Crea un webhook de push en un repositorio Gitea. El webhook notifica a target_url en cada push. |
|
||||
| `go_build_binary_go_infra` | `func GoBuildBinary(projectDir, outputPath string, ldflags string, tags string) error` | Compila un binario Go desde un directorio de proyecto. Si ldflags está vacío usa -s -w (strip debug). Si outputPath está vacío usa build/{dirname} dentro del projectDir. Ejecuta con CGO_ENABLED=0. |
|
||||
| `rsync_deploy_bash_infra` | `rsync_deploy(local_dir: string, ssh_alias: string, remote_dir: string) -> json` | Sincroniza un directorio local a un host remoto via rsync+SSH. Excluye archivos de desarrollo y bases de datos locales. Crea el directorio remoto si no existe. |
|
||||
| `setup_registry_api_bash_infra` | `setup_registry_api(ssh_host: string, api_token: string, basic_auth_user: string, basic_auth_pass: string) -> json` | Deploy completo de registry_api en VPS con Docker + Traefik (Coolify proxy). Sincroniza el repo via rsync, genera el hash bcrypt para basicAuth, sube el traefik-dynamic.yml, crea el .env con el token, hace docker compose build+up y verifica el health check. |
|
||||
| `setup_vps_app_go_infra` | `func SetupVPSApp(conn SSHConn, cfg DeployConfig) error` | Orquesta el setup inicial de una app en un VPS remoto: verifica SSH, crea dirs y usuario, sube binario, instala systemd unit y hace health check. |
|
||||
| `stop_app_go_infra` | `func StopApp(containerName string, removeImage bool) error` | Para y elimina el contenedor de una app desplegada. Si removeImage es true elimina también la imagen Docker. containerName debe coincidir con el imageName usado en deploy_app. |
|
||||
| `systemd_generate_unit_go_infra` | `func SystemdGenerateUnit(name, execStart, workDir, user string, env map[string]string) string` | Genera el texto de un archivo .service de systemd para una app. Incluye restart automático y env vars en orden determinista. |
|
||||
| `systemd_install_go_infra` | `func SystemdInstall(conn SSHConn, unitName, unitContent string) error` | Sube un unit file al host remoto, hace daemon-reload, enable y restart. Idempotente: reemplaza si el unit ya existe. |
|
||||
| `vps_setup_app_go_infra` | `func VPSSetupApp(conn SSHConn, appName, remoteDir, serviceUser string) error` | Prepara un host remoto para recibir una app: crea directorios, usuario de servicio y asigna ownership. |
|
||||
| `wait_for_http_bash_infra` | `wait_for_http <url> [timeout_seconds] [interval_seconds]` | Hace polling a una URL HTTP/HTTPS hasta recibir respuesta 2xx o agotar el timeout. Util en deploys, post-restart de servicios y smoke tests. |
|
||||
| `write_dockerfile_go_infra` | `func WriteDockerfile(dir, content string) (string, error)` | Escribe content en dir/Dockerfile. Crea el directorio si no existe. Retorna el path absoluto del archivo escrito. Compañera impura de generate_dockerfile. |
|
||||
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
### App Go a VPS con Docker+Traefik
|
||||
|
||||
```bash
|
||||
./fn run dockerize_app my_api \
|
||||
--domain my_api.organic-machine.com \
|
||||
--port 8080 \
|
||||
--ssh-host organic-machine \
|
||||
--remote-dir /srv/coolify/my_api \
|
||||
--basic-auth admin:supersecret
|
||||
```
|
||||
|
||||
Empaqueta `apps/my_api/` (Go), genera Dockerfile multistage + docker-compose + traefik-dynamic con basicAuth, sube via rsync, levanta el stack remoto.
|
||||
|
||||
### App Go a VPS con systemd (sin Docker)
|
||||
|
||||
```bash
|
||||
# Setup inicial
|
||||
./fn run setup_vps_app --host organic-machine --app my_api --port 8080
|
||||
|
||||
# Deploy continuo (build local + rsync + restart systemd + health check)
|
||||
./fn run deploy_app_remote --host organic-machine --app my_api
|
||||
```
|
||||
|
||||
### App C++ a Windows Desktop desde WSL2
|
||||
|
||||
```bash
|
||||
./fn run compile_cpp_app registry_dashboard
|
||||
# Cross-compila con mingw-w64 + deploya .exe + DLLs + assets a /mnt/c/Users/lucas/Desktop/apps/registry_dashboard/
|
||||
```
|
||||
|
||||
## Fronteras
|
||||
|
||||
- **NO orquesta CI/CD pipelines**. Si necesitas pipelines completas (GitHub Actions, Gitea Actions), construye yaml a mano.
|
||||
- **NO maneja secretos rotativos**. Pasa secretos via env vars; rotacion es manual.
|
||||
- **NO hace blue-green ni canary**. Cada deploy es un swap directo. Si necesitas zero-downtime sofisticado, usa Traefik con multiple instancias y label-based routing manual.
|
||||
- **NO incluye monitoring post-deploy**. Health check inicial sí; observabilidad continua es responsabilidad del operador (ver grupo `registry` para auditoria).
|
||||
@@ -1,6 +1,6 @@
|
||||
# Capability: docker
|
||||
|
||||
_(Descripcion del grupo — editar a mano)_
|
||||
Operar Docker desde Go y Bash. Cubre: build (`docker_build_image`), run/stop/rm (`docker_run`, `docker_stop_container`, `docker_rm_container`), Docker Compose (`docker_compose_up`, `down`, `restart`, `ps`, remoto via SSH), inspect/logs/exec, networks, volumes, registry push/pull. Pipelines de deploy combinan estas atomicas: `deploy_app`, `dockerize_app`, `docker_compose_remote_deploy`.
|
||||
|
||||
## Funciones
|
||||
|
||||
@@ -48,8 +48,37 @@ _(Descripcion del grupo — editar a mano)_
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
_(Anadir 1-2 bloques de codigo end-to-end)_
|
||||
### Build + run local
|
||||
|
||||
```bash
|
||||
./fn run docker_build_image --path . --tag myapp:latest
|
||||
./fn run docker_run --image myapp:latest --name myapp --port 8080:8080 --detach
|
||||
./fn run docker_inspect_container --name myapp | jq .
|
||||
./fn run docker_container_logs --name myapp --tail 100
|
||||
```
|
||||
|
||||
### Docker Compose local
|
||||
|
||||
```bash
|
||||
./fn run docker_compose_up --file docker-compose.yml --detach
|
||||
./fn run docker_compose_ps
|
||||
./fn run docker_compose_restart --service api
|
||||
./fn run docker_compose_down
|
||||
```
|
||||
|
||||
### Deploy a VPS con Compose + git pull
|
||||
|
||||
```bash
|
||||
./fn run docker_compose_remote_deploy \
|
||||
--host organic-machine \
|
||||
--remote-dir /srv/coolify/myapp \
|
||||
--branch master \
|
||||
--compose-files "docker-compose.yml docker-compose.prod.yml"
|
||||
```
|
||||
|
||||
## Fronteras
|
||||
|
||||
_(Que NO cubre este grupo)_
|
||||
- **NO maneja Kubernetes ni Swarm**. Solo Docker engine + Compose.
|
||||
- **NO instala Docker**. Asume `docker` y `docker compose` ya disponibles en PATH (local + remoto).
|
||||
- **NO genera Dockerfiles a partir de codigo**. Solo opera contenedores y composes. Generacion: `generate_dockerfile_go_infra` del grupo `deploy`.
|
||||
- **NO maneja secretos Docker Swarm**. Pasa secrets via env vars o files montados via `--volume`.
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
# Extractor — Funciones que leen datos de fuentes externas
|
||||
|
||||
Tag: `extractor`. Grupo de funciones que **leen datos crudos** de fuentes externas (DB, API, archivos, web) y los devuelven a la aplicacion. Son el primer nodo del flujo de datos en `data_factory` (issue 0097, analogia Factorio = drills).
|
||||
|
||||
Filtro MCP: `mcp__registry__fn_search query="" tag="extractor"`.
|
||||
|
||||
## Funciones del grupo
|
||||
|
||||
| ID | Lang | Fuente |
|
||||
|---|---|---|
|
||||
| [bq_query_py_infra](../../python/functions/infra/bq_query.md) | py | BigQuery SQL |
|
||||
| [bq_preview_rows_py_infra](../../python/functions/infra/bq_preview_rows.md) | py | BigQuery table preview |
|
||||
| [bq_get_table_py_infra](../../python/functions/infra/bq_get_table.md) | py | BigQuery metadata |
|
||||
| [bq_list_tables_py_infra](../../python/functions/infra/bq_list_tables.md) | py | BigQuery catalog |
|
||||
| [metabase_execute_card_py_infra](../../python/functions/infra/metabase_execute_card.md) | py | Metabase card |
|
||||
| [metabase_execute_query_py_infra](../../python/functions/infra/metabase_execute_query.md) | py | Metabase ad-hoc |
|
||||
| [load_csv_go_datascience](../../functions/datascience/load_csv.md) | go | CSV file |
|
||||
| [load_parquet_go_datascience](../../functions/datascience/load_parquet.md) | go | Parquet file |
|
||||
| [from_csv_py_core](../../python/functions/core/from_csv.md) | py | CSV file |
|
||||
| [fetch_data_frame_go_datascience](../../functions/datascience/fetch_data_frame.md) | go | Generic dataframe loader |
|
||||
| [http_get_json_py_infra](../../python/functions/infra/http_get_json.md) | py | HTTP JSON GET |
|
||||
| [http_get_json_go_infra](../../functions/infra/http_get_json.md) | go | HTTP JSON GET |
|
||||
| [http_download_file_py_infra](../../python/functions/infra/http_download_file.md) | py | HTTP file download |
|
||||
| [http_download_file_go_infra](../../functions/infra/http_download_file.md) | go | HTTP file download |
|
||||
| [jupyter_read_py_notebook](../../python/functions/notebook/jupyter_read.md) | py | Jupyter notebook cells |
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
Extractor BigQuery -> dataframe local.
|
||||
|
||||
```python
|
||||
from infra import bq_query
|
||||
|
||||
# 1. Extraer
|
||||
df = bq_query(
|
||||
project_id="my-gcp-project",
|
||||
query="SELECT * FROM `dataset.events` WHERE event_date = CURRENT_DATE() LIMIT 10000",
|
||||
)
|
||||
|
||||
print(f"extracted {len(df)} rows, {df.memory_usage(deep=True).sum() / 1024:.1f} KB")
|
||||
```
|
||||
|
||||
## Fronteras del grupo
|
||||
|
||||
NO cubre:
|
||||
- **Transform** (clean / dedup / aggregate) -> [[transformer]].
|
||||
- **Sink** (escritura a destino externo) -> [[sink]].
|
||||
- **Validacion** del dato (range / null / drift) -> [[validator]].
|
||||
- Extraccion de funciones desde repos externos (eso es `sources/`, no este grupo).
|
||||
- Streaming continuo (Kafka consumer, etc.) — los extractores son fetch puntual o batch.
|
||||
|
||||
## Cuando NO usar `extractor`
|
||||
|
||||
- Si la funcion lee de la BD interna del registry (`registry.db`, `operations.db`) -> tag `registry` o `doctor`, no `extractor`. Extractor implica fuente externa.
|
||||
- Si solo parsea bytes ya cargados en memoria -> es `transformer`.
|
||||
|
||||
## Consumidores
|
||||
|
||||
- `data_factory` C++ ImGui app — tab Extractors lista el grupo entero y permite Run Now por nodo.
|
||||
- `dag_engine` — un DAG step puede llamar a un extractor via `function: <id>`.
|
||||
- Pipelines ad-hoc en notebooks.
|
||||
|
||||
## Notas
|
||||
|
||||
- BQ extractors estan tambien tageados `bigquery` (otro grupo); Metabase extractors tageados `metabase`. Una funcion puede pertenecer a multiples grupos.
|
||||
- HTTP extractors duplicados Go/Py — eleccion segun stack del consumidor.
|
||||
- Crece a medida que el registry incorpora nuevas fuentes (Mongo, Kafka, S3...). Mantener tag al frontmatter al crear funcion nueva.
|
||||
@@ -0,0 +1,112 @@
|
||||
# Capability: mantine
|
||||
|
||||
Componentes y helpers Mantine v9 + `@fn_library` (alias a `frontend/functions/ui/`). Cubre: inputs (TextInput, NumberInput, Select, MultiSelect, ColorPicker), layout (Group, Stack, AppShell, Grid), feedback (Modal, Drawer, Notification, Loader), data display (Table, Card, Badge), navegacion (Tabs, Accordion, Menu), instalacion+theming (`install_mantine`, `frontend_doctor`). Tambien componentes ImGui equivalentes en `@fn_library` C++ (cpp/core).
|
||||
|
||||
## Funciones
|
||||
|
||||
| ID | Firma | Que hace |
|
||||
|---|---|---|
|
||||
| `accordion_ts_ui` | `Accordion(props: AccordionProps): JSX.Element` | Secciones colapsables con animaciones. Mantine Accordion. Composable: AccordionItem + AccordionTrigger + AccordionContent. |
|
||||
| `action_icon_ts_ui` | `FnActionIcon(props: FnActionIconProps): JSX.Element` | Boton de icono con variantes, loading y tooltip opcional. Wrapper sobre Mantine ActionIcon. |
|
||||
| `alert_ts_ui` | `Alert(props: { variant?: 'default' \| 'destructive' \| 'success' \| 'warning' \| 'info' }): JSX.Element` | Alerta accesible con 5 variantes semánticas (default, destructive, success, warning, info). Mantine Alert con slots para título, descripción y acción. |
|
||||
| `app_shell_ts_ui` | `FnAppShell(props: FnAppShellProps): JSX.Element` | Layout shell con header, navbar colapsable y area principal. Wrapper sobre Mantine AppShell. |
|
||||
| `area_chart_ts_ui` | `AreaChart(props: AreaChartProps): JSX.Element` | Gráfico de área @mantine/charts con gradientes automáticos, multi-series, stacking y tooltips. |
|
||||
| `auth_form_ts_ui` | `AuthForm(config: AuthFormConfig): ReactElement` | Genera página de autenticación con toggle login/register, social buttons opcionales, campos extra en registro y validación. Basado en Mantine AuthenticationForm. |
|
||||
| `autocomplete_ts_ui` | `Autocomplete(props: AutocompleteProps): JSX.Element` | Input con sugerencias de autocompletado. Permite valores libres a diferencia de Select. Wrapper sobre Mantine Autocomplete. |
|
||||
| `avatar_ts_ui` | `Avatar(props: AvatarProps): JSX.Element` | Imagen de usuario circular con fallback a iniciales generadas automaticamente. 5 tamaños via Mantine Avatar. |
|
||||
| `badge_ts_ui` | `Badge(props: BadgeProps & VariantProps<typeof badgeVariants>): JSX.Element` | Badge con 10 variantes semánticas (default, secondary, destructive, outline, ghost, link, success, warning, error, info) y 2 tamaños. Mantine Badge. |
|
||||
| `bar_chart_ts_ui` | `BarChart(props: BarChartProps): JSX.Element` | Gráfico de barras @mantine/charts con multi-series, orientación horizontal/vertical y tooltips. |
|
||||
| `breadcrumb_ts_ui` | `Breadcrumb(props: BreadcrumbProps): JSX.Element` | Navegacion jerarquica con separadores, elipsis para paths largos y soporte para router links via asChild. Mantine Anchor/Text. |
|
||||
| `button_ts_ui` | `Button(props: ButtonProps & VariantProps<typeof buttonVariants>): JSX.Element` | Botón accesible con 6 variantes (default, outline, secondary, ghost, destructive, link) y 8 tamaños. Mantine Button. |
|
||||
| `chart_container_ts_ui` | `ChartContainer(props: { children: ReactNode; height?: number \| string }): JSX.Element` | Thin wrapper Paper y utilidades de colores/series para los charts @mantine/charts. |
|
||||
| `checkbox_ts_ui` | `Checkbox(props: CheckboxProps): JSX.Element` | Input booleano accesible con label opcional y variante indeterminate. Mantine Checkbox. |
|
||||
| `chip_ts_ui` | `Chip(props: ChipProps): JSX.Element` | Chip seleccionable con variantes filled/outline/light. ChipGroup para selección simple o múltiple. Wrapper sobre Mantine Chip. |
|
||||
| `color_bg_ts_ui` | `colorBg(color: string): string` | Genera el valor CSS color-mix para el fondo de un elemento con color Mantine. Sin color retorna dark-6. Hex: mezcla 18% con dark-6. Token Mantine (blue, red, ...): mezcla tono -9 al 18% con dark-6. |
|
||||
| `color_border_ts_ui` | `colorBorder(color: string): string` | Genera el valor CSS color-mix para el borde de un elemento con color Mantine. Sin color retorna dark-4. Hex: mezcla 30% con dark-4. Token Mantine: mezcla tono -7 al 30% con dark-4. |
|
||||
| `color_input_ts_ui` | `ColorInput(props: ColorInputProps): JSX.Element` | Selector de color con picker, swatches predefinidos y eye dropper. Soporta hex, rgb, hsl con alpha. Wrapper sobre Mantine ColorInput. |
|
||||
| `color_picker_grid_ts_ui` | `ColorPickerGrid(props: ColorPickerGridProps): JSX.Element` | Grid de swatches de color con boton extra que abre un modal con ColorPicker de Mantine para seleccionar un hexadecimal libre. Soporta tokens Mantine y valores hex. El swatch activo recibe borde blanco + box-shadow azul; el custom-active aplica el mismo feedback visual. |
|
||||
| `color_swatch_ts_ui` | `colorSwatch(color: string): string` | Genera el valor CSS para mostrar un swatch (muestra de color) Mantine. Sin color retorna dark-3. Hex: retorna el hex directamente. Token Mantine: retorna CSS var del tono -7. |
|
||||
| `date_picker_input_ts_ui` | `DatePickerInput(props: DatePickerInputProps): JSX.Element` | Selector de fecha con input y calendario desplegable. Soporta fecha simple, múltiple y rango. Wrapper sobre Mantine DatePickerInput. |
|
||||
| `dialog_ts_ui` | `Dialog(props: DialogRootProps): JSX.Element` | Diálogo modal accesible con close button y sistema de slots (header, footer, title, description). Mantine Modal. |
|
||||
| `dropdown_menu_ts_ui` | `DropdownMenu(props: DropdownMenuProps): JSX.Element` | Menu de acciones y contexto accesible con items, checkboxes, radios, separadores y submenus. Base-UI Menu primitive. |
|
||||
| `dropzone_ts_ui` | `Dropzone(props: DropzoneProps): JSX.Element` | Zona de drag-and-drop para archivos con estados idle/accept/reject, límite de tamaño y tipos MIME. Wrapper sobre Mantine Dropzone. |
|
||||
| `empty_state_ts_ui` | `EmptyState(props: EmptyStateProps): JSX.Element` | Placeholder para listas y tablas vacías con icono, título, descripción y acción opcional. Tabler Icons por defecto. |
|
||||
| `error_page_ts_ui` | `ErrorPage(config: ErrorPageConfig): JSX.Element` | Genera página de error con código grande, título, descripción y acciones. Soporta 404, 500, 403 y cualquier código custom. |
|
||||
| `file_input_ts_ui` | `FileInput(props: FileInputProps): JSX.Element` | Input de archivos con soporte para múltiples archivos, tipos aceptados y botón de limpiar. Wrapper sobre Mantine FileInput. |
|
||||
| `frontend_doctor_bash_infra` | `frontend_doctor(project_dir: string) -> diagnostics_stdout` | Diagnostica la salud de un proyecto frontend Mantine. Verifica Node, React, Mantine, PostCSS, TypeScript, vite.config y detecta residuos de shadcn/@base-ui. Imprime tabla de checks con exit code 0/1. |
|
||||
| `indicator_ts_ui` | `FnIndicator(props: FnIndicatorProps): JSX.Element` | Badge indicador posicionado sobre un elemento hijo. Wrapper sobre Mantine Indicator. |
|
||||
| `input_ts_ui` | `Input(props: InputHTMLAttributes): JSX.Element` | Campo de entrada accesible con soporte para iconos, grupos, validación ARIA y estados disabled/invalid. Mantine TextInput. |
|
||||
| `install_mantine_bash_infra` | `install_mantine(project_dir: string) -> void` | Instala Mantine UI con todas sus dependencias (@mantine/core, hooks, charts, notifications, form) y PostCSS en un proyecto frontend. Detecta package manager por lockfile. Genera postcss.config.cjs si no existe. Idempotente. |
|
||||
| `label_ts_ui` | `Label(props: LabelHTMLAttributes): JSX.Element` | Etiqueta de formulario accesible con soporte para estados disabled. Mantine Text con component=label. |
|
||||
| `line_chart_ts_ui` | `LineChart(props: LineChartProps): JSX.Element` | Gráfico de líneas @mantine/charts con multi-series, 5 tipos de curva, líneas de referencia y tooltips. |
|
||||
| `loading_overlay_ts_ui` | `FnLoadingOverlay(props: FnLoadingOverlayProps): JSX.Element` | Overlay de carga con blur y opacidad configurable. Wrapper sobre Mantine LoadingOverlay. |
|
||||
| `mantine_provider_ts_ui` | `FnMantineProvider({ children, theme?, defaultColorScheme? })` | Provider raiz de Mantine para apps del registry. Wrappea MantineProvider con Notifications incluido. Importa los CSS de @mantine/core, charts y notifications. |
|
||||
| `month_heatmap_ts_ui` | `MonthHeatmap(props: MonthHeatmapProps): JSX.Element` | Grid mensual de calor (heatmap) con inicio en lunes. Renderiza 7 columnas con header de dias de semana y celdas que muestran numero del dia, hasta dos contadores con icono, borde azul para hoy y fondo tintado segun valores primary/secondary. |
|
||||
| `multi_select_ts_ui` | `MultiSelect(props: MultiSelectProps): JSX.Element` | Selector múltiple con búsqueda, pills y límite de selecciones. Wrapper sobre Mantine MultiSelect. |
|
||||
| `nav_link_ts_ui` | `FnNavLink(props: FnNavLinkProps): JSX.Element` | Link de navegacion con icono, descripcion y anidamiento. Wrapper sobre Mantine NavLink. |
|
||||
| `number_input_ts_ui` | `FnNumberInput(props: FnNumberInputProps): JSX.Element` | Input numerico con min/max, step, prefijo y sufijo. Wrapper sobre Mantine NumberInput. |
|
||||
| `pagination_ts_ui` | `Pagination(props: PaginationProps): JSX.Element` | Controles de navegacion de paginas autocontenido. Mantine Pagination. |
|
||||
| `password_input_ts_ui` | `PasswordInput(props: PasswordInputProps): JSX.Element` | Input de contraseña con toggle de visibilidad y soporte para indicador de fortaleza. Wrapper sobre Mantine PasswordInput. |
|
||||
| `pie_chart_ts_ui` | `PieChart(props: PieChartProps): JSX.Element` | Gráfico de torta/dona @mantine/charts con colores automáticos, labels y tooltip. Usa DonutChart para dona, PieChart para torta. |
|
||||
| `pin_input_ts_ui` | `PinInput(props: PinInputProps): JSX.Element` | Input de código PIN/OTP con campos individuales y autocompletado. Wrapper sobre Mantine PinInput. |
|
||||
| `popover_ts_ui` | `Popover(props: PopoverProps): JSX.Element` | Contenido flotante posicionado accesible con animaciones. Mantine Popover. |
|
||||
| `progress_bar_ts_ui` | `ProgressBar(props: ProgressBarProps): JSX.Element` | Barra de progreso con variantes de color y tamaño, buffer, animación, modo indeterminado y display de valor. Mantine Progress. |
|
||||
| `radio_group_ts_ui` | `RadioGroup(props: RadioGroupProps): JSX.Element` | Grupo de opciones exclusivas accesible. Mantine Radio.Group + Radio. |
|
||||
| `rating_ts_ui` | `Rating(props: RatingProps): JSX.Element` | Selector de calificación con estrellas, fracciones y símbolos custom. Wrapper sobre Mantine Rating. |
|
||||
| `ring_progress_ts_ui` | `FnRingProgress(props: FnRingProgressProps): JSX.Element` | Anillo de progreso con secciones coloreadas y label central. Wrapper sobre Mantine RingProgress. |
|
||||
| `segmented_control_ts_ui` | `FnSegmentedControl(props: FnSegmentedControlProps): JSX.Element` | Control segmentado para seleccion unica entre opciones. Wrapper sobre Mantine SegmentedControl. |
|
||||
| `select_ts_ui` | `Select(props: SelectProps): JSX.Element` | Select dropdown con búsqueda, grupos y accesibilidad. Wrapper sobre Mantine Select con API declarativa via prop data. |
|
||||
| `sheet_ts_ui` | `Sheet(props: SheetProps): JSX.Element` | Panel lateral deslizante (drawer) accesible con variantes de lado y animaciones. Mantine Drawer. |
|
||||
| `skeleton_ts_ui` | `Skeleton(props: HTMLAttributes<HTMLDivElement>): JSX.Element` | Sistema de loading skeletons: base, text, card, avatar, button, table. Variantes preconfiguradas para estados de carga. Mantine Skeleton. |
|
||||
| `slider_cpp_core` | `bool slider_float(const char* label, float* v, float min, float max, const char* fmt); bool slider_float_log(...); bool slider_int(const char* label, int* v, int min, int max, const char* fmt); bool slider_double(const char* label, double* v, double min, double max, const char* fmt)` | Slider ImGui con label muted arriba, estilo acorde con fn_tokens (radius, border, primary grab). Variantes float, float_log (logaritmico), int, double. Equivalente al <Slider> de Mantine / fn_library. |
|
||||
| `slider_ts_ui` | `Slider(props: SliderProps): JSX.Element \| RangeSlider(props: RangeSliderProps): JSX.Element` | Deslizador de valor numérico con marcas, labels y modo rango. Incluye RangeSlider. Wrapper sobre Mantine Slider. |
|
||||
| `stepper_ts_ui` | `FnStepper(props: FnStepperProps): JSX.Element` | Stepper de pasos con orientacion horizontal/vertical. Wrapper sobre Mantine Stepper. |
|
||||
| `sticker_picker_ts_ui` | `StickerPicker(props: StickerPickerProps): JSX.Element` | Selector de emoji/sticker encapsulado en un Popover de Mantine. Monta emoji-mart Picker una sola vez para evitar re-creaciones en cada render. |
|
||||
| `switch_toggle_ts_ui` | `SwitchToggle(props: SwitchToggleProps): JSX.Element` | Toggle on/off accesible con label opcional a izquierda o derecha. Mantine Switch. |
|
||||
| `tabs_ts_ui` | `Tabs(props: TabsRootProps): JSX.Element` | Sistema de tabs con orientacion horizontal/vertical, variantes default y line. Mantine Tabs. |
|
||||
| `tags_input_ts_ui` | `TagsInput(props: TagsInputProps): JSX.Element` | Input de tags libre con sugerencias opcionales. Permite crear valores custom a diferencia de MultiSelect. Wrapper sobre Mantine TagsInput. |
|
||||
| `textarea_ts_ui` | `Textarea(props: TextareaProps): JSX.Element` | Input multilinea accesible con auto-resize opcional. Mantine Textarea con autosize. |
|
||||
| `timeline_ts_ui` | `FnTimeline(props: FnTimelineProps): JSX.Element` | Timeline vertical con items, iconos y colores. Wrapper sobre Mantine Timeline. |
|
||||
| `toast_ts_ui` | `Toast(props: ToastProps): JSX.Element` | Notificaciones temporales con variantes semanticas (success, error, warning, info), iconos automaticos, auto-dismiss y provider con hook useToast. |
|
||||
| `tooltip_ts_ui` | `Tooltip(props: TooltipRootProps): JSX.Element` | Tooltip accesible con posicionamiento automático. Mantine Tooltip con delay configurable. |
|
||||
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
### Frontend nuevo desde cero
|
||||
|
||||
```bash
|
||||
./fn run install_mantine --target frontend
|
||||
./fn doctor # verifica que pnpm + node + tsx esten OK (grupo registry)
|
||||
cd frontend && pnpm dev
|
||||
```
|
||||
|
||||
### Pagina con @fn_library
|
||||
|
||||
```tsx
|
||||
import { FnMantineProvider, FnButton, FnTextInput, FnModal } from "@fn_library";
|
||||
import { Stack, Group } from "@mantine/core";
|
||||
import { IconHome } from "@tabler/icons-react";
|
||||
|
||||
export function App() {
|
||||
return (
|
||||
<FnMantineProvider>
|
||||
<Stack p="md">
|
||||
<Group>
|
||||
<IconHome />
|
||||
<FnTextInput label="Nombre" placeholder="..." />
|
||||
</Group>
|
||||
<FnButton variant="filled" leftSection={<IconHome />}>Submit</FnButton>
|
||||
</Stack>
|
||||
</FnMantineProvider>
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## Fronteras
|
||||
|
||||
- **NO usa Tailwind, CVA, cn(), CSS variables custom**. Solo Mantine theme system + props.
|
||||
- **NO usa lucide-react ni heroicons**. Solo `@tabler/icons-react` (set nativo de Mantine).
|
||||
- **NO crea componentes HTML nativos cuando hay equivalente en @fn_library**. Antes de `<button>`, usa `FnButton`. Antes de `<input>`, usa `FnTextInput`.
|
||||
- **NO usa MUI, Chakra, Ant Design u otro framework**. Stack es Mantine v9 + @fn_library exclusivamente.
|
||||
- Componentes ImGui (`button_cpp_core`, `modal_dialog_cpp_core`, ...) viven en `@fn_library` C++ y son el equivalente para apps nativas; comparten naming pero **son codigo separado**, no convierten entre sí.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Capability: metabase
|
||||
|
||||
_(Descripcion del grupo — editar a mano)_
|
||||
Operar Metabase 100% via API REST. Cubre: auth (`metabase_auth`), CRUD de cards/dashboards/collections/snippets/permissions/databases, ejecucion de queries (`metabase_execute_card`, `metabase_query`), refresh metadata + result_metadata, listado y archivado, gestion de pulses, y composiciones (`init_metabase`, `setup_metabase_volume`). 106 funciones Go+Py. Cliente reutilizable: `MetabaseClient` (Go: `metabase_client_go_infra`; Py: `MetabaseClient_py_infra`).
|
||||
|
||||
## Funciones
|
||||
|
||||
@@ -116,8 +116,44 @@ _(Descripcion del grupo — editar a mano)_
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
_(Anadir 1-2 bloques de codigo end-to-end)_
|
||||
### Auth + leer un dashboard (Python)
|
||||
|
||||
```python
|
||||
import os, sys
|
||||
sys.path.insert(0, os.path.join(os.environ["FN_REGISTRY_ROOT"], "python", "functions"))
|
||||
from metabase import MetabaseClient, metabase_get_dashboard, metabase_list_cards
|
||||
|
||||
client = MetabaseClient(
|
||||
base_url=os.environ["METABASE_URL"],
|
||||
username=os.environ["METABASE_USER"],
|
||||
password=os.environ["METABASE_PASS"],
|
||||
)
|
||||
client.auth()
|
||||
|
||||
dash = metabase_get_dashboard(client, dashboard_id=42)
|
||||
cards = metabase_list_cards(client, collection_id=dash["collection_id"])
|
||||
```
|
||||
|
||||
### Crear card + dashboard + ejecutar (Go)
|
||||
|
||||
```bash
|
||||
./fn run metabase_auth --base-url $METABASE_URL --user admin --pass $MB_PASS
|
||||
./fn run metabase_create_card --name "Sales 30d" --sql "SELECT ..." --database-id 1
|
||||
./fn run metabase_create_dashboard --name "Sales overview" --collection-id 5
|
||||
./fn run metabase_execute_card --card-id 123 | jq .
|
||||
```
|
||||
|
||||
### Setup local con Docker
|
||||
|
||||
```bash
|
||||
./fn run setup_metabase_volume
|
||||
./fn run init_metabase --project fn_registry
|
||||
```
|
||||
|
||||
## Fronteras
|
||||
|
||||
_(Que NO cubre este grupo)_
|
||||
- **NO instala Metabase**. El binario/Docker container debe existir. Solo cliente API + composiciones.
|
||||
- **NO usa el cliente Java/Python oficial**. Llama API REST directa via HTTP — mas simple, sin deps pesadas.
|
||||
- **NO escribe SQL**. La query la pasa el caller. Para construir SQL custom, usar funciones del grupo `sql`.
|
||||
- **NO maneja Metabase Embedded JS SDK** (frontend embed). Solo backend/API.
|
||||
- Wrappear cada endpoint nuevo via `fn-constructor` con tag `metabase`. NO hacer `client._http.request(...)` raw.
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
# navegator — Automatización de browser via CDP + AX tree + LLM
|
||||
|
||||
Grupo de funciones para inspeccionar y automatizar Chrome/Chromium via el protocolo CDP (Chrome DevTools Protocol), procesar árboles de accesibilidad (AX tree) y orquestar llamadas a LLMs desde scripts Python.
|
||||
|
||||
> Nota: Este grupo crece con issue 0098. Las funciones actuales cubren obtención y procesamiento del AX tree. Las siguientes iteraciones añadirán interacción (click, type, navigate) y pipelines completos de agente web.
|
||||
|
||||
## Funciones
|
||||
|
||||
| ID | Firma corta | Qué hace |
|
||||
|---|---|---|
|
||||
| `claude_cli_prompt_py_infra` | `claude_cli_prompt(prompt, timeout_s, model, max_chars_response, extra_args) -> str` | Invoca `claude -p` via subprocess y devuelve la respuesta como string |
|
||||
| `trim_ax_tree_py_core` | `trim_ax_tree(nodes) -> list[dict]` | Compacta AXNode CDP descartando nodos irrelevantes (ignored, generic vacíos, StaticText vacíos) |
|
||||
| `chunk_ax_tree_py_core` | `chunk_ax_tree(nodes, max_chars) -> list[list[dict]]` | Divide AX tree en chunks con path-from-root como contexto para LLMs |
|
||||
| `cdp_get_ax_tree_py_pipelines` | `cdp_get_ax_tree(debug_port, tab_id, depth) -> list[dict]` | Obtiene AX tree completo de un tab Chrome via CDP WebSocket |
|
||||
|
||||
## Ejemplo canónico end-to-end
|
||||
|
||||
```python
|
||||
import json, urllib.request
|
||||
from pipelines.cdp_get_ax_tree import cdp_get_ax_tree
|
||||
from core.trim_ax_tree import trim_ax_tree
|
||||
from core.chunk_ax_tree import chunk_ax_tree
|
||||
from infra.claude_cli_prompt import claude_cli_prompt
|
||||
|
||||
# 1. Chrome debe correr con --remote-debugging-port=9222
|
||||
# Listar tabs
|
||||
with urllib.request.urlopen("http://127.0.0.1:9222/json/list") as r:
|
||||
tabs = json.loads(r.read())
|
||||
tab_id = tabs[0]["id"]
|
||||
|
||||
# 2. Obtener, limpiar y chunkear el AX tree
|
||||
nodes = cdp_get_ax_tree(debug_port=9222, tab_id=tab_id)
|
||||
trimmed = trim_ax_tree(nodes)
|
||||
chunks = chunk_ax_tree(trimmed, max_chars=25000)
|
||||
|
||||
# 3. Procesar cada chunk con Claude
|
||||
for i, chunk in enumerate(chunks):
|
||||
chunk_json = json.dumps(chunk, ensure_ascii=False)
|
||||
prompt = f"Extrae todos los botones y enlaces interactivos de este AX tree:\n{chunk_json}"
|
||||
respuesta = claude_cli_prompt(prompt, timeout_s=90)
|
||||
print(f"--- Chunk {i} ---")
|
||||
print(respuesta)
|
||||
```
|
||||
|
||||
## Fronteras
|
||||
|
||||
Este grupo NO cubre:
|
||||
- Interacción directa con el DOM via CDP (clicks, typing, navegación) — ver funciones `cdp_*_go_browser` en Go.
|
||||
- Automatización Playwright — ver grupo `playwright`.
|
||||
- Captura de screenshots, HAR recording — ver `cdp_screenshot_go_browser`, `cdp_har_record_go_browser`.
|
||||
- Análisis NLP de texto extraído — ver grupo `nlp`.
|
||||
|
||||
## Prerequisitos
|
||||
|
||||
- Chrome/Chromium lanzado con `--remote-debugging-port=<port>`.
|
||||
- En WSL2: usar `chrome.exe` Windows con `--remote-debugging-address=0.0.0.0`.
|
||||
- `websocket-client` en el venv Python (`pip install websocket-client`).
|
||||
- `claude` CLI instalado y autenticado para `claude_cli_prompt`.
|
||||
@@ -1,6 +1,6 @@
|
||||
# Capability: nlp
|
||||
|
||||
_(Descripcion del grupo — editar a mano)_
|
||||
Pipeline NLP para extraccion de entities/relations sobre documentos en castellano (proyecto OSINT principalmente). Cubre: lectura de PDFs (`extract_pdf_text`, `clean_pdf_text`), OCR fallback, chunking (`chunk_with_overlap`), inferencia GLiNER (NER) + GLiREL (relation extraction), dedup (`dedup_entities`, `dedup_relations`), agregacion (`aggregate_extraction_results`), extraccion de elementos especificos (URLs, crypto wallets, IPs, dominios).
|
||||
|
||||
## Funciones
|
||||
|
||||
@@ -43,8 +43,37 @@ _(Descripcion del grupo — editar a mano)_
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
_(Anadir 1-2 bloques de codigo end-to-end)_
|
||||
### Pipeline completo PDF -> entities + relations
|
||||
|
||||
```python
|
||||
import os, sys
|
||||
sys.path.insert(0, os.path.join(os.environ["FN_REGISTRY_ROOT"], "python", "functions"))
|
||||
|
||||
from core import extract_pdf_text, clean_pdf_text, chunk_with_overlap
|
||||
from datascience import (
|
||||
gliner_extract_entities, glirel_extract_relations,
|
||||
dedup_entities, dedup_relations, aggregate_extraction_results,
|
||||
)
|
||||
|
||||
raw = extract_pdf_text("/path/to/doc.pdf")
|
||||
text = clean_pdf_text(raw)
|
||||
chunks = chunk_with_overlap(text, size=512, overlap=64)
|
||||
|
||||
entities = []
|
||||
relations = []
|
||||
for ch in chunks:
|
||||
entities.extend(gliner_extract_entities(ch, labels=["PERSON","ORG","ACCOUNT"]))
|
||||
relations.extend(glirel_extract_relations(ch, entity_pairs=entities))
|
||||
|
||||
result = aggregate_extraction_results(
|
||||
entities=dedup_entities(entities),
|
||||
relations=dedup_relations(relations),
|
||||
)
|
||||
```
|
||||
|
||||
## Fronteras
|
||||
|
||||
_(Que NO cubre este grupo)_
|
||||
- **NO entrena modelos**. Solo inferencia con GLiNER/GLiREL pre-entrenados (HuggingFace).
|
||||
- **NO maneja embeddings densos** (sentence-transformers / e5). Para vectores, usa funciones del grupo `ml`.
|
||||
- **NO hace traduccion ni summarization LLM**. Solo NER + RE. Para LLM, ver tag `llm`.
|
||||
- **NO escribe a BD** automaticamente. La persistencia (vault, sqlite, parquet) la maneja el caller via funciones de `infra`/`datascience`.
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
# Capability: registry
|
||||
|
||||
Auditoria y monitorizacion del propio registry (issue 0085/0086/0087). Cubre: detectar codigo copiado sin import (`audit_copied_code`), funciones huerfanas (`find_unused_functions`), drift entre imports y `uses_functions` declarado, drift `pc_locations` BD vs disco, drift de capability groups (`audit_capability_groups`), telemetria de llamadas (call_monitor `function_stats`), y generacion automatica de proposals (`generate_proposals_from_telemetry`).
|
||||
|
||||
## Funciones
|
||||
|
||||
| ID | Firma | Que hace |
|
||||
|---|---|---|
|
||||
| `audit_copied_code_go_infra` | `func AuditCopiedCode(registryRoot string) ([]CopiedCodeEntry, error)` | Audita apps en busca de cuerpos de funcion copiados del registry sin import. Calcula fingerprint normalizado (strip comments + whitespace) por funcion del registry y por funcion declarada en apps/, projects/*/apps/. Reporta matches exactos. MVP: exact_copy con sha256 truncado. Issue 0085k. |
|
||||
| `audit_registry_paths_bash_pipelines` | `audit_registry_paths([output_file: string]) -> void` | Audita file_path de todas las functions y types en registry.db, verifica que cada ruta apunte a un archivo existente en disco, y genera un txt con las rutas rotas para que agentes puedan corregirlas. |
|
||||
| `find_unused_functions_go_infra` | `func FindUnusedFunctions(registryRoot string) ([]UnusedFunction, error)` | Abre registry.db y retorna todas las funciones que no son referenciadas por ninguna otra funcion, app ni analisis. Util para detectar candidatas a deprecar o eliminar (fn doctor unused). |
|
||||
| `full_git_pull_bash_pipelines` | `full_git_pull() -> stdout: tabla resumen` | Pull automatico de fn_registry + todos los sub-repos locales + submodules + fn sync. Descubre repos locales, stashea dirty trees antes de pullear, hace pull --ff-only, actualiza submodulos del repo principal, pulla ~/.password-store, regenera registry.db con fn index y ejecuta fn sync. |
|
||||
| `full_git_push_bash_pipelines` | `full_git_push(commit_message?: string) -> stdout: tabla resumen` | Push automatico de fn_registry + todos los sub-repos + fn sync. Descubre repos, escanea secrets (aborta si detecta), auto-inicializa apps/analyses sin .git via ensure_repo_synced, auto-commitea dirty trees, pushea solo repos adelantados, pushea ~/.password-store sin commitear, y ejecuta fn sync. |
|
||||
| `generate_proposals_from_telemetry_go_infra` | `func GenerateProposalsFromTelemetry(registryRoot string) ([]ProposalDraft, error)` | Genera proposals automaticas para registry.db.proposals a partir de telemetria de call_monitor.operations.db. Aplica 4 reglas MVP: copy_detected, orphan, bug, wrapper_skip. IDs deterministas (sha1 truncado) garantizan idempotencia. Cierra la fase MEJORAR del bucle reactivo. Issue 0085h. |
|
||||
| `parser_registry_py_core` | `class ParserRegistry: register(name: str, parser: BaseParser) -> None; unregister(name: str) -> None; get_parser(name: str) -> BaseParser \| None; get_parser_for_file(path: str) -> BaseParser \| None; async parse(source: str, **kwargs) -> ParseResult; list_parsers() -> list[str]; list_supported_extensions() -> list[str]` | Registry extensible que despacha parsing de archivos al parser correcto basado en extension. Patron plugin: registrar parsers por nombre y extensiones, resolver automaticamente. Mantiene estado mutable (mapa extension→parser). Singleton global disponible via get_registry(). |
|
||||
| `proposal_from_failure_go_infra` | `func ProposalFromFailure(registryDB string, appID string, results []CheckResult, executionID string) ([]string, error)` | Crea una fila en la tabla proposals de registry.db por cada CheckResult con Status=fail. Usa kind=new_function para fallos criticos y kind=improve_function para warnings. Retorna los IDs de proposals creados. Parte del bucle reactivo: conecta los resultados de e2e_run_checks con la etapa MEJORAR. |
|
||||
| `registry_telemetry_py_infra` | `install() -> bool` | Telemetria de invocaciones del registry desde Python. Patchea via sys.meta_path los paquetes del registry (core, finance, metabase, etc.) y registra cada llamada en call_monitor.operations.db. Activable con FN_TELEMETRY=1. Issue 0085c. |
|
||||
| `setup_registry_api_bash_infra` | `setup_registry_api(ssh_host: string, api_token: string, basic_auth_user: string, basic_auth_pass: string) -> json` | Deploy completo de registry_api en VPS con Docker + Traefik (Coolify proxy). Sincroniza el repo via rsync, genera el hash bcrypt para basicAuth, sube el traefik-dynamic.yml, crea el .env con el token, hace docker compose build+up y verifica el health check. |
|
||||
| `sql_workbench_cpp_core` | `void fn::sql_workbench(const char* id, sqlite3* db, fn::SqlWorkbenchState& state, ImVec2 size); bool fn::sql_workbench_run_query(sqlite3*, const char*, fn::SqlWorkbenchState&); void fn::sql_workbench_load_schema(sqlite3*, fn::SqlWorkbenchState&); void fn::sql_workbench_destroy(fn::SqlWorkbenchState&)` | Workbench SQL embebido en ImGui: editor con highlighting (text_editor + CodeLang::SQL), tabla de resultados (table_view), sidebar de schema (sqlite_master) e historial. Ejecuta queries contra una sqlite3* del caller (no abre/cierra la DB). |
|
||||
| `tag_unused_pending_py_pipelines` | `python tag_unused_pending.py` | Etiqueta cada funcion del registry sin consumidor con el tag `pendiente-usar` (issue 0062). Lee `fn doctor unused --json`, modifica el frontmatter YAML del .md de cada funcion, y deja la indicacion para correr `fn index`. Idempotente: tambien retira el tag de funciones que han vuelto a usarse. |
|
||||
| `telemetry_prelude_bash_infra` | `source telemetry_prelude.sh` | Prelude bash que envuelve cada funcion del registry definida en el shell con un wrapper que mide duration y registra cada llamada en call_monitor.operations.db. Activable con FN_TELEMETRY=1. Issue 0085c. |
|
||||
| `validate_registry_paths_bash_shell` | `validate_registry_paths(db_path: string, table: string, root_dir: string) -> tsv_stdout` | Consulta registry.db y verifica que cada file_path apunte a un archivo existente en disco. Imprime a stdout las rutas rotas en formato TSV (id, file_path, domain, tabla). |
|
||||
| `vault_aggregate_index_go_infra` | `func VaultAggregateIndex(repoRoot string) (AggregateReport, error)` | Agrega los índices de todos los vaults del registry en la tabla vault_files de registry.db. Lee cada vault_index.db (via VaultIndexOpen) y reemplaza las filas de forma atómica. Idempotente: re-ejecutar limpia y reescribe sin duplicar. |
|
||||
| `write_analysis_md_bash_infra` | `write_analysis_md(analysis_dir: string, name: string, description: string, tags_csv: string) -> string` | Genera un archivo analysis.md con frontmatter valido para el registry. Calcula dir_path relativo a FN_REGISTRY_ROOT (o lo deduce buscando registry.db hacia arriba). Acepta tags como CSV. |
|
||||
| `write_jupyter_registry_kernel_bash_infra` | `write_jupyter_registry_kernel([project_dir: string]) -> string` | Genera un script de startup de IPython que autoconfigura FN_REGISTRY_ROOT, sys.path a python/functions del registry, y helpers fn_query/fn_search/fn_code para consultar registry.db desde notebooks. |
|
||||
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
### Auditoria completa pre-merge
|
||||
|
||||
```bash
|
||||
./fn doctor # todos los checks
|
||||
./fn doctor uses-functions --json | jq .
|
||||
./fn doctor unused --json | jq '.[] | select(.calls_90d == 0)'
|
||||
./fn doctor copied-code --json | jq '.[] | select(.kind == "exact_copy")'
|
||||
./fn doctor capabilities --json
|
||||
```
|
||||
|
||||
### Telemetria del agente (issue 0085)
|
||||
|
||||
```bash
|
||||
cd projects/fn_monitoring/apps/call_monitor
|
||||
|
||||
./call_monitor copied-code # popula tabla copied_code
|
||||
./call_monitor snapshot # snapshot a function_versions
|
||||
./call_monitor propose # crea proposals automaticas desde function_stats
|
||||
```
|
||||
|
||||
### Detectar candidatos a capability group
|
||||
|
||||
```bash
|
||||
./fn run propose_capability_groups --min-count 10 --max-domains 4
|
||||
# Promociona uno: ./fn run propose_capability_groups --apply mantine
|
||||
```
|
||||
|
||||
## Fronteras
|
||||
|
||||
- **NO modifica codigo del registry** automaticamente. Solo audita + abre proposals.
|
||||
- **NO sincroniza** entre PCs. Para eso usar `fn sync` (grupo `git` cubre push/pull).
|
||||
- **NO ejecuta tests**. Solo audita schema, hash, y telemetria.
|
||||
- Funciones runtime de apps compiladas (Go/C++): quedan **fuera** de la telemetria salvo via `./fn run` o e2e_checks. Ver issue 0085 "Que se escapa del monitor".
|
||||
@@ -0,0 +1,78 @@
|
||||
# Scheduler — Cron expression parsing, matching, scheduling
|
||||
|
||||
Tag: `scheduler`. Grupo de funciones para trabajar con expresiones cron: parsear, validar coincidencias, calcular proximo run, traducir a lenguaje humano. Lo consume `apps/dag_engine` + `cpp/apps/dag_engine_ui` (tab Schedule). Crece a medida que se anaden nuevos formatos (Quartz, k8s CronJob, etc.).
|
||||
|
||||
Filtro MCP: `mcp__registry__fn_search query="" tag="scheduler"`.
|
||||
|
||||
## Funciones del grupo
|
||||
|
||||
| ID | Firma corta | Que hace |
|
||||
|---|---|---|
|
||||
| [parse_cron_expr_go_core](../../functions/core/parse_cron_expr.md) | `ParseCronExpr(expr string) (CronSchedule, error)` | Parsea expresion cron de 5 campos a struct expandido. Soporta `*`, rangos, listas, pasos y aliases `@hourly/@daily/@weekly/@monthly/@yearly`. |
|
||||
| [cron_match_go_core](../../functions/core/cron_match.md) | `CronMatch(s CronSchedule, t time.Time) bool` | True si un instante coincide con un schedule. Compara los 5 campos. |
|
||||
| [next_cron_time_go_core](../../functions/core/next_cron_time.md) | `NextCronTime(s CronSchedule, after time.Time) time.Time` | Proxima ejecucion despues de un instante. Salta minuto a minuto. Zero time si no hay match en 366 dias. |
|
||||
| [cron_explain_go_core](../../functions/core/cron_explain.md) | `CronExplain(expr string) string` | Traduce expresion cron a frase humana corta (`"every 15 minutes"`, `"daily at 09:00"`, `"weekdays at HH:MM"`). |
|
||||
|
||||
## Ejemplo canonico end-to-end
|
||||
|
||||
Trabajar con un schedule completo: parsear, calcular proximo run, explicarlo al usuario.
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
"fn-registry/functions/core"
|
||||
)
|
||||
|
||||
func main() {
|
||||
expr := "*/15 * * * *"
|
||||
|
||||
// 1. Parsear
|
||||
sched, err := core.ParseCronExpr(expr)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
// 2. Proximo run
|
||||
next := core.NextCronTime(sched, time.Now())
|
||||
fmt.Printf("next run: %s\n", next.Format(time.RFC3339))
|
||||
|
||||
// 3. Explicacion humana
|
||||
fmt.Printf("schedule: %s (%s)\n", expr, core.CronExplain(expr))
|
||||
// -> schedule: */15 * * * * (every 15 minutes)
|
||||
|
||||
// 4. Verificacion puntual
|
||||
if core.CronMatch(sched, time.Now()) {
|
||||
fmt.Println("would trigger now")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Fronteras del grupo
|
||||
|
||||
NO cubre:
|
||||
- Persistencia de schedules (vive en BD de quien lo use: `apps/dag_engine/store/`).
|
||||
- Spawn/wait de procesos (eso es `process_*_go_infra`).
|
||||
- Sintaxis Quartz extendida con `?` o anos. Solo soporta los 5 campos estandar + aliases `@hourly/@daily/@weekly/@monthly/@yearly`.
|
||||
- Timezones — todo se evalua en `time.Local` del proceso. Si necesitas TZ explicita, parsea fuera y traduce.
|
||||
- Sintaxis especial de Kubernetes CronJob (segundos opcionales en el primer campo).
|
||||
- DAG orchestration / DAG runs (consume estas funciones via `apps/dag_engine`).
|
||||
|
||||
## Prerequisitos
|
||||
|
||||
- Go stdlib. Sin dependencias externas en ninguna funcion del grupo.
|
||||
- Tipo compartido `cron_schedule_go_core` (struct con `Minute/Hour/Day/Month/Weekday` como `[]int` expandidos).
|
||||
|
||||
## Notas
|
||||
|
||||
- `cron_match` esta tageada `pendiente-usar` — sin consumidores actuales aparte del propio paquete. Si se incorpora a `next_cron_time` como optimizacion -> quitar tag.
|
||||
- `cron_explain` mantiene contrato simple: si el patron no encaja en los reconocidos, devuelve el expr crudo sin error. Apto para UI fallback.
|
||||
- Para anadir nueva funcion al grupo: anadir tag `scheduler` al `tags:` del frontmatter, anadir fila a la tabla arriba, actualizar `N` en `docs/capabilities/INDEX.md`.
|
||||
|
||||
## Consumidores actuales
|
||||
|
||||
- `apps/dag_engine` — `parse_cron_expr_go_core`, `next_cron_time_go_core` (+ `cron_ticker_go_infra` adyacente).
|
||||
- `cpp/apps/dag_engine_ui` — tab Schedule consume `/api/dags` expuesto por dag_engine; via backend usa estas funciones.
|
||||
- Candidato futuro: `cron_explain_go_core` en `dag_engine` `/api/dags` (anadir campo `schedule_human` al JSON response) para que la UI no tenga que duplicar logica.
|
||||
@@ -0,0 +1,81 @@
|
||||
# Sink — Funciones que escriben datos a destinos externos
|
||||
|
||||
Tag: `sink`. Grupo de funciones que **escriben** datos a un destino externo: BD, archivo, dashboard, alerta, email. Input: datos. Output: efecto observable. Ultimo eslabon del flujo en `data_factory` (analogia Factorio = rocket silo).
|
||||
|
||||
Filtro MCP: `mcp__registry__fn_search query="" tag="sink"`.
|
||||
|
||||
## Funciones del grupo
|
||||
|
||||
| ID | Lang | Destino |
|
||||
|---|---|---|
|
||||
| [bq_insert_rows_py_infra](../../python/functions/infra/bq_insert_rows.md) | py | BigQuery streaming insert |
|
||||
| [bq_load_from_file_py_infra](../../python/functions/infra/bq_load_from_file.md) | py | BigQuery batch load (file) |
|
||||
| [bq_load_from_gcs_py_infra](../../python/functions/infra/bq_load_from_gcs.md) | py | BigQuery batch load (GCS) |
|
||||
| [bq_export_to_gcs_py_infra](../../python/functions/infra/bq_export_to_gcs.md) | py | BigQuery -> GCS export |
|
||||
| [metabase_create_card_py_infra](../../python/functions/infra/metabase_create_card.md) | py | Metabase saved question |
|
||||
| [metabase_export_card_py_infra](../../python/functions/infra/metabase_export_card.md) | py | Metabase card export (CSV/Excel) |
|
||||
| [metabase_create_dashboard_subscription_py_infra](../../python/functions/infra/metabase_create_dashboard_subscription.md) | py | Dashboard email subscription |
|
||||
| [metabase_create_card_alert_py_infra](../../python/functions/infra/metabase_create_card_alert.md) | py | Card alert (threshold/schedule) |
|
||||
| [http_post_json_py_infra](../../python/functions/infra/http_post_json.md) | py | HTTP JSON POST |
|
||||
| [http_post_json_go_infra](../../functions/infra/http_post_json.md) | go | HTTP JSON POST |
|
||||
| [db_insert_row_go_infra](../../functions/infra/db_insert_row.md) | go | SQL row insert |
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
Persistir resultados a BigQuery + crear card Metabase + alerta.
|
||||
|
||||
```python
|
||||
from infra import bq_insert_rows, metabase_create_card, metabase_create_card_alert
|
||||
|
||||
# 1. Insertar filas a BQ
|
||||
bq_insert_rows(
|
||||
project_id="my-gcp-project",
|
||||
dataset_id="analytics",
|
||||
table_id="daily_kpis",
|
||||
rows=results,
|
||||
)
|
||||
|
||||
# 2. Card en Metabase apuntando a esa tabla
|
||||
card = metabase_create_card(
|
||||
name="Daily KPIs",
|
||||
sql="SELECT * FROM `my-gcp-project.analytics.daily_kpis` WHERE date >= CURRENT_DATE() - 7",
|
||||
database_id=3,
|
||||
)
|
||||
|
||||
# 3. Alerta si la metrica clave cae
|
||||
metabase_create_card_alert(
|
||||
card_id=card["id"],
|
||||
threshold={"goal": 100, "above_goal": False},
|
||||
schedule="hourly",
|
||||
)
|
||||
```
|
||||
|
||||
## Fronteras del grupo
|
||||
|
||||
NO cubre:
|
||||
- **Extract** (leer de fuente externa) -> [[extractor]].
|
||||
- **Transform** (modificar datos sin efectos externos) -> [[transformer]].
|
||||
- Escritura a la BD interna del registry (`registry.db`, `operations.db`) — tag `registry` o equivalente.
|
||||
- Deploy de codigo (eso es `deploy`).
|
||||
- Logs / telemetria propia (van a `call_monitor.db`, no son sinks de pipeline).
|
||||
|
||||
## Cuando NO usar `sink`
|
||||
|
||||
- Si la funcion escribe pero los datos NO salen del proceso actual (cache en memoria) -> no es sink.
|
||||
- Si la funcion devuelve algo al caller sin efecto externo -> es transformer.
|
||||
|
||||
## Side effects observables
|
||||
|
||||
Por definicion, todos los sinks son **impuros** y tienen `error_type` definido. La salida del flujo data_factory es siempre visible: una fila nueva en BD, un email enviado, un dashboard actualizado.
|
||||
|
||||
## Consumidores
|
||||
|
||||
- `data_factory` — tab Sinks.
|
||||
- `dag_engine` DAG steps con `function: <sink_id>`.
|
||||
- Pipelines de produccion (con secrets de credenciales fuera del registry).
|
||||
|
||||
## Notas
|
||||
|
||||
- BQ load/insert/export todos como sink — escriben a recurso externo.
|
||||
- Metabase notifications (subscription + alert) son sinks "puros" en el sentido de que producen efectos sin cambiar datos.
|
||||
- HTTP POST cuenta como sink solo cuando se usa para **enviar** datos (webhook, API target), no para "obtener via POST".
|
||||
@@ -0,0 +1,58 @@
|
||||
# Capability: ssh
|
||||
|
||||
Operar hosts remotos via SSH. Cubre: CRUD de `~/.ssh/config` (`ssh_config_add_entry`, `ssh_config_find`, `ssh_config_remove`), conexiones reutilizables (`SSHConn`), ejecucion remota (`ssh_run`, `ssh_check`), transferencia (`scp_put`, `scp_get`), port-forwarding, y orquestacion de deploys (setup_vps_app, deploy_app_remote).
|
||||
|
||||
## Funciones
|
||||
|
||||
| ID | Firma | Que hace |
|
||||
|---|---|---|
|
||||
| `audit_ssh_config_bash_cybersecurity` | `audit_ssh_config(config_path: string) -> void` | Audita la configuración de sshd_config evaluando parámetros de seguridad críticos (PermitRootLogin, PasswordAuthentication, Port, MaxAuthTries, X11Forwarding, AllowUsers). También revisa intentos de login fallidos en los logs y lista las claves autorizadas del usuario actual. |
|
||||
| `docker_compose_remote_deploy_bash_infra` | `docker_compose_remote_deploy(host: string, remote_dir: string, branch: string, compose_files: string) -> json` | Despliega un stack Docker Compose en un host remoto via SSH. Verifica conectividad, hace git pull del branch indicado, actualiza imagenes con docker-compose pull y levanta/recrea los servicios modificados con docker-compose up -d. Soporta compose files adicionales. Retorna JSON con status, containers corriendo y duracion. |
|
||||
| `rsync_deploy_bash_infra` | `rsync_deploy(local_dir: string, ssh_alias: string, remote_dir: string) -> json` | Sincroniza un directorio local a un host remoto via rsync+SSH. Excluye archivos de desarrollo y bases de datos locales. Crea el directorio remoto si no existe. |
|
||||
| `setup_registry_api_bash_infra` | `setup_registry_api(ssh_host: string, api_token: string, basic_auth_user: string, basic_auth_pass: string) -> json` | Deploy completo de registry_api en VPS con Docker + Traefik (Coolify proxy). Sincroniza el repo via rsync, genera el hash bcrypt para basicAuth, sube el traefik-dynamic.yml, crea el .env con el token, hace docker compose build+up y verifica el health check. |
|
||||
| `setup_vps_app_go_infra` | `func SetupVPSApp(conn SSHConn, cfg DeployConfig) error` | Orquesta el setup inicial de una app en un VPS remoto: verifica SSH, crea dirs y usuario, sube binario, instala systemd unit y hace health check. |
|
||||
| `ssh_check_go_infra` | `func SSHCheck(conn SSHConn) error` | Verifica conectividad SSH ejecutando un comando noop en el host remoto. Timeout de 5 segundos. |
|
||||
| `ssh_config_add_entry_go_infra` | `func SSHConfigAddEntry(entries []SSHConfigEntry, entry SSHConfigEntry) ([]SSHConfigEntry, error)` | Añade un nuevo SSHConfigEntry a la lista. Error si el alias ya existe. |
|
||||
| `ssh_config_find_go_infra` | `func SSHConfigFind(entries []SSHConfigEntry, alias string) (SSHConfigEntry, bool)` | Busca un entry por alias en la lista de SSHConfigEntry. |
|
||||
| `ssh_config_parse_go_infra` | `func SSHConfigParse(content string) []SSHConfigEntry` | Parsea el contenido de un archivo ~/.ssh/config y retorna una lista de SSHConfigEntry. |
|
||||
| `ssh_config_read_go_infra` | `func SSHConfigRead() ([]SSHConfigEntry, error)` | Lee y parsea ~/.ssh/config. Retorna lista vacia si el archivo no existe. |
|
||||
| `ssh_config_remove_entry_go_infra` | `func SSHConfigRemoveEntry(entries []SSHConfigEntry, alias string) ([]SSHConfigEntry, error)` | Elimina un entry por alias de la lista. Error si el alias no existe. |
|
||||
| `ssh_config_render_go_infra` | `func SSHConfigRender(entries []SSHConfigEntry) string` | Convierte una lista de SSHConfigEntry al formato texto de ~/.ssh/config. |
|
||||
| `ssh_config_write_go_infra` | `func SSHConfigWrite(entries []SSHConfigEntry) error` | Escribe entries a ~/.ssh/config con backup automatico del archivo previo. |
|
||||
| `ssh_download_go_infra` | `func SSHDownload(conn SSHConn, remotePath, localPath string) error` | Descarga un archivo del host remoto al filesystem local via scp. |
|
||||
| `ssh_exec_go_infra` | `func SSHExec(conn SSHConn, command string) (string, string, int, error)` | Ejecuta un comando en el host remoto via SSH. Retorna stdout, stderr y exit code separados. |
|
||||
| `ssh_tunnel_close_go_infra` | `func SSHTunnelClose(pid int) error` | Cierra un tunel SSH enviando SIGTERM al proceso por PID. |
|
||||
| `ssh_tunnel_open_go_infra` | `func SSHTunnelOpen(conn SSHConn, localPort int, remoteHost string, remotePort int) (int, error)` | Abre un tunel SSH (local port forwarding) en background. Retorna el PID del proceso para cerrarlo despues. |
|
||||
| `ssh_upload_go_infra` | `func SSHUpload(conn SSHConn, localPath, remotePath string) error` | Sube un archivo local al host remoto via scp. |
|
||||
| `validate_git_ssh_uri_py_core` | `def validate_git_ssh_uri(url: str) -> None` | Valida el formato de una URI SSH de git (git@host:path). Lanza ValueError si la URI es invalida. |
|
||||
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
### Anadir host nuevo + comprobar + ejecutar comando
|
||||
|
||||
```bash
|
||||
./fn run ssh_config_add_entry \
|
||||
--alias organic-machine \
|
||||
--hostname 1.2.3.4 \
|
||||
--user deploy \
|
||||
--identity-file ~/.ssh/organic_ed25519
|
||||
|
||||
./fn run ssh_check --host organic-machine
|
||||
./fn run ssh_run --host organic-machine --cmd "systemctl status nginx"
|
||||
```
|
||||
|
||||
### Transferencia + deploy
|
||||
|
||||
```bash
|
||||
./fn run scp_put --host organic-machine --src ./build/myapp --dst /opt/apps/myapp/myapp
|
||||
./fn run systemd_restart --host organic-machine --unit myapp.service
|
||||
./fn run wait_for_http https://myapp.example.com/health 30
|
||||
```
|
||||
|
||||
## Fronteras
|
||||
|
||||
- **NO genera ni rota llaves SSH automaticamente**. Asume llave ya generada con `ssh-keygen`.
|
||||
- **NO valida fingerprints de host**. Confianza inicial es responsabilidad del operador (acepta el prompt manualmente o usa `StrictHostKeyChecking=no` con conciencia del riesgo).
|
||||
- **NO orquesta multi-hop / jump hosts** mas alla del ProxyJump del config. Para bastion compleja, edita `~/.ssh/config` a mano.
|
||||
- **NO maneja credenciales con password**. Solo auth por llave. Tunneling password-based via sshpass queda fuera (ver `cybersecurity/audit_ssh_config` para auditar).
|
||||
@@ -0,0 +1,68 @@
|
||||
# Capability: systemd
|
||||
|
||||
Gestionar units systemd local y remoto via SSH. Cubre: generar texto de unit files (`systemd_generate_unit`, pura), instalar+enable+start (`systemd_install`, `install_systemd_service`), restart/status (`systemd_restart`, `systemd_status`, `services_status`), y auditoria de apps con tag `service`.
|
||||
|
||||
## Funciones
|
||||
|
||||
| ID | Firma | Que hace |
|
||||
|---|---|---|
|
||||
| `install_systemd_service_bash_pipelines` | `install_systemd_service --name <N> --exec <PATH> [opts] -> json` | Pipeline que registra una app como servicio systemd del sistema: genera el unit, lo instala en /etc/systemd/system/, hace daemon-reload, enable, start y devuelve status. Requiere sudo sin password para systemctl y escritura en /etc/systemd/system/. |
|
||||
| `services_status_go_infra` | `func ServicesStatus(registryRoot string) ([]ServiceStatus, error)` | Lista todas las apps registradas con tag 'service' y reporta su estado: unidad systemd activa, puerto escuchando, y pc_id local. |
|
||||
| `setup_vps_app_go_infra` | `func SetupVPSApp(conn SSHConn, cfg DeployConfig) error` | Orquesta el setup inicial de una app en un VPS remoto: verifica SSH, crea dirs y usuario, sube binario, instala systemd unit y hace health check. |
|
||||
| `systemd_generate_unit_go_infra` | `func SystemdGenerateUnit(name, execStart, workDir, user string, env map[string]string) string` | Genera el texto de un archivo .service de systemd para una app. Incluye restart automático y env vars en orden determinista. |
|
||||
| `systemd_install_go_infra` | `func SystemdInstall(conn SSHConn, unitName, unitContent string) error` | Sube un unit file al host remoto, hace daemon-reload, enable y restart. Idempotente: reemplaza si el unit ya existe. |
|
||||
| `systemd_local_enable_bash_infra` | `systemd_local_enable(name: string) -> json` | Habilita un servicio systemd local con systemctl enable para que arranque automáticamente al boot. Requiere sudo. |
|
||||
| `systemd_local_install_unit_bash_infra` | `systemd_local_install_unit(name: string, unit_content: string) -> json` | Instala un unit file de systemd en /etc/systemd/system/<name>.service y ejecuta daemon-reload. Requiere sudo sin password para install y systemctl. Sobrescribe si el unit ya existe. |
|
||||
| `systemd_local_restart_bash_infra` | `systemd_local_restart(name: string) -> json` | Reinicia un servicio systemd local con systemctl restart. Útil tras actualizar el binario o cambiar el unit. Requiere sudo. |
|
||||
| `systemd_local_start_bash_infra` | `systemd_local_start(name: string) -> json` | Arranca un servicio systemd local con systemctl start. Devuelve el MainPID asignado. Requiere sudo. |
|
||||
| `systemd_local_status_bash_infra` | `systemd_local_status(name: string, log_lines: int = 10) -> json` | Devuelve el estado de un servicio systemd local: active state, sub state, PID, enabled, y las N últimas líneas de journalctl. No requiere sudo. |
|
||||
| `systemd_local_uninstall_bash_infra` | `systemd_local_uninstall(name: string) -> json` | Detiene, deshabilita y elimina el unit file de un servicio systemd local. Idempotente: no falla si el servicio ya está parado o el unit no existe. Requiere sudo. |
|
||||
| `systemd_restart_go_infra` | `func SystemdRestart(conn SSHConn, unitName string) error` | Reinicia un servicio systemd en un host remoto via SSH. |
|
||||
| `systemd_status_go_infra` | `func SystemdStatus(conn SSHConn, unitName string, logLines int) (SystemdServiceStatus, error)` | Consulta el estado de un servicio systemd en un host remoto. Retorna estado activo, sub-estado, PID y logs recientes. |
|
||||
| `tail_journal_bash_infra` | `tail_journal(unit: string, lines: int=100, follow: bool=false, since: string="", priority: string="info") -> void` | Wrapper sobre journalctl con formato consistente. Tail logs de una unidad systemd con coloreado, filtro por prioridad y seguimiento opcional. |
|
||||
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
### Service local (requiere sudo)
|
||||
|
||||
```bash
|
||||
./fn run install_systemd_service \
|
||||
--name myapp \
|
||||
--exec /opt/myapp/myapp \
|
||||
--workdir /opt/myapp \
|
||||
--user www-data \
|
||||
--env "PORT=8080" \
|
||||
--env "DB_PATH=/var/lib/myapp/db"
|
||||
```
|
||||
|
||||
### Service remoto via SSH
|
||||
|
||||
```bash
|
||||
# 1. Generar unit (pure, sin side effects)
|
||||
unit=$(./fn run systemd_generate_unit \
|
||||
--name myapp \
|
||||
--exec-start "/opt/myapp/myapp" \
|
||||
--work-dir /opt/myapp \
|
||||
--user deploy \
|
||||
--env "PORT=8080")
|
||||
|
||||
# 2. Instalar remoto
|
||||
./fn run systemd_install --host organic-machine --unit-name myapp --content "$unit"
|
||||
|
||||
# 3. Status
|
||||
./fn run systemd_status --host organic-machine --unit myapp.service
|
||||
```
|
||||
|
||||
### Auditoria de todos los services
|
||||
|
||||
```bash
|
||||
./fn doctor services --json | jq '.[] | {id, active, port_listening}'
|
||||
```
|
||||
|
||||
## Fronteras
|
||||
|
||||
- **NO maneja timers ni paths units**. Solo service units. Para cron/timer usa Dagu o cron clasico.
|
||||
- **NO genera socket activation**. Asume que la app abre su propio puerto.
|
||||
- **NO instala unit a nivel usuario (`--user`)** salvo que el caller pase la flag al systemctl. Default es `/etc/systemd/system/`.
|
||||
- Apps en `apps/` que se ejecutan local pero NO son service de larga duracion: NO necesitan systemd. Solo apps con `tags: [service]` en su `app.md`.
|
||||
@@ -0,0 +1,95 @@
|
||||
---
|
||||
group: cpp-tables
|
||||
tag: cpp-tables
|
||||
lang: cpp
|
||||
domain: core
|
||||
n: 10
|
||||
description: "Table Query Language: pipeline de transformacion tabular pura (filter, group, agg, sort, join, stats, formulas Lua, emit/apply round-trip) + render UI completa (data_table)."
|
||||
---
|
||||
|
||||
# cpp-tables — Table Query Language (C++ puro)
|
||||
|
||||
Stack de transformacion tabular C++. Nucleo headless (filtros, agrupacion, stats, joins, formulas Lua, round-trip TQL). Entry-point de render via `data_table_cpp_viz` que compone todo el stack con UI ImGui completa. Apps migran desde el playground cambiando solo el include path.
|
||||
|
||||
## Funciones del grupo
|
||||
|
||||
| ID | Firma corta | Que hace |
|
||||
|---|---|---|
|
||||
| `auto_detect_type_cpp_core` | `ColumnType auto_detect_type(cells, rows, cols, col, sample_n=64)` | Infiere ColumnType escaneando hasta N celdas no-vacias |
|
||||
| `compute_column_stats_cpp_core` | `ColStats compute_column_stats(cells, rows, cols, col, ...)` | Mean, p25/p50/p75, hist 24 bins, top-8 categorias |
|
||||
| `compute_stage_cpp_core` | `StageOutput compute_stage(cells, rows, cols, headers, types, stage)` | Ejecuta un Stage TQL: filter → group+agg → sort |
|
||||
| `compute_pipeline_cpp_core` | `StageOutput compute_pipeline(cells, rows, cols, headers, types, stages)` | Encadena N stages secuencialmente |
|
||||
| `join_tables_cpp_core` | `StageOutput join_tables(left..., right, jn)` | Hash join multi-key (inner/left/right/full) |
|
||||
| `lua_engine_cpp_core` | `Engine* get(); string eval(engine, id, ctx, err)` | Motor Lua 5.4 sandbox para formulas de columnas derivadas |
|
||||
| `tql_emit_cpp_core` | `std::string tql::emit(state, headers, types)` | Serializa State a texto Lua TQL v1 |
|
||||
| `tql_apply_cpp_core` | `ApplyResult tql::apply(lua_text, available_headers)` | Parsea texto TQL v1 y produce State + warnings |
|
||||
| `tql_helpers_cpp_core` | `op_label, view_mode_token, agg_fn_token, ...` | Conversiones puras enum↔token usadas por emit/apply |
|
||||
|
||||
## Tipos usados
|
||||
|
||||
- `data_table_types_cpp_core` — enums y structs base: `ColumnType`, `Op`, `Stage`, `StageOutput`, `Filter`, `Aggregation`, `Join`, `TableInput`, `ViewMode`, `VizPanel`, `ViewConfig`, `State`, etc.
|
||||
|
||||
## Ejemplo canonico end-to-end
|
||||
|
||||
```cpp
|
||||
// 1. Datos raw (row-major, 4 filas x 3 cols)
|
||||
#include "core/auto_detect_type.h"
|
||||
#include "core/compute_pipeline.h"
|
||||
#include "core/tql_emit.h"
|
||||
#include "core/tql_apply.h"
|
||||
using namespace data_table;
|
||||
|
||||
const char* raw[] = {
|
||||
"EU","A","100",
|
||||
"US","A","200",
|
||||
"EU","B","300",
|
||||
"EU","A","50"
|
||||
};
|
||||
std::vector<std::string> hdrs = {"region","type","revenue"};
|
||||
|
||||
// 2. Auto-detectar tipos
|
||||
std::vector<ColumnType> types;
|
||||
for (int c = 0; c < 3; ++c) types.push_back(auto_detect_type(raw, 4, 3, c));
|
||||
|
||||
// 3. Pipeline: filtrar EU, agrupar por type, sumar revenue
|
||||
Stage s0;
|
||||
s0.filters.push_back({0, Op::Eq, "EU"});
|
||||
|
||||
Stage s1;
|
||||
s1.breakouts = {"type"};
|
||||
Aggregation agg; agg.fn = AggFn::Sum; agg.col = "revenue"; s1.aggregations.push_back(agg);
|
||||
s1.sorts.push_back({"sum_revenue", true});
|
||||
|
||||
// 4. Ejecutar pipeline
|
||||
StageOutput out = compute_pipeline(raw, 4, 3, hdrs, types, {s0, s1});
|
||||
// out.rows==2: [B,300] [A,150]
|
||||
|
||||
// 5. Persistir estado como TQL (para guardar en BD o portapapeles)
|
||||
State st;
|
||||
st.stages = {s0, s1};
|
||||
st.display = ViewMode::Bar;
|
||||
std::string tql_text = tql::emit(st, hdrs, types);
|
||||
|
||||
// 6. Restaurar desde TQL
|
||||
tql::ApplyResult res = tql::apply(tql_text, hdrs);
|
||||
if (res.ok) {
|
||||
// res.state listo — compilar formulas derivadas con lua_engine si las hay
|
||||
}
|
||||
```
|
||||
|
||||
| `data_table_cpp_viz` | `void data_table::render(id, tables, st, show_chrome=true)` | Render UI completa: chips bar, tabla, viz panels, drill, TQL editor, Ask AI. Entry-point publica del stack. |
|
||||
|
||||
## Fronteras del grupo headless (core)
|
||||
|
||||
- NO incluye render: ningun include de ImGui, ImPlot ni tipos de ventana.
|
||||
- NO incluye I/O: no lee archivos CSV, no escribe a disco.
|
||||
- El motor Lua (`lua_engine`) es la unica funcion impura del grupo — gestiona estado del interprete.
|
||||
- `tql_apply` abre/cierra su propio `lua_State` por llamada (no usa lua_engine internamente). Fórmulas en `DerivedColumn.formula` se almacenan verbatim; el caller las compila con `lua_engine::compile()`.
|
||||
- La deteccion de fechas solo reconoce ISO 8601 (`YYYY-MM-DD`). Otros formatos caen en String.
|
||||
- Los joins trabajan sobre `TableInput` en memoria; no hay join lazy ni streaming.
|
||||
|
||||
## Prerequisitos
|
||||
|
||||
- `data_table_types_cpp_core` debe estar en el include path (`cpp/functions/`).
|
||||
- Para `lua_engine` y `tql_apply`: linkar contra la lib Lua 5.4 vendored (`cpp/vendor/lua/`, target `lua54`).
|
||||
- Para tests headless: usar `add_fn_test` del `cpp/tests/CMakeLists.txt` — no necesita fn_framework ni ImGui context.
|
||||
@@ -0,0 +1,74 @@
|
||||
# Transformer — Funciones que limpian, refinan o agregan datos
|
||||
|
||||
Tag: `transformer`. Grupo de funciones que **transforman datos**: clean, dedup, aggregate, feature-engineer, filter, impute, normalize. Input + output ambos datos (no efectos externos). Segundo eslabon del flujo en `data_factory` (analogia Factorio = assemblers).
|
||||
|
||||
Filtro MCP: `mcp__registry__fn_search query="" tag="transformer"`.
|
||||
|
||||
## Funciones del grupo
|
||||
|
||||
| ID | Lang | Que hace |
|
||||
|---|---|---|
|
||||
| [aggregate_by_group_py_datascience](../../python/functions/datascience/aggregate_by_group.md) | py | GROUP BY + agg |
|
||||
| [deduplicate_entities_py_datascience](../../python/functions/datascience/deduplicate_entities.md) | py | Dedup entities |
|
||||
| [deduplicate_relations_py_datascience](../../python/functions/datascience/deduplicate_relations.md) | py | Dedup relations |
|
||||
| [align_relations_to_entities_py_datascience](../../python/functions/datascience/align_relations_to_entities.md) | py | Reconciliacion |
|
||||
| [clip_py_datascience](../../python/functions/datascience/clip.md) | py | Cap valores fuera de rango |
|
||||
| [clip_go_datascience](../../functions/datascience/clip.md) | go | Cap valores fuera de rango |
|
||||
| [impute_py_datascience](../../python/functions/datascience/impute.md) | py | Rellenar nulls |
|
||||
| [impute_go_datascience](../../functions/datascience/impute.md) | go | Rellenar nulls |
|
||||
| [histogram_py_datascience](../../python/functions/datascience/histogram.md) | py | Bin frequencies |
|
||||
| [histogram_go_datascience](../../functions/datascience/histogram.md) | go | Bin frequencies |
|
||||
| [group_by_go_datascience](../../functions/datascience/group_by.md) | go | Group rows by key |
|
||||
| [coerce_types_py_core](../../python/functions/core/coerce_types.md) | py | Convert column types |
|
||||
| [csv_to_parquet_duckdb_py_core](../../python/functions/core/csv_to_parquet_duckdb.md) | py | Format conversion |
|
||||
| [diff_entities_py_datascience](../../python/functions/datascience/diff_entities.md) | py | Compare entity snapshots |
|
||||
| [diff_relations_py_datascience](../../python/functions/datascience/diff_relations.md) | py | Compare relation snapshots |
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
Pipeline limpiar dataframe: impute nulls -> clip outliers -> dedup -> aggregate.
|
||||
|
||||
```python
|
||||
from datascience import impute, clip, deduplicate_entities, aggregate_by_group
|
||||
|
||||
# 1. Rellenar nulls
|
||||
df = impute(df, columns=["price"], strategy="median")
|
||||
|
||||
# 2. Cap valores extremos
|
||||
df = clip(df, column="price", lower=0.0, upper=10000.0)
|
||||
|
||||
# 3. Deduplicar por clave
|
||||
df = deduplicate_entities(df, key_columns=["sku"])
|
||||
|
||||
# 4. Agregar por categoria
|
||||
out = aggregate_by_group(df, group_by=["category"], aggs={"price": "mean", "qty": "sum"})
|
||||
```
|
||||
|
||||
## Fronteras del grupo
|
||||
|
||||
NO cubre:
|
||||
- **Extract** (leer de fuente externa) -> [[extractor]].
|
||||
- **Sink** (escribir a destino externo) -> [[sink]].
|
||||
- **Validacion** (range/null/drift checks) -> [[validator]]. Transformers ASUMEN datos validos en entrada.
|
||||
- ML training (eso es `trainer`, deferred a v2 de data_factory).
|
||||
|
||||
## Cuando NO usar `transformer`
|
||||
|
||||
- Si la funcion solo lee/escribe pero no transforma -> es extractor o sink.
|
||||
- Si solo verifica una condicion (bool/list) -> es validator.
|
||||
- Si solo encadena varias funciones -> es `pipeline` (kind), no transformer plano.
|
||||
|
||||
## Pureza
|
||||
|
||||
La mayoria de transformers son **puros** (mismo input -> mismo output). Algunos son impuros por usar SDKs con caches (duckdb, polars). El tag `transformer` no impone pureza — verifica `purity` en frontmatter de cada funcion.
|
||||
|
||||
## Consumidores
|
||||
|
||||
- `data_factory` — tab Transformers.
|
||||
- `dag_engine` DAG steps con `function: <transformer_id>`.
|
||||
- Notebooks analysis/ pipelines.
|
||||
|
||||
## Notas
|
||||
|
||||
- Transformers Go/Py duplicados intencionalmente — eleccion por stack y rendimiento.
|
||||
- Si el registry incorpora `polars`/`duckdb` masivamente, considerar crear sub-grupos.
|
||||
@@ -0,0 +1,64 @@
|
||||
# Validator — Funciones que verifican datos o configuracion contra reglas
|
||||
|
||||
Tag: `validator`. Grupo de funciones que **verifican** un dato/config/spec contra una regla y devuelven resultado de validacion (bool / lista de errores / metricas de calidad). NO transforman ni mueven datos — solo dictaminan.
|
||||
|
||||
Filtro MCP: `mcp__registry__fn_search query="" tag="validator"`.
|
||||
|
||||
## Funciones del grupo
|
||||
|
||||
| ID | Lang | Que valida |
|
||||
|---|---|---|
|
||||
| [config_validate_go_infra](../../functions/infra/config_validate.md) | go | Estructura de config (campos requeridos, tipos) |
|
||||
| [file_validate_type_go_infra](../../functions/infra/file_validate_type.md) | go | Tipo MIME / extension de archivo |
|
||||
| [dag_validate_go_core](../../functions/core/dag_validate.md) | go | DAG topologia (ciclos, deps, schedule) |
|
||||
| [detect_outliers_py_datascience](../../python/functions/datascience/detect_outliers.md) | py | Outliers numericos (z-score / IQR) |
|
||||
| [detect_outliers_go_datascience](../../functions/datascience/detect_outliers.md) | go | Outliers numericos |
|
||||
| [detect_drift_py_datascience](../../python/functions/datascience/detect_drift.md) | py | Drift de distribucion vs baseline |
|
||||
|
||||
## Ejemplo canonico
|
||||
|
||||
Validar dataset antes de cargarlo a sink productivo.
|
||||
|
||||
```python
|
||||
from datascience import detect_outliers, detect_drift
|
||||
|
||||
# 1. Detectar outliers en columna critica
|
||||
outliers = detect_outliers(df, column="amount", method="zscore", threshold=3.0)
|
||||
if len(outliers) > len(df) * 0.05:
|
||||
raise ValueError(f"too many outliers: {len(outliers)} / {len(df)}")
|
||||
|
||||
# 2. Comparar contra distribucion historica
|
||||
drift_report = detect_drift(df_current=df, df_baseline=df_yesterday, column="amount")
|
||||
if drift_report["ks_pvalue"] < 0.01:
|
||||
print(f"WARNING: distribution drift detected (KS p={drift_report['ks_pvalue']:.4f})")
|
||||
```
|
||||
|
||||
## Fronteras del grupo
|
||||
|
||||
NO cubre:
|
||||
- **Audits** del registry mismo (capability groups, copied code, etc.) -> tag `doctor`.
|
||||
- **Tests** unitarios (estan en `*_test.go`/`pytest`, no son funciones del registry).
|
||||
- **Assertions** de runtime de apps (entan en `operations.db.assertions`).
|
||||
- Validacion de codigo (lint, formato) — fuera del registry.
|
||||
|
||||
## Cuando NO usar `validator`
|
||||
|
||||
- Si la funcion transforma datos para HACERLOS validos (ej. clip outliers) -> es transformer.
|
||||
- Si la funcion solo lee y reporta status sin verificar regla -> es extractor.
|
||||
|
||||
## Relacion con el bucle reactivo
|
||||
|
||||
Validators encajan en **Fase 4 ANALIZAR** del bucle reactivo (`fn_operations`). Una assertion definida en `operations.db.assertions` puede usar una funcion validator del registry como predicado.
|
||||
|
||||
## Consumidores
|
||||
|
||||
- `data_factory` — futura tab Validators (no v1; v1 solo extractor/transformer/sink/database).
|
||||
- DAGs de auditoria (ej. `weekly-deep-scan.yaml` invoca audits del grupo `doctor`).
|
||||
- Pipelines ML que validan datos antes de entrenar (defer v2).
|
||||
- Pre-flight de sinks: ejecutar validator antes de escribir a destino productivo.
|
||||
|
||||
## Notas
|
||||
|
||||
- Casi todos los validators son **puros** o casi-puros (solo dependen del input).
|
||||
- Grupo pequeño (6 funcs) — crecera cuando el registry incorpore validators de schema (JSON Schema, Pandera, etc.).
|
||||
- DAG step `function: <validator_id>` con `continue_on.failure: false` actua como gate en un pipeline.
|
||||
@@ -0,0 +1,124 @@
|
||||
# 2026-05-16
|
||||
|
||||
## 02:00 — Framework C++ anti-jitter v2 + floating panel survival + Alt drag/resize + bulk redeploy
|
||||
|
||||
Extensiones al anti-jitter del framework C++ (`cpp/framework/app_base.cpp` + `.h`):
|
||||
|
||||
- **Multi-HWND subclass.** `g_subclassed` ahora `unordered_map<HWND, WNDPROC>`. Scan per-frame en `pio.Viewports` instala subclass en cada HWND nuevo (cubre main + cada viewport flotante). `prune_dead_subclassed()` con `IsWindow` limpia stale. `uninstall_sizemove_subclass_all()` al exit restaura cada HWND. Fix del temblor en paneles arrastrados fuera del main.
|
||||
- **Iconified main + flotantes vivos.** El legacy `glfwWaitEvents+continue` paraba todo el frame loop. Con multi-viewport activo eso congelaba los flotantes. Ahora detecta secondary viewports y, si existen, fall-through al frame normal (main GL clear en HWND minimizado es harmless, contexts GL secundarios siguen pintando).
|
||||
- **Alt + RMB resize anywhere.** WndProc detecta `WM_RBUTTONDOWN` + `GetAsyncKeyState(VK_MENU) & 0x8000`, calcula direccion por cuadrante del cursor (WMSZ_TOPLEFT/TOPRIGHT/BOTTOMLEFT/BOTTOMRIGHT relativo al centro del client rect), `ReleaseCapture()` + `PostMessage(WM_SYSCOMMAND, SC_SIZE|dir)`. Modal nativo, gate sizemove existente pausa render automaticamente.
|
||||
- **Alt + LMB move anywhere.** Misma estructura para `WM_LBUTTONDOWN`. Posta `WM_SYSCOMMAND, SC_MOVE | HTCAPTION`. Modal MOVE nativo.
|
||||
- **`io.ConfigWindowsMoveFromTitleBarOnly = true`** en `fn::run_app`. Floating panels (OS window borderless + UNA ventana ImGui rellenandolo) ahora respetan "solo header arrastra". Fix del drag-anywhere-sin-alt en flotantes.
|
||||
- **`fn::internal::*` test observability.** `sizemove_enter_count()`, `alt_rmb_resize_count()`, `alt_lmb_move_count()`, `rbuttondown_seen_count()`, `set_force_alt_for_test(bool)`. Counters monotonicos zero-cost. En test mode los handlers de Alt cuentan pero NO postean SC_SIZE/SC_MOVE.
|
||||
|
||||
Tests en `apps/altsnap_jitter_test/main.cpp` extendidos a **6 phases** (p1 sync, p2 main HWND modal, p3 secondary HWND modal, p4 iconify+restore preserva flotante, p5 Alt+RMB consumed, p6 Alt+LMB consumed). Todas PASS en Windows.
|
||||
|
||||
Pipeline nuevo: **`redeploy_all_cpp_apps_bash_pipelines`** (`bash/functions/pipelines/redeploy_all_cpp_apps.sh` + `.md`). Cross-compila TODO el arbol `cpp/` en un solo cmake pass + redeploy de cada `.exe` al Desktop. Filtro opcional por substring. Tolerante a fallos (build best-effort, summary OK/SKIPPED/FAILED). Composicion: `build_cpp_windows_bash_infra` + loop `taskkill.exe` + `deploy_cpp_exe_to_windows_bash_infra`. Primera corrida: **12 OK / 1 SKIP / 0 FAILED**.
|
||||
|
||||
Fix `resolve_cpp_app_dir_bash_infra` **v1.1.0**: ahora busca apps en `apps/<X>/` (canonical issue 0096), luego `cpp/apps/<X>/` (legacy), luego `projects/*/apps/<X>/`. Antes solo veia los dos ultimos → `./fn run compile_cpp_app dag_engine_ui` fallaba. Deduccion desde CWD tambien cubre los tres layouts.
|
||||
|
||||
### Pitfalls / gotchas registrados
|
||||
|
||||
- `ImGui_ImplGlfw` subclassea el HWND DESPUES que nuestro framework. Captura nuestro WndProc como `bd->PrevWndProc` y chainea via `CallWindowProc` → todos los mensajes nos llegan en orden correcto. **NO re-subclassear** despues de ImGui init (genera recursion infinita por cycle).
|
||||
- `keybd_event(VK_MENU)` no es fiable para drivear `GetAsyncKeyState` desde tests headless cross-compilados (sesion de input no foreground). Usar `set_force_alt_for_test(true)` + `SendMessageW` sincrono mismo-hilo. `PostMessage(WM_RBUTTONDOWN)` sintetizado tambien es dropeado por el kernel input filter.
|
||||
- Pre-existing build break en `cpp/tests/test_llm_anthropic.cpp` + `cpp/tests/test_graph_icons.cpp` por `setenv()` no disponible en mingw-w64. NO bloquea `redeploy_all_cpp_apps` (build best-effort). Candidato a guard `#ifdef _WIN32` + `_putenv_s` o skip cross-compile.
|
||||
|
||||
### Lo siguiente que pega
|
||||
|
||||
- Resolver el build break de `test_llm_anthropic.cpp` + `test_graph_icons.cpp` con `_putenv_s` bajo `#ifdef _WIN32`. Quitar el SKIP del log de `redeploy_all_cpp_apps`.
|
||||
- Considerar regla nueva sobre **ImGui io flags criticos** (`ConfigWindowsMoveFromTitleBarOnly`, `ConfigViewportsNoAutoMerge`, `ConfigDragClickToInputText`, etc.) que cambian comportamiento de viewports. Sub-seccion en `cpp_apps.md` o archivo separado en `.claude/rules/imgui_io_flags.md`.
|
||||
- Documentar en `cpp/PATTERNS.md` que `add_imgui_app` ya embebe el icono `appicon.ico` si esta en el `<app_dir>` (verificado en sesion; el usuario lo mantuvo entre redeploys).
|
||||
|
||||
### Archivos tocados
|
||||
|
||||
- `cpp/framework/app_base.cpp` (multi-HWND, iconified-gate, Alt+RMB, Alt+LMB, ConfigWindowsMoveFromTitleBarOnly, fn::internal::*)
|
||||
- `cpp/framework/app_base.h` (declaracion de fn::internal::*)
|
||||
- `apps/altsnap_jitter_test/main.cpp` (phases 3-6)
|
||||
- `apps/altsnap_jitter_test/app.md` (6 phases doc)
|
||||
- `bash/functions/pipelines/redeploy_all_cpp_apps.sh` (nuevo)
|
||||
- `bash/functions/pipelines/redeploy_all_cpp_apps.md` (nuevo, growth log v1.0.0)
|
||||
- `bash/functions/infra/resolve_cpp_app_dir.sh` (v1.1.0)
|
||||
- `bash/functions/infra/resolve_cpp_app_dir.md` (v1.1.0 + growth log)
|
||||
- `.claude/rules/cpp_apps.md` (seccion 7.1 ampliada con v2)
|
||||
- `docs/capabilities/cpp-windows.md` (tabla + seccion bulk redeploy + layouts)
|
||||
- `CHANGELOG.md` (entrada 2026-05-16)
|
||||
|
||||
---
|
||||
|
||||
## 01:05 — Iconos .ico para apps C++ + funcion del registry
|
||||
|
||||
Las 11 apps C++ desplegadas a Windows no tenian icono → imposible distinguirlas en Desktop/taskbar (todas con el icono generico de exe).
|
||||
|
||||
### Pipeline build-time end-to-end
|
||||
|
||||
1. **Fuente de glyphs**: clonado `phosphor-icons/core` (MIT) en `sources/phosphor-core/` con `git clone --depth=1`. 1512 SVGs en 6 weights. Registrado en `sources/sources.yaml`.
|
||||
|
||||
2. **Mapping inicial** en `dev/gen_app_icons.py` (script reproducible, NO es del registry — vive en `dev/`). Tabla `APPS = [(app_id, dir, phosphor_icon, accent_hex), ...]` con un Tailwind color 500-700 por app:
|
||||
- chart_demo → chart-bar / sky-500
|
||||
- dag_engine_ui → tree-structure / violet-600
|
||||
- data_factory → factory / orange-500
|
||||
- engine_smoke (no aplicado: usa raw add_executable, no `add_imgui_app`)
|
||||
- graph_explorer → graph / cyan-600
|
||||
- navegator_dashboard → compass / blue-600
|
||||
- odr_console → terminal-window / zinc-600
|
||||
- primitives_gallery → shapes / pink-600
|
||||
- registry_dashboard → gauge / emerald-600
|
||||
- runtime_test (no aplicado: raw add_executable)
|
||||
- shaders_lab → palette / orange-600
|
||||
- text_editor_smoke → note-pencil / teal-600
|
||||
- altsnap_jitter_test → arrows-clockwise / red-600
|
||||
|
||||
3. **Funcion del registry**: `generate_app_icon_py_infra` (`python/functions/infra/generate_app_icon.py`). Lo escribe `fn-constructor`. Signature `generate_app_icon(phosphor_icon_name, accent_hex, out_ico_path, *, weight='fill', sizes=None, phosphor_root=None) -> str`. Tags `cpp-windows, icon, phosphor`. Deps `cairosvg + Pillow` ya en `python/.venv` (`uv pip install cairosvg` en `python/`). Validado con `./fn show` + smoke test `/tmp/test_icon2.ico`.
|
||||
|
||||
4. **Wiring CMake** (`cpp/CMakeLists.txt`):
|
||||
- Linea 1-5: `project(... LANGUAGES C CXX RC)` solo en WIN32. Linux ignora.
|
||||
- Macro `add_imgui_app` (linea ~241): si `WIN32 AND EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/appicon.ico`, genera `${target}_appicon.rc` en build dir con `IDI_ICON1 ICON "<full_path>"` y lo anade a `add_executable`. `x86_64-w64-mingw32-windres` (ya en toolchain) lo compila a `.res` y se enlaza al `.exe` como seccion `.rsrc`.
|
||||
|
||||
5. **Rebuild + redeploy**: `cmake --build cpp/build/windows -- -j$(nproc)` + `deploy_cpp_exe_to_windows` para las 11. Verificacion: `x86_64-w64-mingw32-objdump -h <exe> | grep rsrc` muestra la seccion en todas.
|
||||
|
||||
### Decision: build-time vs post-build
|
||||
|
||||
Existia `set_exe_icon_go_infra` (Go puro, embebe `.ico` post-build modificando PE). Descartado porque:
|
||||
- Falla si el `.exe` ya tiene seccion `.rsrc` (limita robustez).
|
||||
- Anade paso extra al pipeline redeploy.
|
||||
- Build-time es atomico: el `.exe` recien linkeado YA tiene el icono.
|
||||
|
||||
### Gotchas
|
||||
|
||||
- **engine_smoke** y **runtime_test** usan `add_executable` raw, no `add_imgui_app` — no reciben icono automatico. Son smoke tests, no apps user-facing. Skip aceptable.
|
||||
- **shaders_lab** vive en `apps/shaders_lab/`, no en `cpp/apps/shaders_lab/` (confusion inicial del mapping; corregido).
|
||||
- **Pillow `save(format='ICO')` con `append_images`**: si pasas la imagen 16x16 como base, el `.ico` final solo tiene 16x16. Hay que pasar la imagen MAS GRANDE (256x256) como base + lista de variants per-size. Variants per-size dan crispness en 16/24 mejor que dejar Pillow downscalear.
|
||||
- **Cache iconos Windows** (`iconcache.db`): tras desplegar nuevo `.ico`, Explorer puede seguir mostrando el viejo. Refresh: `ie4uinit.exe -show` o restart explorer.
|
||||
|
||||
### Para anadir icono a una app nueva
|
||||
|
||||
```bash
|
||||
# 1. Elegir nombre Phosphor desde sources/phosphor-core/assets/fill/
|
||||
ls sources/phosphor-core/assets/fill/ | grep <keyword>
|
||||
|
||||
# 2. Generar el .ico
|
||||
./fn run generate_app_icon "<phosphor-name>" "#<accent_hex>" "<app_dir>/appicon.ico"
|
||||
|
||||
# 3. Rebuild + redeploy
|
||||
./fn run redeploy_cpp_app_windows <app_name> <app_dir> --build
|
||||
```
|
||||
|
||||
### Archivos tocados
|
||||
|
||||
- `sources/phosphor-core/` (nuevo, clonado)
|
||||
- `sources/sources.yaml` (entry phosphor-icons/core)
|
||||
- `dev/gen_app_icons.py` (nuevo, script reproducible con mapping inicial)
|
||||
- `python/functions/infra/generate_app_icon.py` (nueva funcion registro)
|
||||
- `python/functions/infra/generate_app_icon.md` (idem)
|
||||
- `python/functions/infra/__init__.py` (export)
|
||||
- `cpp/CMakeLists.txt` (project LANGUAGES + add_imgui_app icon wiring)
|
||||
- 11 x `<app_dir>/appicon.ico` (nuevos, multi-res)
|
||||
- `.claude/rules/cpp_apps.md` (§11 Icono Windows)
|
||||
- `CHANGELOG.md` (entrada 2026-05-16 Added: iconos .ico)
|
||||
- 11 x `.exe` rebuildeadas + redesplegadas a `/mnt/c/Users/lucas/Desktop/apps/`
|
||||
|
||||
### Lo siguiente que pega
|
||||
|
||||
- Anadir icono tambien a `engine_smoke` y `runtime_test` si se promueven a apps user-facing (hoy son smoke tests).
|
||||
- Considerar `fn doctor cpp-apps` check: app C++ sin `appicon.ico` → warning.
|
||||
- Si aparecen iconos antiguos en Explorer tras redeploy, anadir `ie4uinit.exe -show` al final de `deploy_cpp_exe_to_windows`.
|
||||
Reference in New Issue
Block a user