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:
2026-05-16 16:33:22 +02:00
parent d6175964e4
commit 212875ed0d
290 changed files with 12703 additions and 19778 deletions
+120
View File
@@ -0,0 +1,120 @@
// cdp_pick_element_js - JS injected via CDP Runtime.evaluate.
// Activates click-to-pick mode: hover highlights, click captures element selectors
// and prints via console.log so caller (navegator_dashboard) reads via
// Runtime.consoleAPICalled event.
//
// Inject as expression: see cdp_pick_element_js.md for the wrapping payload.
// Idempotent: re-injecting cleans up previous overlays before reattaching.
(function () {
if (window.__fn_pick_active) {
window.__fn_pick_cleanup && window.__fn_pick_cleanup();
}
window.__fn_pick_active = true;
const overlay = document.createElement('div');
overlay.id = '__fn_pick_overlay';
Object.assign(overlay.style, {
position: 'fixed', pointerEvents: 'none', zIndex: '2147483647',
border: '2px solid #f44', background: 'rgba(255,80,80,0.10)',
boxSizing: 'border-box', transition: 'all 30ms linear',
left: '0px', top: '0px', width: '0px', height: '0px',
});
document.body.appendChild(overlay);
function cssPath(el) {
if (!(el instanceof Element)) return '';
const path = [];
while (el && el.nodeType === Node.ELEMENT_NODE && el !== document.body) {
let seg = el.tagName.toLowerCase();
if (el.id) { seg += '#' + CSS.escape(el.id); path.unshift(seg); break; }
// Prefer class-based path when class is short and not utility-noise.
if (el.className && typeof el.className === 'string') {
const cls = el.className.trim().split(/\s+/).filter(c => c && c.length < 24 && !/^(active|hover|focus|selected|open|disabled|[0-9])/.test(c));
if (cls.length) seg += '.' + cls.map(CSS.escape).join('.');
}
// Disambiguate by nth-of-type when siblings share tag.
if (el.parentNode) {
const siblings = [...el.parentNode.children].filter(c => c.tagName === el.tagName);
if (siblings.length > 1) {
const idx = siblings.indexOf(el) + 1;
seg += `:nth-of-type(${idx})`;
}
}
path.unshift(seg);
el = el.parentNode;
if (path.length > 6) break;
}
return path.join(' > ');
}
function xpath(el) {
if (!el) return '';
if (el.id) return `//*[@id="${el.id}"]`;
const parts = [];
while (el && el.nodeType === Node.ELEMENT_NODE) {
let ix = 0;
for (const sib of el.parentNode ? el.parentNode.childNodes : []) {
if (sib === el) { parts.unshift(`${el.tagName.toLowerCase()}[${ix + 1}]`); break; }
if (sib.nodeType === 1 && sib.tagName === el.tagName) ix++;
}
el = el.parentNode;
if (el === document.body) { parts.unshift('body'); break; }
}
return '/html/body/' + parts.join('/');
}
function describe(el) {
if (!el) return null;
const r = el.getBoundingClientRect();
const attrs = {};
for (const a of el.attributes || []) attrs[a.name] = a.value;
return {
selector: cssPath(el),
xpath: xpath(el),
tag: el.tagName.toLowerCase(),
text: (el.innerText || el.textContent || '').trim().slice(0, 200),
attrs,
rect: { x: Math.round(r.left), y: Math.round(r.top), w: Math.round(r.width), h: Math.round(r.height) },
};
}
function onMove(e) {
const el = e.target;
if (!el || el === overlay) return;
const r = el.getBoundingClientRect();
overlay.style.left = r.left + 'px';
overlay.style.top = r.top + 'px';
overlay.style.width = r.width + 'px';
overlay.style.height = r.height + 'px';
}
function onClick(e) {
e.preventDefault();
e.stopPropagation();
const info = describe(e.target);
console.log('__fn_picked__', JSON.stringify(info));
cleanup();
}
function onKey(e) {
if (e.key === 'Escape') {
console.log('__fn_picked__', JSON.stringify({ cancelled: true }));
cleanup();
}
}
function cleanup() {
window.__fn_pick_active = false;
document.removeEventListener('mousemove', onMove, true);
document.removeEventListener('click', onClick, true);
document.removeEventListener('keydown', onKey, true);
overlay.remove();
}
window.__fn_pick_cleanup = cleanup;
document.addEventListener('mousemove', onMove, true);
document.addEventListener('click', onClick, true);
document.addEventListener('keydown', onKey, true);
return 'pick mode active';
})();
+45
View File
@@ -0,0 +1,45 @@
---
name: cdp_pick_element_js
kind: function
lang: js
domain: browser
version: "1.0.0"
purity: pure
signature: "(self-executing IIFE injected via CDP Runtime.evaluate)"
description: "Snippet JS inyectable que activa modo click-to-pick en una pagina Chrome remota: hover muestra overlay rojo, click captura CSS selector + XPath + tag + texto + bbox y los emite por console.log con prefijo '__fn_picked__'. El caller (navegator_dashboard) lee via CDP Runtime.consoleAPICalled."
tags: [navegator, cdp, browser, picker, scraping, js]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: ""
file_path: "functions/browser/cdp_pick_element_js.js"
params:
- name: (none — IIFE, sin argumentos)
desc: "Snippet stand-alone. Se inyecta el fichero entero como `expression` de Runtime.evaluate."
output: "Mensaje en console.log con prefijo `__fn_picked__` seguido de JSON {selector,xpath,tag,text,attrs,rect}. Si user pulsa Escape: {cancelled: true}."
example: |
# En navegator_dashboard, al pulsar boton "Pick":
# 1. Lee el archivo cdp_pick_element_js.js (string).
# 2. Envia via WebSocket CDP al tab activo:
# {"id": N, "method": "Runtime.evaluate", "params": {"expression": "<contenido del .js>"}}
# 3. Escucha eventos "Runtime.consoleAPICalled" filtrando args[0].value == "__fn_picked__".
# 4. Parsea args[1].value como JSON -> dict con selector/xpath/tag/text/attrs/rect.
---
## Cuando usarla
Cuando un user pulsa "Pick element" en un tab de Chrome remoto para capturar un selector robusto sin abrir DevTools. Salida usable en recipes YAML o tests.
## Gotchas
- Idempotente: re-inyectar limpia overlay anterior.
- Path CSS truncado a 6 niveles para evitar selectores fragiles cross-rerender.
- Filtra clases dinamicas comunes (`active`, `hover`, `selected`...) que rotan.
- No funciona sobre iframes anidados — solo top frame.
- Escape cancela y emite `{cancelled: true}`.
- El listener captura events en fase capture para preceder a handlers de la pagina.
## Notas
Reutilizable en otras apps C++/Python que hablen CDP. La salida via `console.log` es preferible a `Runtime.evaluate` con `returnByValue=true` porque el pick es asincrono (espera click del user) y no encaja en una sola RPC.