fix: fix: hire-an-agent fails — unbound user_pass, admin auth, silent repo creation failure, unauthenticated clone (#184)
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
ci/woodpecker/pr/ci Pipeline was successful
ci/woodpecker/pr/smoke-init Pipeline was successful

This commit is contained in:
Agent 2026-04-03 12:26:20 +00:00
parent 3ca62fa96d
commit 0b0e8f8608

View file

@ -2634,38 +2634,54 @@ disinto_hire_an_agent() {
echo "Step 1: Creating user '${agent_name}' (if not exists)..." echo "Step 1: Creating user '${agent_name}' (if not exists)..."
local user_exists=false local user_exists=false
local user_pass=""
local admin_pass=""
# Read admin password from .env for standalone runs (#184)
local env_file="${FACTORY_ROOT}/.env"
if [ -f "$env_file" ] && grep -q '^FORGE_ADMIN_PASS=' "$env_file" 2>/dev/null; then
admin_pass=$(grep '^FORGE_ADMIN_PASS=' "$env_file" | head -1 | cut -d= -f2-)
fi
# Get admin token early (needed for both user creation and password reset)
local admin_user="disinto-admin"
admin_pass="${admin_pass:-admin}"
local admin_token=""
admin_token=$(curl -sf -X POST \
-u "${admin_user}:${admin_pass}" \
-H "Content-Type: application/json" \
"${forge_url}/api/v1/users/${admin_user}/tokens" \
-d '{"name":"temp-token","scopes":["all"]}' 2>/dev/null \
| jq -r '.sha1 // empty') || admin_token=""
if [ -z "$admin_token" ]; then
admin_token=$(curl -sf \
-u "${admin_user}:${admin_pass}" \
"${forge_url}/api/v1/users/${admin_user}/tokens" 2>/dev/null \
| jq -r '.[0].sha1 // empty') || admin_token=""
fi
if [ -z "$admin_token" ]; then
echo " Warning: could not obtain admin token, trying FORGE_TOKEN..."
admin_token="${FORGE_TOKEN}"
fi
if curl -sf --max-time 5 "${forge_url}/api/v1/users/${agent_name}" >/dev/null 2>&1; then if curl -sf --max-time 5 "${forge_url}/api/v1/users/${agent_name}" >/dev/null 2>&1; then
user_exists=true user_exists=true
echo " User '${agent_name}' already exists" echo " User '${agent_name}' already exists"
else # Reset user password so we can get a token (#184)
# Create user using admin token user_pass="agent-$(head -c 16 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 20)"
local admin_user="disinto-admin" if curl -sf -X PATCH \
local admin_pass="${_FORGE_ADMIN_PASS:-admin}" -H "Authorization: token ${admin_token}" \
# Try to get admin token first
local admin_token
admin_token=$(curl -sf -X POST \
-u "${admin_user}:${admin_pass}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${forge_url}/api/v1/users/${admin_user}/tokens" \ "${forge_url}/api/v1/admin/users/${agent_name}" \
-d '{"name":"temp-token","scopes":["all"]}' 2>/dev/null \ -d "{\"password\":\"${user_pass}\"}" >/dev/null 2>&1; then
| jq -r '.sha1 // empty') || admin_token="" echo " Reset password for existing user '${agent_name}'"
else
if [ -z "$admin_token" ]; then echo " Warning: could not reset password for existing user" >&2
# Token might already exist — try listing
admin_token=$(curl -sf \
-u "${admin_user}:${admin_pass}" \
"${forge_url}/api/v1/users/${admin_user}/tokens" 2>/dev/null \
| jq -r '.[0].sha1 // empty') || admin_token=""
fi fi
else
if [ -z "$admin_token" ]; then # Create user using admin token (admin_token already obtained above)
echo " Warning: could not obtain admin token, trying FORGE_TOKEN..."
admin_token="${FORGE_TOKEN}"
fi
# Create the user # Create the user
local user_pass="agent-$(head -c 16 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 20)" user_pass="agent-$(head -c 16 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 20)"
if curl -sf -X POST \ if curl -sf -X POST \
-H "Authorization: token ${admin_token}" \ -H "Authorization: token ${admin_token}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
@ -2695,24 +2711,21 @@ disinto_hire_an_agent() {
echo " Repo '${agent_name}/.profile' already exists" echo " Repo '${agent_name}/.profile' already exists"
else else
# Get user token for creating repo # Get user token for creating repo
# Always try to get token using user_pass (set in Step 1 for new users, reset for existing)
local user_token="" local user_token=""
if [ "$user_exists" = true ]; then user_token=$(curl -sf -X POST \
# Try to get token for the new user -u "${agent_name}:${user_pass}" \
# Note: user_pass was set in Step 1; for existing users this will fail (unknown password) -H "Content-Type: application/json" \
user_token=$(curl -sf -X POST \ "${forge_url}/api/v1/users/${agent_name}/tokens" \
-u "${agent_name}:${user_pass}" \ -d "{\"name\":\".profile-repo-token\",\"scopes\":[\"repository\"]}" 2>/dev/null \
-H "Content-Type: application/json" \ | jq -r '.sha1 // empty') || user_token=""
"${forge_url}/api/v1/users/${agent_name}/tokens" \
-d "{\"name\":\".profile-repo-token\",\"scopes\":[\"repository\"]}" 2>/dev/null \
| jq -r '.sha1 // empty') || user_token=""
if [ -z "$user_token" ]; then if [ -z "$user_token" ]; then
# Try listing existing tokens # Try listing existing tokens
user_token=$(curl -sf \ user_token=$(curl -sf \
-u "${agent_name}:${user_pass}" \ -u "${agent_name}:${user_pass}" \
"${forge_url}/api/v1/users/${agent_name}/tokens" 2>/dev/null \ "${forge_url}/api/v1/users/${agent_name}/tokens" 2>/dev/null \
| jq -r '.[0].sha1 // empty') || user_token="" | jq -r '.[0].sha1 // empty') || user_token=""
fi
fi fi
# Fall back to admin token if user token not available # Fall back to admin token if user token not available
@ -2721,26 +2734,45 @@ disinto_hire_an_agent() {
user_token="${admin_token:-${FORGE_TOKEN}}" user_token="${admin_token:-${FORGE_TOKEN}}"
fi fi
# Create the repo # Create the repo using the user's namespace (user/repos with user_token creates in that user's namespace)
if curl -sf -X POST \ # or use admin API to create in specific user's namespace
-H "Authorization: token ${user_token}" \ local repo_created=false
-H "Content-Type: application/json" \ local create_output
"${forge_url}/api/v1/user/repos" \
-d "{\"name\":\".profile\",\"description\":\"${agent_name}'s .profile repo\",\"private\":true,\"auto_init\":false}" >/dev/null 2>&1; then if [ -n "$user_token" ]; then
echo " Created repo '${agent_name}/.profile'" # Try creating as the agent user (user token creates in that user's namespace)
else create_output=$(curl -sf -X POST \
# Try with org path
if curl -sf -X POST \
-H "Authorization: token ${user_token}" \ -H "Authorization: token ${user_token}" \
-H "Content-Type: application/json" \ -H "Content-Type: application/json" \
"${forge_url}/api/v1/orgs/${agent_name}/repos" \ "${forge_url}/api/v1/user/repos" \
-d "{\"name\":\".profile\",\"description\":\"${agent_name}'s .profile repo\",\"private\":true,\"auto_init\":false}" >/dev/null 2>&1; then -d "{\"name\":\".profile\",\"description\":\"${agent_name}'s .profile repo\",\"private\":true,\"auto_init\":false}" 2>&1) || true
echo " Created repo '${agent_name}/.profile' (in org)"
else if echo "$create_output" | grep -q '"id":\|[0-9]'; then
echo " Error: failed to create repo '${agent_name}/.profile'" >&2 repo_created=true
exit 1 echo " Created repo '${agent_name}/.profile'"
fi fi
fi fi
# If user token failed or wasn't available, use admin API to create in agent's namespace
if [ "$repo_created" = false ]; then
echo " Using admin API to create repo in ${agent_name}'s namespace"
create_output=$(curl -sf -X POST \
-H "Authorization: token ${user_token}" \
-H "Content-Type: application/json" \
"${forge_url}/api/v1/admin/users/${agent_name}/repos" \
-d "{\"name\":\".profile\",\"description\":\"${agent_name}'s .profile repo\",\"private\":true,\"auto_init\":false}" 2>&1) || true
if echo "$create_output" | grep -q '"id":\|[0-9]'; then
repo_created=true
echo " Created repo '${agent_name}/.profile' (via admin API)"
fi
fi
if [ "$repo_created" = false ]; then
echo " Error: failed to create repo '${agent_name}/.profile'" >&2
echo " Response: ${create_output}" >&2
exit 1
fi
fi fi
# Step 3: Clone repo and create initial commit # Step 3: Clone repo and create initial commit
@ -2751,23 +2783,28 @@ disinto_hire_an_agent() {
rm -rf "$clone_dir" rm -rf "$clone_dir"
mkdir -p "$clone_dir" mkdir -p "$clone_dir"
# Build clone URL (unauthenticated version for display) # Build authenticated clone URL
# Use user_token if available, otherwise fall back to FORGE_TOKEN
local clone_token="${user_token:-${FORGE_TOKEN}}"
if [ -z "$clone_token" ]; then
echo " Error: no authentication token available for cloning" >&2
exit 1
fi
local clone_url="${forge_url}/${agent_name}/.profile.git" local clone_url="${forge_url}/${agent_name}/.profile.git"
local auth_url local auth_url
auth_url=$(printf '%s' "$forge_url" | sed "s|://|://${agent_name}:${user_token:-${FORGE_TOKEN}}@|") auth_url=$(printf '%s' "$forge_url" | sed "s|://|://${agent_name}:${clone_token}@|")
clone_url="${auth_url}/.profile.git" auth_url="${auth_url}/.profile.git"
# Display unauthenticated URL (auth token only in actual git clone command) # Display unauthenticated URL (auth token only in actual git clone command)
echo " Cloning: ${forge_url}/${agent_name}/.profile.git" echo " Cloning: ${forge_url}/${agent_name}/.profile.git"
if ! git clone --quiet "$clone_url" "$clone_dir" 2>/dev/null; then # Try authenticated clone first (required for private repos)
# Try without auth (might work for public repos or with FORGE_TOKEN) if ! git clone --quiet "$auth_url" "$clone_dir" 2>/dev/null; then
clone_url="${forge_url}/${agent_name}/.profile.git" echo " Error: failed to clone repo with authentication" >&2
if ! git clone --quiet "$clone_url" "$clone_dir" 2>/dev/null; then echo " Note: Ensure the user has a valid API token with repository access" >&2
echo " Error: failed to clone repo" >&2 rm -rf "$clone_dir"
rm -rf "$clone_dir" exit 1
exit 1
fi
fi fi
# Configure git # Configure git