diff --git a/scripts/servidor/claude-caja.sh b/scripts/servidor/claude-caja.sh index a369339f..7d871573 100755 --- a/scripts/servidor/claude-caja.sh +++ b/scripts/servidor/claude-caja.sh @@ -107,13 +107,15 @@ _proy="${PROY:-$PWD}" [ -d "$_proy" ] || _proy=/opt/takana exec takana qorpa run "$_inst" -- sh -c \ - 'hogar="$1"; proy="$2"; shift 2 + 'hogar="$1"; proy="$2"; yo="$3"; shift 3 cd "$proy" 2>/dev/null || { echo "claude: adentro de la jaula no existe \"$proy\" — ¿está concedido ese directorio en la instancia? (ver grants.dirs)" >&2; exit 1; } + # `/work//bin` trae el toolchain del CORPUS (make, python3, rustc, cargo, takana…): + # musl estáticos del store, que corren dentro de la imagen glibc. Lo arma jaula-herramientas.sh. # ⚠ EL PATH DEL LAB, que ningún doc decía y muerde en el primer build (2026-09-18, reportado por # el agente de adentro): `takana build` invoca `go` y `zig` por nombre, y en la imagen de la jaula # no están. Muere con `spawn go mod vendor: No such file or directory`, que no menciona el PATH. # Viven en el lab, que ya está concedido. exec env HOME="$hogar" TERM="${TERM:-xterm-256color}" \ - PATH="/work/dev-fs/tools/go/bin:/work/dev-fs/tools/zig:$PATH" \ + PATH="/work/$yo/bin:/work/dev-fs/tools/go/bin:/work/dev-fs/tools/zig:$PATH" \ /work/claude-cli/2.1.240 "$@"' \ - _ "$_hogar" "$_proy" "$@" + _ "$_hogar" "$_proy" "$_yo" "$@" diff --git a/scripts/servidor/jaula-herramientas.sh b/scripts/servidor/jaula-herramientas.sh new file mode 100755 index 00000000..8c83ec78 --- /dev/null +++ b/scripts/servidor/jaula-herramientas.sh @@ -0,0 +1,66 @@ +#!/bin/sh +# Arma el `bin/` de la persona con las herramientas del CORPUS, para que el agente enjaulado tenga +# toolchain sin depender del gestor de paquetes de la imagen. +# +# ── POR QUÉ NO SE INSTALAN CON PACMAN (2026-09-18) ────────────────────────────────────────────── +# El agente de adentro reportó que le faltan `cargo rustc make pkg-config python3`, y la salida +# obvia —sumarlos a `packages` y `takana qorpa provision`— **hoy no funciona**: con un mapa de un +# solo id, `pacman` no puede chownear su directorio de descarga y muere en +# +# error: failed to chown temporary download directory …: Invalid argument +# +# que es exactamente la consecuencia que el aviso de qorpa viene anunciando (SDD 28 §6.55). +# +# Pero no hace falta pacman: **las herramientas ya están selladas en el store**, son musl estáticas y +# corren igual dentro de una imagen glibc — comprobado con `takana` y con `rustc`. Usarlas es además +# lo coherente con la distro: el corpus es el que provee, no Arch. +# +# `go` y `zig` no van acá: viven en el lab (`/work/dev-fs/tools`) y el lanzador ya los pone en PATH. +set -eu + +U=${1:-sergio} +BIN=${BIN:-/work/$U/bin} +install -d -o "$U" -g "$U" "$BIN" + +# Cuerpo común de los envoltorios. $1 = nombre del binario, $2 = nombre de la RECETA, $3 = extra. +# +# ⚠ El nombre de la receta se compara ENTERO, no por sufijo: el glob `/store/*-make` también casa +# con `…-cargo-make` —medido: la primera versión eligió cargo-make y `make --version` moría con un +# `exec` que no nombraba la causa—. El directorio es `-`: se le quita el hash y se +# compara. Y se elige el MÁS RECIENTE, no un hash cableado, para sobrevivir a un re-sellado. +envoltorio() { + t=$1; r=$2; pre=$3 + { + echo '#!/bin/sh' + echo "# Envoltorio: $t sale del store (musl estático, corre dentro de la imagen glibc)." + echo "b=\$(ls -dt /store/*-$r 2>/dev/null | while read -r d; do n=\${d##*/}; [ \"\${n#*-}\" = \"$r\" ] && { echo \"\$d\"; break; }; done)" + echo "[ -n \"\$b\" ] || { echo '$t: no hay artefacto -$r en /store' >&2; exit 1; }" + echo "$pre" + echo "exec \"\$b/usr/bin/$t\" \"\$@\"" + } > "$BIN/$t" + chmod 755 "$BIN/$t" +} + +for par in make:make pkgconf:pkgconf python3:python3 jq:jq rsync:rsync git:git curl:curl takana:takana; do + envoltorio "${par%%:*}" "${par##*:}" ":" +done +# rust necesita su LD_LIBRARY_PATH: la receta va con `rpath = false` (y por buenas razones, ver +# recipes/rust.toml), así que el cargador no encuentra librustc_driver por su cuenta. +for t in rustc cargo; do + envoltorio "$t" rust "export LD_LIBRARY_PATH=\"\$b/usr/lib\${LD_LIBRARY_PATH:+:\$LD_LIBRARY_PATH}\"" +done +chown -R "$U:$U" "$BIN" + +# ── CONTROL: que CORRAN, no que existan ───────────────────────────────────────────────────────── +# ⚠ Sin tubería: `cmd | head` devuelve el estado de `head`, o sea SIEMPRE 0, y la primera versión de +# este control dio por bueno un envoltorio que fallaba al `exec`. Se captura primero, se recorta después. +fallos=0 +for t in make pkgconf python3 jq rsync git curl takana rustc cargo; do + if out=$("$BIN/$t" --version 2>&1); then + printf " ✓ %-9s %s\n" "$t" "$(printf '%s' "$out" | head -1 | cut -c1-46)" + else + printf " ✗ %-9s %s\n" "$t" "$(printf '%s' "$out" | head -1 | cut -c1-60)"; fallos=$((fallos+1)) + fi +done +[ "$fallos" -eq 0 ] || { echo "==> $fallos herramienta(s) no corren" >&2; exit 1; } +echo "==> $BIN listo"