feat(browser): auto-commit con 178 cambios
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
---
|
||||
name: focus_cdp_tab_window
|
||||
id: focus_cdp_tab_window_bash_infra
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.0.0"
|
||||
purity: impure
|
||||
signature: "focus_cdp_tab_window(port: int, [target_id: string]) -> void"
|
||||
description: "Handoff humano de captcha: trae al frente la pestaña (via CDP /json/activate) y la ventana del SO de un Chrome con CDP, para que el humano resuelva el captcha a mano. Promocion del patron inline que acompaña a detect_captcha_go_browser."
|
||||
tags: [browser, captcha, handoff, cdp, wmctrl, xdotool, infra, navegator]
|
||||
params:
|
||||
- name: "port"
|
||||
desc: "Puerto CDP del Chrome (ej. 9333 = Chrome aislado del browser_mcp; 9222 = navegador diario). Obligatorio."
|
||||
- name: "target_id"
|
||||
desc: "Opcional. Target/tab id CDP de la pestaña del captcha. Si se pasa, se activa esa pestaña dentro del browser antes de levantar la ventana del SO. Si se omite, solo se levanta la ventana."
|
||||
output: "Stdout una linea legible y JSON-parseable simple: 'focus_cdp_tab_window: focused win=<wid> pid=<pid> port=<port> tab=<target_id_o_->'. Exit 0 en exito; 2 sin puerto, 3 sin DISPLAY, 4 falta wmctrl/xdotool, 5 no hay chromium en el puerto, 6 sin ventana top-level."
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
returns_optional: false
|
||||
error_type: "error_go_core"
|
||||
imports: []
|
||||
tested: false
|
||||
tests: []
|
||||
test_file_path: ""
|
||||
file_path: "bash/functions/infra/focus_cdp_tab_window.sh"
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
# Activar la pestaña del captcha (por su target id CDP) y levantar la ventana del Chrome aislado
|
||||
focus_cdp_tab_window 9333 20EF6E28AA792C53AF0D260F34A768B3
|
||||
# -> focus_cdp_tab_window: focused win=0x03a00007 pid=48213 port=9333 tab=20EF6E28AA792C53AF0D260F34A768B3
|
||||
|
||||
# Solo levantar la ventana del Chrome (sin activar tab concreta)
|
||||
focus_cdp_tab_window 9333
|
||||
# -> focus_cdp_tab_window: focused win=0x03a00007 pid=48213 port=9333 tab=-
|
||||
```
|
||||
|
||||
Invocacion canonica via el CLI del registry (despacho bash automatico):
|
||||
|
||||
```bash
|
||||
./fn run focus_cdp_tab_window 9333 20EF6E28AA792C53AF0D260F34A768B3
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
En el handoff humano de captcha: cuando el `browser_mcp` marca `⚠️ CAPTCHA-DETECTED`
|
||||
(via `detect_captcha_go_browser`), usa esta funcion para traer la pestaña del captcha y la
|
||||
ventana del Chrome al frente para que el humano lo resuelva a mano; luego se le notifica y se
|
||||
para la automatizacion. Pasa el `target_id` de la tab donde se detecto el captcha para activar
|
||||
esa pestaña exacta; omitelo si solo necesitas levantar la ventana del navegador.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Impura, requiere X11**: necesita un entorno grafico (`$DISPLAY` no vacio) + `wmctrl` + `xdotool`
|
||||
instalados. No sirve headless ni por SSH sin X forwarding — sale con error y exit != 0.
|
||||
- **Match pid->ventana fragil**: resuelve la ventana cruzando el PID del browser principal con la
|
||||
columna PID de `wmctrl -lp`. Puede fallar si el window manager agrupa ventanas o si chromium no
|
||||
expone `_NET_WM_PID` en el main; de ahi el fallback a `xdotool search --pid <pid> --onlyvisible`.
|
||||
- **No reposiciona entre monitores**: solo activa/levanta la ventana donde ya esta; no la mueve a
|
||||
otra pantalla.
|
||||
- **Varias ventanas del mismo Chrome**: si el browser tiene varias ventanas top-level, coge la
|
||||
primera que matchea el PID.
|
||||
- **Activate CDP best-effort**: `curl /json/activate/<target_id>` puede dar 404 si el `target_id`
|
||||
caduco (la tab cambio de id o se cerro). La funcion NO aborta: sigue con el raise de la ventana
|
||||
igualmente.
|
||||
- **Reintento por XFCE**: xfwm pisa el primer `windowactivate`/`windowraise`, por eso se hace el
|
||||
activate+raise dos veces con una espera corta entre medias.
|
||||
- **Identifica el browser process por ausencia de `--type=`**: las lineas de `pgrep` con
|
||||
`--type=renderer/gpu/utility/zygote` son procesos hijos; se descartan para quedarse con el main.
|
||||
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
# focus_cdp_tab_window — trae al frente la pestaña + la ventana del SO de un Chrome con CDP
|
||||
#
|
||||
# Handoff humano de captcha: activa la tab del captcha (opcional, via CDP) y levanta
|
||||
# la ventana X11 del proceso browser principal de ese puerto para que un humano resuelva
|
||||
# el captcha a mano. Best-effort y robusto: cada paso continua aunque uno falle.
|
||||
|
||||
focus_cdp_tab_window() {
|
||||
set -uo pipefail
|
||||
|
||||
local port="${1:-}"
|
||||
local target_id="${2:-}"
|
||||
|
||||
# 1. Validacion de entorno y dependencias.
|
||||
if [[ -z "$port" ]]; then
|
||||
echo "focus_cdp_tab_window: falta el puerto CDP (uso: focus_cdp_tab_window <port> [target_id])" >&2
|
||||
return 2
|
||||
fi
|
||||
if [[ -z "${DISPLAY:-}" ]]; then
|
||||
echo "focus_cdp_tab_window: sin entorno grafico (DISPLAY vacio)" >&2
|
||||
return 3
|
||||
fi
|
||||
if ! command -v wmctrl >/dev/null 2>&1 || ! command -v xdotool >/dev/null 2>&1; then
|
||||
echo "focus_cdp_tab_window: falta wmctrl/xdotool" >&2
|
||||
return 4
|
||||
fi
|
||||
|
||||
# 2. Activar la tab del captcha dentro del browser (best-effort, no aborta).
|
||||
if [[ -n "$target_id" ]]; then
|
||||
curl -sf "http://127.0.0.1:${port}/json/activate/${target_id}" >/dev/null 2>&1 || true
|
||||
fi
|
||||
|
||||
# 3. Encontrar el PID del proceso BROWSER principal de ese puerto.
|
||||
# De las lineas que matchean el flag de debugging, el browser process es el que
|
||||
# NO lleva --type= (los renderers/gpu/utility/zygote son procesos hijos).
|
||||
local browser_pid=""
|
||||
local line
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
if [[ "$line" == *"--type="* ]]; then
|
||||
continue
|
||||
fi
|
||||
# pgrep -af antepone el PID seguido de la cmdline.
|
||||
browser_pid="${line%% *}"
|
||||
break
|
||||
done < <(pgrep -af -- "remote-debugging-port=${port}" 2>/dev/null)
|
||||
|
||||
if [[ -z "$browser_pid" ]]; then
|
||||
echo "focus_cdp_tab_window: no hay chromium en el puerto ${port}" >&2
|
||||
return 5
|
||||
fi
|
||||
|
||||
# 4. Resolver el window id top-level.
|
||||
# Primero por wmctrl -lp (columna 3 = PID). Fallback xdotool si el main no expone _NET_WM_PID.
|
||||
local wid=""
|
||||
while IFS= read -r line; do
|
||||
[[ -z "$line" ]] && continue
|
||||
# Formato: <wid> <desktop> <pid> <host> <title...>
|
||||
local w_id w_pid
|
||||
w_id="$(awk '{print $1}' <<<"$line")"
|
||||
w_pid="$(awk '{print $3}' <<<"$line")"
|
||||
if [[ "$w_pid" == "$browser_pid" ]]; then
|
||||
wid="$w_id"
|
||||
break
|
||||
fi
|
||||
done < <(wmctrl -lp 2>/dev/null)
|
||||
|
||||
if [[ -z "$wid" ]]; then
|
||||
wid="$(xdotool search --pid "$browser_pid" --onlyvisible 2>/dev/null | head -n1)"
|
||||
fi
|
||||
|
||||
if [[ -z "$wid" ]]; then
|
||||
echo "focus_cdp_tab_window: no se encontro ventana top-level para pid ${browser_pid} (puerto ${port})" >&2
|
||||
return 6
|
||||
fi
|
||||
|
||||
# 5. Traer al frente con REINTENTO (xfwm de XFCE pisa el primer activate/raise).
|
||||
# Espera no bloqueante con read -t en vez de sleep.
|
||||
local attempt
|
||||
for attempt in 1 2; do
|
||||
xdotool windowactivate "$wid" >/dev/null 2>&1 || true
|
||||
read -r -t 0.2 _ < /dev/zero 2>/dev/null || true
|
||||
xdotool windowraise "$wid" >/dev/null 2>&1 || true
|
||||
done
|
||||
|
||||
# 6. Salida legible y JSON-parseable simple.
|
||||
echo "focus_cdp_tab_window: focused win=${wid} pid=${browser_pid} port=${port} tab=${target_id:--}"
|
||||
return 0
|
||||
}
|
||||
|
||||
# Permitir ejecucion directa: focus_cdp_tab_window <port> [target_id]
|
||||
if [[ "${BASH_SOURCE[0]}" == "${0}" ]]; then
|
||||
focus_cdp_tab_window "$@"
|
||||
fi
|
||||
@@ -3,10 +3,10 @@ name: launch_fleetclaude
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: "1.3.2"
|
||||
version: "1.4.0"
|
||||
purity: impure
|
||||
signature: "launch_fleetclaude [--cwd <dir>] [--bin <path>] [--session <name>] [--cols <n>]"
|
||||
description: "Entrypoint de FleetView: abre una ventana kitty con una sesion tmux (socket aislado -L fleet) de dos panes (TUI fleetview a la izquierda, claude --dangerously-skip-permissions a la derecha) para centralizar la flota de Claudes. Instala atajos alt+flechas/alt+enter/alt+n que controlan la TUI desde cualquier pane, y fija el ancho del sidebar con hooks."
|
||||
signature: "launch_fleetclaude [--cwd <dir>] [--bin <path>] [--session <name>] [--reuse] [--cols <n>]"
|
||||
description: "Entrypoint de FleetView: abre una ventana kitty con una sesion tmux (socket aislado por perfil) de dos panes (TUI fleetview a la izquierda, claude --dangerously-skip-permissions a la derecha) para centralizar la flota de Claudes. Soporta PERFILES multiples: sin --session/--reuse cada invocacion abre un perfil nuevo (fleet, fleet2, fleet3, ...) con su propia flota; inyecta FLEET_SOCKET/FLEET_SESSION a la TUI para que cada panel vea solo sus Claudes. Instala atajos alt+flechas/alt+enter/alt+n que controlan la TUI desde cualquier pane, y fija el ancho del sidebar con hooks."
|
||||
tags: [claude-fleet, infra, kitty, tmux, claude, fleetview, launcher]
|
||||
params:
|
||||
- name: --cwd
|
||||
@@ -14,7 +14,9 @@ params:
|
||||
- name: --bin
|
||||
desc: "Ruta al binario de la TUI fleetview que corre en el pane izquierdo. Opcional. Default: <repo>/apps/fleetview/fleetview. Si no es ejecutable, el pane izquierdo muestra un mensaje de como compilarla y deja una shell viva."
|
||||
- name: --session
|
||||
desc: "Nombre de la sesion tmux a crear o reutilizar. Opcional. Default: fleet. La funcion es idempotente sobre este nombre."
|
||||
desc: "Fija el perfil (socket+sesion tmux comparten nombre) por nombre exacto; reutiliza el existente si ya vive (idempotente sobre ese nombre). Opcional. Sin esta opcion, el perfil se elige automaticamente (primer nombre libre de la secuencia fleet, fleet2, ...)."
|
||||
- name: --reuse
|
||||
desc: "Reattach al perfil principal 'fleet' en vez de abrir uno nuevo. Opcional. Recupera el comportamiento idempotente clasico (volver a invocar NO duplica la flota, reusa la existente)."
|
||||
- name: --cols
|
||||
desc: "Ancho en columnas del pane izquierdo (la TUI). Opcional. Default: 40."
|
||||
output: "Crea/reutiliza una sesion tmux detached con dos panes y lanza una ventana kitty 'FleetView' adjunta a ella, desacoplada del shell padre (setsid). Imprime el estado por stdout. Sin valor de retorno; exit 0 en exito."
|
||||
@@ -36,17 +38,22 @@ file_path: "bash/functions/infra/launch_fleetclaude.sh"
|
||||
# Via fn run (resuelve por nombre o ID):
|
||||
fn run launch_fleetclaude
|
||||
|
||||
# Directo, con cwd explicito:
|
||||
launch_fleetclaude --cwd ~/fn_registry
|
||||
# Perfil nuevo automatico (fleet la 1a vez; fleet2, fleet3, ... si ya hay uno):
|
||||
launch_fleetclaude
|
||||
|
||||
# Sesion y ancho de pane personalizados:
|
||||
launch_fleetclaude --session fleet --cols 50
|
||||
# Reattach a la flota principal 'fleet' (comportamiento idempotente clasico):
|
||||
launch_fleetclaude --reuse
|
||||
|
||||
# Perfil con nombre fijo y ancho de pane personalizado:
|
||||
launch_fleetclaude --session trabajo --cols 50
|
||||
```
|
||||
|
||||
Tras invocarlo aparece una ventana kitty titulada `FleetView` con dos panes
|
||||
lado a lado: a la izquierda la TUI `fleetview`, a la derecha una sesion de
|
||||
`claude --dangerously-skip-permissions`. Volver a invocarlo NO duplica la
|
||||
sesion: reusa la existente y solo abre otra kitty adjunta.
|
||||
Tras invocarlo aparece una ventana kitty titulada `FleetView (<perfil>)` con dos
|
||||
panes lado a lado: a la izquierda la TUI `fleetview`, a la derecha una sesion de
|
||||
`claude --dangerously-skip-permissions`. Cada perfil es un socket+sesion tmux
|
||||
aislados con su propia flota: puedes tener varias FleetView abiertas a la vez.
|
||||
Por defecto, volver a invocarlo abre un perfil NUEVO (no reusa); usa `--reuse`
|
||||
o `--session <nombre>` para volver a una flota concreta.
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
@@ -57,9 +64,23 @@ al retomar el trabajo en el repo `fn_registry`.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- **Idempotencia tmux**: si la sesion `<session>` (default `fleet`) ya existe,
|
||||
NO se recrea el layout; solo se abre una kitty nueva adjunta a la misma
|
||||
sesion. Para empezar de cero: `tmux kill-session -t fleet` antes de invocar.
|
||||
- **Perfiles multiples (default = perfil nuevo)**: sin `--session` ni `--reuse`,
|
||||
cada invocacion abre un perfil NUEVO usando el primer nombre libre de la
|
||||
secuencia `fleet`, `fleet2`, `fleet3`, ... (socket+sesion tmux comparten el
|
||||
nombre del perfil). Asi puedes tener varias FleetView abiertas a la vez, cada
|
||||
una con su flota independiente. Un perfil cerrado libera su nombre: tras matar
|
||||
`fleet`, el siguiente lanzamiento vuelve a `fleet`. Para reattach a una flota
|
||||
concreta: `--reuse` (principal `fleet`) o `--session <nombre>` (idempotente
|
||||
sobre ese nombre, reusa el layout si ya vive).
|
||||
- **Perfil ↔ TUI por entorno**: el launcher inyecta `FLEET_SOCKET`/`FLEET_SESSION`
|
||||
al pane de la TUI (y los fija en el server con `set-environment -g`, para que
|
||||
`respawn-pane` de alt+R y los Claude nuevos hereden el socket). `main.go` los
|
||||
lee con fallback a `fleet`. Por eso cada panel ve SOLO los Claude de su perfil
|
||||
(cruza la lista del sistema con los panes de su socket).
|
||||
- **Dentro de tmux abre ventana nueva**: si invocas `fleetclaude` desde dentro de
|
||||
una sesion tmux (`$TMUX` definido), NO hace `attach` anidado (rompe / avisa de
|
||||
nesting); cae a la ruta kitty y abre una ventana nueva. Fuera de tmux y con
|
||||
TTY, reutiliza la terminal actual con `exec tmux attach`.
|
||||
- **kitty detached (setsid)**: la ventana se lanza con `setsid ... &` para
|
||||
sobrevivir al cierre de la terminal que la invoco. No bloquea al shell padre.
|
||||
- **`exec` en los panes**: tanto la TUI como `claude` se lanzan con `exec`, asi
|
||||
@@ -70,10 +91,11 @@ al retomar el trabajo en el repo `fn_registry`.
|
||||
`<repo>/apps/fleetview/fleetview`. Si ese binario no existe, el pane izquierdo
|
||||
muestra `cd apps/fleetview && go build -o fleetview .` en lugar de fallar en
|
||||
silencio. Compila la TUI antes para el flujo completo.
|
||||
- **Socket tmux aislado (`-L fleet`)**: toda la sesion vive en un server tmux
|
||||
propio, separado del tmux por defecto del usuario. Asi los atajos `bind -n`
|
||||
NO afectan otras sesiones (ej. una sesion `mobile-1` del movil) y matar el
|
||||
server fleet no toca nada mas: `tmux -L fleet kill-server`.
|
||||
- **Socket tmux aislado por perfil (`-L <perfil>`)**: cada perfil vive en su
|
||||
propio server tmux (socket = nombre del perfil), separado del tmux por defecto
|
||||
del usuario y de los demas perfiles. Asi los atajos `bind -n` NO afectan otras
|
||||
sesiones (ej. una sesion `mobile-1` del movil) y matar un perfil no toca los
|
||||
otros: `tmux -L <perfil> kill-server` (o `alt+q` dentro de la TUI).
|
||||
- **Atajos en el socket, NO en kitty.conf**: instala `bind -n` para
|
||||
`alt+flechas` (mover el cursor de la TUI), `alt+enter` (conmutar al Claude
|
||||
seleccionado) y `alt+n` (abrir Claude nuevo). Son bindings de tmux que
|
||||
@@ -91,6 +113,15 @@ al retomar el trabajo en el repo `fn_registry`.
|
||||
|
||||
## Capability growth log
|
||||
|
||||
- v1.4.0 (2026-06-18) — **perfiles multiples**. Socket+sesion tmux ya no son el
|
||||
fijo `fleet`: cada perfil tiene los suyos (mismo nombre). Sin `--session`/
|
||||
`--reuse`, cada invocacion abre el primer perfil libre (`fleet`, `fleet2`, ...),
|
||||
asi abrir FleetView con uno ya abierto arranca otra flota en vez de reusarla.
|
||||
Nuevo flag `--reuse` para el reattach idempotente clasico. El launcher inyecta
|
||||
`FLEET_SOCKET`/`FLEET_SESSION` (env + `set-environment -g`) y `main.go` de
|
||||
`fleetview` los lee (fallback `fleet`), de modo que cada panel ve solo su flota.
|
||||
Titulo de kitty `FleetView (<perfil>)`. Guard anti-nesting: invocado dentro de
|
||||
tmux abre ventana kitty nueva en vez de `attach` anidado.
|
||||
- v1.3.2 (2026-06-17) — targeting de panes por **pane ID** (`%0`/`%1`) en vez de
|
||||
por indice (`console.0`). Antes fallaba con `can't find pane: 0` en hosts cuyo
|
||||
`~/.tmux.conf` define `base-index 1`/`pane-base-index 1` (el socket `-L fleet`
|
||||
|
||||
@@ -21,7 +21,9 @@ launch_fleetclaude() {
|
||||
local bin=""
|
||||
local session="fleet"
|
||||
local cols=52
|
||||
local T="tmux -L fleet" # socket tmux aislado: no toca el tmux normal del usuario
|
||||
local explicit_session=0 # 1 si el usuario pasó --session <name> a mano
|
||||
local reuse=0 # 1 si el usuario pidió --reuse (reattach al perfil principal)
|
||||
local T="" # socket tmux aislado; se fija al resolver el perfil
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Parseo de argumentos
|
||||
@@ -39,6 +41,10 @@ launch_fleetclaude() {
|
||||
--session)
|
||||
shift
|
||||
session="${1:-}"
|
||||
explicit_session=1
|
||||
;;
|
||||
--reuse)
|
||||
reuse=1
|
||||
;;
|
||||
--cols)
|
||||
shift
|
||||
@@ -51,19 +57,28 @@ Uso: launch_fleetclaude [opciones]
|
||||
Abre una ventana kitty con una sesion tmux de dos panes: la TUI fleetview a la
|
||||
izquierda y 'claude --dangerously-skip-permissions' a la derecha.
|
||||
|
||||
Cada PERFIL de FleetView es un socket+sesion tmux aislados (su propia flota de
|
||||
Claudes). Sin --session ni --reuse, cada invocacion abre un perfil NUEVO: usa
|
||||
el primer nombre libre de la secuencia fleet, fleet2, fleet3, ... Asi puedes
|
||||
tener varias FleetView abiertas a la vez, cada una con su flota independiente.
|
||||
|
||||
Opciones:
|
||||
--cwd <dir> Directorio de trabajo de los panes.
|
||||
Default: raiz del repo fn_registry (derivada dinamicamente).
|
||||
--bin <path> Ruta al binario de la TUI fleetview.
|
||||
Default: <repo>/apps/fleetview/fleetview
|
||||
--session <name> Nombre de la sesion tmux. Default: fleet.
|
||||
--session <name> Fija el perfil (socket+sesion) por nombre exacto; reutiliza
|
||||
el existente si ya esta vivo. Sin esta opcion, perfil auto.
|
||||
--reuse Reattach al perfil principal 'fleet' en vez de abrir uno
|
||||
nuevo (vuelve al comportamiento idempotente clasico).
|
||||
--cols <n> Ancho (columnas) del pane izquierdo. Default: 40.
|
||||
-h, --help Muestra esta ayuda.
|
||||
|
||||
Ejemplos:
|
||||
launch_fleetclaude
|
||||
launch_fleetclaude --cwd ~/fn_registry
|
||||
launch_fleetclaude --session fleet --cols 50
|
||||
launch_fleetclaude # perfil nuevo (fleet, luego fleet2, ...)
|
||||
launch_fleetclaude --reuse # reattach a la flota principal 'fleet'
|
||||
launch_fleetclaude --session trabajo # perfil con nombre fijo 'trabajo'
|
||||
launch_fleetclaude --cwd ~/fn_registry --cols 50
|
||||
USAGE
|
||||
return 0
|
||||
;;
|
||||
@@ -111,6 +126,34 @@ USAGE
|
||||
echo "launch_fleetclaude: tmux no esta instalado." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
# -----------------------------------------------------------------------
|
||||
# Resolver el PERFIL (socket+sesion tmux comparten nombre).
|
||||
#
|
||||
# - --session <name> -> usa ese nombre exacto (reutiliza si ya vive).
|
||||
# - --reuse -> usa 'fleet' (el perfil principal), idempotente.
|
||||
# - sin nada -> perfil NUEVO: primer nombre libre de la secuencia
|
||||
# fleet, fleet2, fleet3, ... Asi abrir FleetView con
|
||||
# uno ya abierto arranca otra flota, no la reusa.
|
||||
#
|
||||
# "Libre" = no hay un server tmux con esa sesion (has-session falla). Un
|
||||
# perfil cerrado libera su nombre, asi que tras cerrar 'fleet' el siguiente
|
||||
# lanzamiento vuelve a 'fleet'.
|
||||
# -----------------------------------------------------------------------
|
||||
if [[ "$explicit_session" -eq 0 && "$reuse" -eq 0 ]]; then
|
||||
local base="$session" n=1 cand
|
||||
while :; do
|
||||
if [[ "$n" -eq 1 ]]; then cand="$base"; else cand="${base}${n}"; fi
|
||||
if ! tmux -L "$cand" has-session -t "$cand" 2>/dev/null; then
|
||||
session="$cand"
|
||||
break
|
||||
fi
|
||||
n=$((n + 1))
|
||||
done
|
||||
echo "launch_fleetclaude: perfil nuevo '$session'."
|
||||
fi
|
||||
# A partir de aqui el socket aislado es el del perfil resuelto.
|
||||
T="tmux -L $session"
|
||||
# Nota: kitty NO se exige aqui. La ruta interactiva (TTY) reutiliza la
|
||||
# terminal actual con `exec tmux attach` y no necesita kitty. Solo la
|
||||
# ruta sin-TTY (abrir ventana nueva con setsid kitty) lo requiere, y ahi
|
||||
@@ -121,9 +164,13 @@ USAGE
|
||||
# - Si el binario fleetview existe -> ejecutarlo (exec, sin shell colgado).
|
||||
# - Si NO existe -> mensaje claro + shell interactiva (no falla en silencio).
|
||||
# -----------------------------------------------------------------------
|
||||
# La TUI necesita saber a qué perfil pertenece: se lo pasamos por entorno
|
||||
# (FLEET_SOCKET/FLEET_SESSION), que main.go lee con fallback a "fleet".
|
||||
local envpfx
|
||||
envpfx="FLEET_SOCKET=$(printf '%q' "$session") FLEET_SESSION=$(printf '%q' "$session")"
|
||||
local left_cmd
|
||||
if [[ -x "$bin" ]]; then
|
||||
left_cmd="exec $(printf '%q' "$bin")"
|
||||
left_cmd="$envpfx exec $(printf '%q' "$bin")"
|
||||
else
|
||||
# Fallback claro: instruye como compilar la TUI y deja una shell viva.
|
||||
left_cmd="echo 'fleetview no compilado: cd apps/fleetview && go build -o fleetview .'; exec \"\$SHELL\""
|
||||
@@ -181,8 +228,14 @@ USAGE
|
||||
$T bind -n M-r send-keys -t "$left_pane" r
|
||||
$T bind -n M-u send-keys -t "$left_pane" u
|
||||
$T bind -n M-h send-keys -t "$left_pane" h
|
||||
$T bind -n M-R send-keys -t "$left_pane" R
|
||||
$T bind -n M-Left send-keys -t "$left_pane" Escape
|
||||
$T bind -n M-q send-keys -t "$left_pane" Q
|
||||
# Entorno del perfil en el server tmux: respawn-pane (alt+R, recompila la TUI)
|
||||
# y los Claude nuevos heredan FLEET_SOCKET/FLEET_SESSION para apuntar al
|
||||
# socket correcto aunque no sea el default "fleet".
|
||||
$T set-environment -g FLEET_SOCKET "$session"
|
||||
$T set-environment -g FLEET_SESSION "$session"
|
||||
# Raton: enruta clicks/rueda al pane bajo el cursor; la TUI los interpreta.
|
||||
$T set -g mouse on
|
||||
# Al salir un Claude (exit / Ctrl-D / kill), cerrar su window en vez de
|
||||
@@ -207,24 +260,25 @@ USAGE
|
||||
# (Mismo patron que reboot_all_claudes para relanzar terminales.)
|
||||
# -----------------------------------------------------------------------
|
||||
# Adjuntar la sesion:
|
||||
# - Si se invoca desde una terminal interactiva, convertir ESA terminal en
|
||||
# el panel FleetView (exec reemplaza el proceso; al hacer detach vuelve la
|
||||
# - Terminal interactiva y FUERA de tmux: convertir ESA terminal en el
|
||||
# panel FleetView (exec reemplaza el proceso; al hacer detach vuelve la
|
||||
# shell). Asi `fleetclaude` no abre otra ventana: usa la actual.
|
||||
# - Si NO hay TTY (atajo de escritorio, cron, script), abrir una ventana
|
||||
# kitty nueva desacoplada (setsid) como antes.
|
||||
if [ -t 0 ] && [ -t 1 ]; then
|
||||
exec tmux -L fleet attach -t "$session"
|
||||
# - DENTRO de tmux (o sin TTY: atajo de escritorio, cron, script): abrir
|
||||
# una ventana kitty nueva desacoplada (setsid). No hacemos `attach`
|
||||
# anidado dentro de otra sesion tmux (rompe / da el warning de nesting).
|
||||
if [ -t 0 ] && [ -t 1 ] && [ -z "${TMUX:-}" ]; then
|
||||
exec tmux -L "$session" attach -t "$session"
|
||||
fi
|
||||
# Ruta sin-TTY: necesitamos kitty para abrir la ventana nueva.
|
||||
# Ruta ventana-nueva: necesitamos kitty para abrirla.
|
||||
if ! command -v kitty >/dev/null 2>&1; then
|
||||
echo "launch_fleetclaude: kitty no esta instalado (necesario solo sin TTY)." >&2
|
||||
echo "launch_fleetclaude: lanzalo desde una terminal interactiva, o instala kitty." >&2
|
||||
echo "launch_fleetclaude: kitty no esta instalado (necesario para abrir ventana nueva)." >&2
|
||||
echo "launch_fleetclaude: lanzalo desde una terminal interactiva fuera de tmux, o instala kitty." >&2
|
||||
return 1
|
||||
fi
|
||||
setsid kitty --title "FleetView" -e tmux -L fleet attach -t "$session" </dev/null >/dev/null 2>&1 &
|
||||
setsid kitty --title "FleetView ($session)" -e tmux -L "$session" attach -t "$session" </dev/null >/dev/null 2>&1 &
|
||||
disown 2>/dev/null || true
|
||||
|
||||
echo "launch_fleetclaude: ventana kitty 'FleetView' adjunta a la sesion tmux '$session'."
|
||||
echo "launch_fleetclaude: ventana kitty 'FleetView ($session)' adjunta al perfil '$session'."
|
||||
return 0
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,55 @@
|
||||
---
|
||||
name: open_doc_onlyoffice
|
||||
kind: function
|
||||
lang: bash
|
||||
domain: infra
|
||||
version: 1.0.0
|
||||
purity: impure
|
||||
signature: "open_doc_onlyoffice <ruta_archivo> [--restart]"
|
||||
description: "Abre un documento ofimático (xlsx, docx, pptx, csv, ods, odt, ...) con OnlyOffice Desktop Editors, desacoplado del shell (setsid + background). Localiza el binario por PATH sin hardcodear rutas. Flag --restart cierra toda la app OnlyOffice y la relanza para forzar la recarga desde disco de un archivo regenerado (OnlyOffice cachea en memoria la versión vieja de los documentos abiertos)."
|
||||
tags:
|
||||
- onlyoffice
|
||||
- desktop
|
||||
- office
|
||||
- open
|
||||
uses_functions: []
|
||||
uses_types: []
|
||||
returns: []
|
||||
error_type: error_go_core
|
||||
params:
|
||||
- name: ruta_archivo
|
||||
desc: "Ruta (relativa o absoluta) del documento ofimático a abrir. Debe existir."
|
||||
- name: --restart
|
||||
desc: "Opcional. Si se pasa, cierra TODA la instancia de OnlyOffice (pkill -x DesktopEditors) antes de relanzar, forzando la recarga desde disco. Cierra cualquier otro documento abierto: usar solo si ninguno tiene cambios sin guardar."
|
||||
output: "Imprime la ruta absoluta abierta. Exit 0 si lanza OnlyOffice; exit 1 si el archivo no existe o el binario no está en PATH; exit 2 en error de uso."
|
||||
file_path: bash/functions/infra/open_doc_onlyoffice.sh
|
||||
---
|
||||
|
||||
## Ejemplo
|
||||
|
||||
```bash
|
||||
# Abrir un documento (lo enfoca si OnlyOffice ya está corriendo)
|
||||
fn run open_doc_onlyoffice ~/Desktop/negocio_dashboards.xlsx
|
||||
|
||||
# Tras regenerar el archivo en disco, forzar que OnlyOffice lo recargue
|
||||
fn run open_doc_onlyoffice ~/Desktop/negocio_dashboards.xlsx --restart
|
||||
```
|
||||
|
||||
## Cuando usarla
|
||||
|
||||
Cuando necesites abrir o mostrar al usuario un documento ofimático (`.xlsx`, `.docx`, `.pptx`, `.csv`, `.ods`, `.odt`) en su escritorio. Es la forma canónica de abrir documentos en este equipo: el usuario usa OnlyOffice, nunca LibreOffice. Usa `--restart` cuando acabas de regenerar un archivo que probablemente ya está abierto y OnlyOffice muestra la versión cacheada en memoria.
|
||||
|
||||
## Gotchas
|
||||
|
||||
- OnlyOffice es **instancia única**: lanzarlo con un archivo ya abierto reenfoca la pestaña existente con la versión cacheada en memoria, NO recarga desde disco. Por eso existe `--restart`.
|
||||
- `--restart` cierra **toda** la app (`pkill -x DesktopEditors`), no solo la pestaña del archivo. Cualquier otro documento abierto se cierra. No usar si hay documentos con cambios sin guardar.
|
||||
- No hay forma por CLI de cerrar/recargar una sola pestaña: o se acepta la versión cacheada, o se reinicia la app entera.
|
||||
- Usa `setsid` + `&` para que el editor sobreviva al proceso que lo invoca (no muere al cerrar la terminal/sesión).
|
||||
- Localiza el binario con `command -v onlyoffice-desktopeditors`; el proceso real subyacente es `/opt/onlyoffice/desktopeditors/DesktopEditors`.
|
||||
|
||||
## example
|
||||
|
||||
```bash
|
||||
open_doc_onlyoffice ~/Desktop/negocio_dashboards.xlsx
|
||||
open_doc_onlyoffice ~/Desktop/negocio_dashboards.xlsx --restart # fuerza recarga desde disco
|
||||
```
|
||||
@@ -0,0 +1,61 @@
|
||||
#!/usr/bin/env bash
|
||||
# open_doc_onlyoffice — abre un documento ofimático con OnlyOffice Desktop Editors.
|
||||
#
|
||||
# Uso:
|
||||
# open_doc_onlyoffice <ruta_archivo> [--restart]
|
||||
#
|
||||
# Lanza el editor desacoplado del shell (setsid + background) para que sobreviva
|
||||
# al proceso que lo invoca. Localiza el binario por PATH, sin hardcodear rutas.
|
||||
#
|
||||
# --restart cierra toda la instancia de OnlyOffice antes de relanzar, para forzar
|
||||
# la recarga desde disco de un archivo que se regeneró (OnlyOffice mantiene en
|
||||
# memoria la versión vieja de los documentos ya abiertos).
|
||||
set -euo pipefail
|
||||
|
||||
usage() {
|
||||
echo "uso: open_doc_onlyoffice <ruta_archivo> [--restart]" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
[ $# -ge 1 ] || usage
|
||||
|
||||
doc=""
|
||||
restart=0
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--restart) restart=1 ;;
|
||||
-h|--help) usage ;;
|
||||
*) doc="$arg" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$doc" ] || usage
|
||||
|
||||
if [ ! -f "$doc" ]; then
|
||||
echo "error: archivo no encontrado: $doc" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
bin="$(command -v onlyoffice-desktopeditors || true)"
|
||||
if [ -z "$bin" ]; then
|
||||
echo "error: onlyoffice-desktopeditors no esta en PATH" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Ruta absoluta para que OnlyOffice no dependa del directorio de trabajo.
|
||||
doc_abs="$(readlink -f "$doc")"
|
||||
|
||||
if [ "$restart" -eq 1 ]; then
|
||||
# Cierra la app entera para descartar la copia en memoria de los documentos.
|
||||
# pkill -x sobre el comm exacto del proceso real (no -f, para no auto-matar
|
||||
# el propio script si su ruta contiene el patrón).
|
||||
pkill -x DesktopEditors 2>/dev/null || true
|
||||
# Espera (máx ~5s) a que el proceso principal termine antes de relanzar.
|
||||
for _ in $(seq 1 25); do
|
||||
pgrep -x DesktopEditors >/dev/null 2>&1 || break
|
||||
sleep 0.2
|
||||
done
|
||||
fi
|
||||
|
||||
setsid "$bin" "$doc_abs" >/dev/null 2>&1 &
|
||||
echo "abierto en OnlyOffice: $doc_abs"
|
||||
Reference in New Issue
Block a user