fix: attach_command from one builder, no kill/start_command, row-less stop, one herdr name rule, registry format 2 (Issue #71) #73
No files matched your search
+43
-50
@@ -33,6 +33,11 @@ MAM_VERSION="4.10.1"
|
||||
export MAM_VERSION
|
||||
AGENT_SESSIONS_YAML="${AGENT_SESSIONS_YAML:-$WORKSPACE_ROOT/.mam/agent-sessions.yaml}"
|
||||
|
||||
# The one bash form of lib_py.agents.sanitize.sanitize_herdr_agent_name
|
||||
# (Issue #71). The herdr shim gets this same definition: _init_herdr_isolation
|
||||
# writes it in with `declare -f`. A long name needs its SHA-1 suffix; with no
|
||||
# shasum, sha1sum, openssl or python3 to make it, this fails (stderr, nonzero)
|
||||
# rather than return a name herdr never registered.
|
||||
_sanitize_herdr_agent_name() {
|
||||
local n="$1"
|
||||
if [ -z "$n" ]; then
|
||||
@@ -46,16 +51,21 @@ _sanitize_herdr_agent_name() {
|
||||
local h=""
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
h=$(printf '%s' "$s" | shasum 2>/dev/null | awk '{print $1}' | cut -c 1-8)
|
||||
elif command -v sha1sum >/dev/null 2>&1; then
|
||||
fi
|
||||
if [ "${#h}" -ne 8 ] && command -v sha1sum >/dev/null 2>&1; then
|
||||
h=$(printf '%s' "$s" | sha1sum 2>/dev/null | awk '{print $1}' | cut -c 1-8)
|
||||
elif command -v openssl >/dev/null 2>&1; then
|
||||
fi
|
||||
if [ "${#h}" -ne 8 ] && command -v openssl >/dev/null 2>&1; then
|
||||
h=$(printf '%s' "$s" | openssl sha1 2>/dev/null | awk '{print $NF}' | cut -c 1-8)
|
||||
fi
|
||||
if [ -n "$h" ]; then
|
||||
s="${s:0:23}-$h"
|
||||
else
|
||||
s="${s:0:32}"
|
||||
if [ "${#h}" -ne 8 ] && command -v python3 >/dev/null 2>&1; then
|
||||
h=$(python3 -c 'import hashlib,sys; print(hashlib.sha1(sys.argv[1].encode("utf-8")).hexdigest()[:8])' "$s" 2>/dev/null || true)
|
||||
fi
|
||||
if [ "${#h}" -ne 8 ]; then
|
||||
echo "ERROR: cannot make the herdr name of '$n': no shasum, sha1sum, openssl or python3 to hash it" >&2
|
||||
return 1
|
||||
fi
|
||||
s="${s:0:23}-${h}"
|
||||
fi
|
||||
printf '%s\n' "$s"
|
||||
}
|
||||
@@ -155,8 +165,10 @@ _init_herdr_isolation() {
|
||||
fi
|
||||
|
||||
local tmp_file="$wrapper_dir/herdr.tmp.$$.$RANDOM"
|
||||
cat <<'EOF' > "$tmp_file"
|
||||
#!/usr/bin/env bash
|
||||
{
|
||||
printf '%s\n' '#!/usr/bin/env bash'
|
||||
declare -f _sanitize_herdr_agent_name
|
||||
cat <<'EOF'
|
||||
# Herdr-to-Herdr translation shim wrapper
|
||||
set -euo pipefail
|
||||
|
||||
@@ -290,31 +302,6 @@ _real_herdr() {
|
||||
fi
|
||||
}
|
||||
|
||||
_sanitize_herdr_agent_name() {
|
||||
local n="$1"
|
||||
if [ -z "$n" ]; then
|
||||
printf 'agent\n'
|
||||
return 0
|
||||
fi
|
||||
local s
|
||||
s=$(echo "$n" | tr '[:upper:]' '[:lower:]' | sed -E 's/[^a-z0-9_-]/-/g')
|
||||
if [[ ! "$s" =~ ^[a-z] ]]; then s="x-$s"; fi
|
||||
if [ "${#s}" -gt 32 ]; then
|
||||
local h=""
|
||||
if command -v shasum >/dev/null 2>&1; then
|
||||
h=$(printf '%s' "$s" | shasum 2>/dev/null | awk '{print $1}' | cut -c 1-8)
|
||||
elif command -v sha1sum >/dev/null 2>&1; then
|
||||
h=$(printf '%s' "$s" | sha1sum 2>/dev/null | awk '{print $1}' | cut -c 1-8)
|
||||
elif command -v openssl >/dev/null 2>&1; then
|
||||
h=$(printf '%s' "$s" | openssl sha1 2>/dev/null | awk '{print $NF}' | cut -c 1-8)
|
||||
else
|
||||
h=$(python3 -c "import hashlib,sys; print(hashlib.sha1(sys.argv[1].encode()).hexdigest()[:8])" "$s" 2>/dev/null || echo "00000000")
|
||||
fi
|
||||
s="${s:0:23}-${h}"
|
||||
fi
|
||||
printf '%s\n' "${s:0:32}"
|
||||
}
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Workspace scoping (ISSUE-3).
|
||||
#
|
||||
@@ -441,7 +428,7 @@ sys.exit(1)
|
||||
[ -n "$resolved_ws" ] && target_ws="$resolved_ws"
|
||||
fi
|
||||
local sat pid=""
|
||||
sat=$(_sanitize_herdr_agent_name "$target")
|
||||
sat=$(_sanitize_herdr_agent_name "$target") || return 1
|
||||
|
||||
local cand get_ws
|
||||
if [ "$#" -ge 2 ]; then
|
||||
@@ -534,7 +521,7 @@ case "$cmd" in
|
||||
_resolve_herdr_target() {
|
||||
local raw="$1"
|
||||
local sat
|
||||
sat=$(_sanitize_herdr_agent_name "$raw")
|
||||
sat=$(_sanitize_herdr_agent_name "$raw") || return 1
|
||||
if [ -n "$(_herdr_agent_get_scoped "$sat" 2>/dev/null || true)" ]; then
|
||||
echo "$sat"
|
||||
return 0
|
||||
@@ -604,6 +591,15 @@ sys.exit(1)
|
||||
exit 1
|
||||
fi
|
||||
_real_herdr agent read "$tgt" "$@"
|
||||
elif [ "$sub" = "attach" ] && [ $# -ge 1 ]; then
|
||||
t="$1"
|
||||
shift
|
||||
tgt=$(_resolve_herdr_target "$t" || true)
|
||||
if [ -z "$tgt" ]; then
|
||||
echo "Error: no in-scope herdr agent for '$t'" >&2
|
||||
exit 1
|
||||
fi
|
||||
_real_herdr agent attach "$tgt" "$@"
|
||||
else
|
||||
_real_herdr agent "$sub" "$@"
|
||||
fi
|
||||
@@ -623,19 +619,15 @@ sys.exit(1)
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
if [ -n "$(_herdr_agent_get_scoped "$(_sanitize_herdr_agent_name "$sess")" 2>/dev/null || true)" ] \
|
||||
ssess=$(_sanitize_herdr_agent_name "$sess") || exit 1
|
||||
if [ -n "$(_herdr_agent_get_scoped "$ssess" 2>/dev/null || true)" ] \
|
||||
|| [ -n "$(_herdr_agent_get_scoped "$sess" 2>/dev/null || true)" ]; then
|
||||
exit 0
|
||||
fi
|
||||
if _real_herdr agent list 2>/dev/null | TARGET_NAME="$sess" TARGET_WS="$(_herdr_ws_scope)" python3 -c '
|
||||
if _real_herdr agent list 2>/dev/null | TARGET_NAME="$sess" TARGET_SAN="$ssess" TARGET_WS="$(_herdr_ws_scope)" python3 -c '
|
||||
import sys, json, os
|
||||
try:
|
||||
sys.path.insert(0, os.path.abspath(".agents/skills"))
|
||||
from lib_py.agents.sanitize import sanitize_herdr_agent_name
|
||||
except Exception:
|
||||
sanitize_herdr_agent_name = lambda s: s.lower()[:32] if s else "agent"
|
||||
tn = os.environ.get("TARGET_NAME", "")
|
||||
stn = sanitize_herdr_agent_name(tn)
|
||||
stn = os.environ.get("TARGET_SAN", "")
|
||||
tws = os.environ.get("TARGET_WS", "")
|
||||
try:
|
||||
d = json.loads(sys.stdin.read())
|
||||
@@ -682,6 +674,7 @@ sys.exit(1)
|
||||
*) run_cmd="$1"; shift ;;
|
||||
esac
|
||||
done
|
||||
agent_name=$(_sanitize_herdr_agent_name "$name") || exit 1
|
||||
if [ -z "$run_cmd" ]; then
|
||||
run_cmd="${SHELL:-/bin/bash}"
|
||||
fi
|
||||
@@ -896,7 +889,6 @@ except Exception:
|
||||
res=""
|
||||
success=0
|
||||
backoffs=(0.5 1 2)
|
||||
agent_name=$(_sanitize_herdr_agent_name "$name")
|
||||
for i in $(seq 0 2); do
|
||||
if [ -n "$final_cmd" ]; then
|
||||
res=$(eval "_real_herdr agent start \"$agent_name\" --kind \"$kind\" --pane \"$target_pane\" -- $final_cmd" 2>&1 || true)
|
||||
@@ -967,7 +959,7 @@ except Exception:
|
||||
# `herdr session list`), not individual agent panes — `session stop/delete
|
||||
# "$sess"` with a MAM session name always fails (silently, via `|| true`).
|
||||
# Resolve the real pane_id via `agent get` and close just that pane instead.
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess")
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess") || exit 1
|
||||
pane_id=$(_resolve_herdr_pane_id "$sess" 2>/dev/null || true)
|
||||
if [ -n "$pane_id" ]; then
|
||||
_real_herdr pane close "$pane_id" >/dev/null 2>&1 || true
|
||||
@@ -1002,7 +994,7 @@ except Exception:
|
||||
# there is no top-level "pane" key, and no "pid" field at all (only
|
||||
# "pane_id", herdr's own wN:pN identifier). The real OS pid requires a
|
||||
# second call to `pane process-info --pane <pane_id>`.
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess")
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess") || exit 1
|
||||
info=$(_real_herdr agent get "$agent_target" 2>/dev/null || _real_herdr agent get "$sess" 2>/dev/null || true)
|
||||
pane_id="" cwd="" cmd=""
|
||||
if [ -n "$info" ]; then
|
||||
@@ -1072,7 +1064,7 @@ except Exception:
|
||||
*) shift ;;
|
||||
esac
|
||||
done
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess")
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess") || exit 1
|
||||
pane_id=$(_resolve_herdr_pane_id "$sess" 2>/dev/null || true)
|
||||
if [ -n "$pane_id" ]; then
|
||||
_real_herdr pane read "$pane_id" --source visible --lines 100 2>/dev/null \
|
||||
@@ -1103,7 +1095,7 @@ except Exception:
|
||||
fi
|
||||
# `pane send-keys` requires a real pane_id ("wN:pN"), not an agent name —
|
||||
# resolve it via `agent get` first (agent-level commands accept names).
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess")
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess") || exit 1
|
||||
pane_id=$(_resolve_herdr_pane_id "$sess" 2>/dev/null || true)
|
||||
if [ -n "$pane_id" ]; then
|
||||
_real_herdr pane send-keys "$pane_id" "$key" >/dev/null 2>&1 || true
|
||||
@@ -1387,6 +1379,7 @@ for name, pid in rows:
|
||||
;;
|
||||
esac
|
||||
EOF
|
||||
} > "$tmp_file"
|
||||
chmod +x "$tmp_file" 2>/dev/null || true
|
||||
mv -f "$tmp_file" "$wrapper_dir/herdr" 2>/dev/null || rm -f "$tmp_file" 2>/dev/null || true
|
||||
if [[ ":$PATH:" != *":$wrapper_dir:"* ]]; then
|
||||
@@ -2787,7 +2780,7 @@ wait_for_agent_idle() {
|
||||
fi
|
||||
|
||||
local target
|
||||
target=$(_sanitize_herdr_agent_name "$sess")
|
||||
target=$(_sanitize_herdr_agent_name "$sess") || return 3
|
||||
|
||||
local raw_status="" agent_st=""
|
||||
raw_status="$(_sks_herdr agent get "$target" 2>/dev/null || _sks_herdr agent get "$sess" 2>/dev/null || true)"
|
||||
@@ -2912,7 +2905,7 @@ send_keys_safe() {
|
||||
fi
|
||||
|
||||
local agent_target
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess")
|
||||
agent_target=$(_sanitize_herdr_agent_name "$sess") || return 1
|
||||
|
||||
# Busy gate: wait up to SKS_BUSY_TIMEOUT only if wait_for_agent_idle returned 3 (unknown)
|
||||
if [ "${_SKS_IDLE_RC:-0}" = "3" ]; then
|
||||
|
||||
@@ -895,8 +895,8 @@ finalize_unfinished() {
|
||||
FINAL_JOB_STATUS="$final"
|
||||
if [ "$step" = "probe" ]; then
|
||||
local sess="${target_sess#herdr:}" target_agent diag="no"
|
||||
target_agent=$(_sanitize_herdr_agent_name "$sess")
|
||||
_within_deadline "$deadline" _agent_still_working "$sess" "$target_agent" && diag="yes"
|
||||
target_agent=$(_sanitize_herdr_agent_name "$sess") || target_agent=""
|
||||
[ -n "$target_agent" ] && _within_deadline "$deadline" _agent_still_working "$sess" "$target_agent" && diag="yes"
|
||||
echo "liveness: no wire event for ${IDLE_TIMEOUT}s on job $job_id; herdr/pane says busy=$diag (diagnostic only, not evidence)" >&2
|
||||
fi
|
||||
# The answer comes back on fd 3: stdout carries registry.py's close output.
|
||||
|
||||
Reference in new issue
Block a user