feat(infra): grupo claude-fleet — FleetView TUI + orquestacion de Claudes
Sistema FleetView para centralizar la flota de procesos Claude Code vivos en una sola ventana kitty + tmux (socket aislado -L fleet) con un panel TUI: - list_claude_fleet (+ tipo claude_fleet): escanea ~/.claude/sessions + goals + runtime, valida procesos vivos (anti-PID-reciclado), join por sessionId. - list_resumable_claudes (+ tipo resumable_claude): sesiones cerradas reanudables. - wrappers tmux: tmux_new_claude_window (con --resume), tmux_swap_window_into_console (preserva ancho del sidebar), tmux_map_claude_panes. - launch_kittyclaude: comando entrypoint; instala atajos alt+flechas/enter/n/0/k/r, mouse on, remain-on-exit off; fija el ancho del sidebar con hooks. - docs/capabilities/claude-fleet.md + entrada en el INDEX. Incluye ademas funciones datascience en progreso (excel/duckdb/postgres) y ajustes varios de docs e infra de otra sesion, agrupados aqui para no perderlos. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
"""Ingesta una hoja de un archivo .xlsx a una tabla DuckDB.
|
||||
|
||||
Funcion impura: abre el archivo DuckDB destino en modo read-write
|
||||
(`duckdb.connect(duckdb_path)`, que crea el archivo si no existe), carga la
|
||||
extension `excel` de DuckDB y materializa la hoja del .xlsx en una tabla con
|
||||
`read_xlsx`. La conexion se cierra siempre en un bloque try/finally. Devuelve un
|
||||
dict sin lanzar excepciones, siguiendo el estilo del grupo duckdb del registry:
|
||||
{status:'ok', ...} en exito y {status:'error', error:str} en fallo.
|
||||
|
||||
Camino activo (verificado en DuckDB 1.5.2): extension nativa `excel`. El path del
|
||||
.xlsx y el nombre de la hoja se pasan como parametros posicionales (marcador `?`)
|
||||
a `read_xlsx`, por lo que NO se interpolan en el SQL y no hay inyeccion por esa
|
||||
via. El identificador de tabla destino SI se interpola (CREATE/INSERT no admiten
|
||||
parametro para el nombre de tabla), asi que se valida contra un regex estricto.
|
||||
|
||||
mode='replace' (default) -> `CREATE OR REPLACE TABLE <table> AS SELECT * FROM
|
||||
read_xlsx(?)`: reemplaza la tabla entera. mode='append' -> crea la tabla si no
|
||||
existe (`CREATE TABLE IF NOT EXISTS ... AS SELECT ... LIMIT 0` para fijar el
|
||||
schema) y luego `INSERT INTO <table> SELECT * FROM read_xlsx(?)`.
|
||||
"""
|
||||
|
||||
import re
|
||||
|
||||
# Identificador de tabla valido: letras, digitos y guion bajo, sin empezar por
|
||||
# digito. Rechaza cualquier cosa que pudiera inyectarse en el CREATE/INSERT.
|
||||
_VALID_IDENT = re.compile(r"^[A-Za-z_][A-Za-z0-9_]*$")
|
||||
|
||||
|
||||
def excel_to_duckdb(
|
||||
xlsx_path: str,
|
||||
duckdb_path: str,
|
||||
table: str,
|
||||
sheet: str = None,
|
||||
mode: str = "replace",
|
||||
) -> dict:
|
||||
"""Ingesta una hoja de un .xlsx a una tabla DuckDB via la extension excel.
|
||||
|
||||
Args:
|
||||
xlsx_path: ruta al archivo .xlsx de origen. Debe existir y ser legible.
|
||||
Se pasa como parametro posicional a read_xlsx (no se interpola).
|
||||
duckdb_path: ruta al archivo DuckDB destino. Se abre en modo escritura, que
|
||||
crea el archivo si no existe. DuckDB es single-writer: si otro proceso
|
||||
lo tiene abierto en escritura, falla con error de lock.
|
||||
table: nombre de la tabla destino. Se valida contra
|
||||
^[A-Za-z_][A-Za-z0-9_]*$ antes de interpolarlo en el SQL (CREATE/INSERT
|
||||
no admiten parametro para el nombre de tabla). Identificador invalido
|
||||
devuelve {status:'error', ...} sin tocar la base.
|
||||
sheet: nombre de la hoja a leer. None (default) lee la primera hoja del
|
||||
libro. Se pasa como parametro posicional (sheet=?) a read_xlsx.
|
||||
mode: 'replace' (default) reemplaza la tabla entera con CREATE OR REPLACE
|
||||
TABLE AS SELECT. 'append' crea la tabla si no existe y luego inserta
|
||||
las filas con INSERT INTO ... SELECT. Cualquier otro valor devuelve
|
||||
{status:'error', ...}.
|
||||
|
||||
Returns:
|
||||
dict. En exito: {status:'ok', table:str, row_count:int} donde row_count es
|
||||
el numero de filas que tiene la tabla tras la ingesta. En error (sin
|
||||
lanzar): {status:'error', error:str}.
|
||||
"""
|
||||
if not isinstance(table, str) or not _VALID_IDENT.match(table):
|
||||
return {
|
||||
"status": "error",
|
||||
"error": f"invalid table identifier: {table!r}",
|
||||
}
|
||||
if mode not in ("replace", "append"):
|
||||
return {
|
||||
"status": "error",
|
||||
"error": f"invalid mode: {mode!r} (expected 'replace' or 'append')",
|
||||
}
|
||||
|
||||
quoted = '"' + table.replace('"', '""') + '"'
|
||||
|
||||
# Argumentos de read_xlsx: path siempre, sheet solo si se especifica. Todo
|
||||
# como parametros posicionales para evitar inyeccion via el .xlsx/hoja.
|
||||
if sheet is not None:
|
||||
read_call = "read_xlsx(?, sheet=?)"
|
||||
read_params = [xlsx_path, sheet]
|
||||
else:
|
||||
read_call = "read_xlsx(?)"
|
||||
read_params = [xlsx_path]
|
||||
|
||||
conn = None
|
||||
try:
|
||||
conn = __import__("duckdb").connect(duckdb_path)
|
||||
# La extension excel se instala (red la 1a vez) y carga en la conexion.
|
||||
conn.execute("INSTALL excel; LOAD excel;")
|
||||
|
||||
if mode == "replace":
|
||||
conn.execute(
|
||||
f"CREATE OR REPLACE TABLE {quoted} AS SELECT * FROM {read_call}",
|
||||
read_params,
|
||||
)
|
||||
else: # append
|
||||
# Fijamos el schema de la tabla con un SELECT vacio si no existe, sin
|
||||
# cargar datos; luego insertamos todas las filas.
|
||||
conn.execute(
|
||||
f"CREATE TABLE IF NOT EXISTS {quoted} AS "
|
||||
f"SELECT * FROM {read_call} LIMIT 0",
|
||||
read_params,
|
||||
)
|
||||
conn.execute(
|
||||
f"INSERT INTO {quoted} SELECT * FROM {read_call}",
|
||||
read_params,
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
row_count = conn.execute(f"SELECT COUNT(*) FROM {quoted}").fetchone()[0]
|
||||
return {"status": "ok", "table": table, "row_count": int(row_count)}
|
||||
except Exception as e: # noqa: BLE001
|
||||
return {"status": "error", "error": str(e)}
|
||||
finally:
|
||||
if conn is not None:
|
||||
conn.close()
|
||||
Reference in New Issue
Block a user