Merge pull request 'fix: bug: local-model agents reuse FORGE_TOKEN of main agent — wrong Forgejo identity (#563)' (#595) from fix/issue-563 into main
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
All checks were successful
ci/woodpecker/push/ci Pipeline was successful
This commit is contained in:
commit
d29a19612e
5 changed files with 165 additions and 3 deletions
23
.env.example
23
.env.example
|
|
@ -19,15 +19,32 @@ FORGE_URL=http://localhost:3000 # [CONFIG] local Forgejo instance
|
||||||
# ── Auth tokens ───────────────────────────────────────────────────────────
|
# ── Auth tokens ───────────────────────────────────────────────────────────
|
||||||
# Each agent has its own Forgejo account and API token (#747).
|
# Each agent has its own Forgejo account and API token (#747).
|
||||||
# Per-agent tokens fall back to FORGE_TOKEN if not set.
|
# Per-agent tokens fall back to FORGE_TOKEN if not set.
|
||||||
|
#
|
||||||
|
# Tokens and passwords are auto-generated by `disinto init` and stored in .env.
|
||||||
|
# Each bot user gets:
|
||||||
|
# - FORGE_TOKEN_<BOT> = API token for REST calls (user identity via /api/v1/user)
|
||||||
|
# - FORGE_PASS_<BOT> = password for git HTTP push (#361, Forgejo 11.x limitation)
|
||||||
|
#
|
||||||
|
# Local-model agents (agents-llama) use FORGE_TOKEN_LLAMA / FORGE_PASS_LLAMA
|
||||||
|
# with FORGE_BOT_USER_LLAMA=dev-qwen to ensure correct attribution (#563).
|
||||||
FORGE_TOKEN= # [SECRET] dev-bot API token (default for all agents)
|
FORGE_TOKEN= # [SECRET] dev-bot API token (default for all agents)
|
||||||
FORGE_TOKEN_DEVQWEN= # [SECRET] dev-qwen API token (for agents-llama)
|
FORGE_PASS= # [SECRET] dev-bot password for git HTTP push (#361)
|
||||||
|
FORGE_TOKEN_LLAMA= # [SECRET] dev-qwen API token (for agents-llama)
|
||||||
|
FORGE_PASS_LLAMA= # [SECRET] dev-qwen password for git HTTP push
|
||||||
FORGE_REVIEW_TOKEN= # [SECRET] review-bot API token
|
FORGE_REVIEW_TOKEN= # [SECRET] review-bot API token
|
||||||
|
FORGE_REVIEW_PASS= # [SECRET] review-bot password for git HTTP push
|
||||||
FORGE_PLANNER_TOKEN= # [SECRET] planner-bot API token
|
FORGE_PLANNER_TOKEN= # [SECRET] planner-bot API token
|
||||||
|
FORGE_PLANNER_PASS= # [SECRET] planner-bot password for git HTTP push
|
||||||
FORGE_GARDENER_TOKEN= # [SECRET] gardener-bot API token
|
FORGE_GARDENER_TOKEN= # [SECRET] gardener-bot API token
|
||||||
|
FORGE_GARDENER_PASS= # [SECRET] gardener-bot password for git HTTP push
|
||||||
FORGE_VAULT_TOKEN= # [SECRET] vault-bot API token
|
FORGE_VAULT_TOKEN= # [SECRET] vault-bot API token
|
||||||
|
FORGE_VAULT_PASS= # [SECRET] vault-bot password for git HTTP push
|
||||||
FORGE_SUPERVISOR_TOKEN= # [SECRET] supervisor-bot API token
|
FORGE_SUPERVISOR_TOKEN= # [SECRET] supervisor-bot API token
|
||||||
|
FORGE_SUPERVISOR_PASS= # [SECRET] supervisor-bot password for git HTTP push
|
||||||
FORGE_PREDICTOR_TOKEN= # [SECRET] predictor-bot API token
|
FORGE_PREDICTOR_TOKEN= # [SECRET] predictor-bot API token
|
||||||
|
FORGE_PREDICTOR_PASS= # [SECRET] predictor-bot password for git HTTP push
|
||||||
FORGE_ARCHITECT_TOKEN= # [SECRET] architect-bot API token
|
FORGE_ARCHITECT_TOKEN= # [SECRET] architect-bot API token
|
||||||
|
FORGE_ARCHITECT_PASS= # [SECRET] architect-bot password for git HTTP push
|
||||||
FORGE_BOT_USERNAMES=dev-bot,review-bot,planner-bot,gardener-bot,vault-bot,supervisor-bot,predictor-bot,architect-bot
|
FORGE_BOT_USERNAMES=dev-bot,review-bot,planner-bot,gardener-bot,vault-bot,supervisor-bot,predictor-bot,architect-bot
|
||||||
|
|
||||||
# ── Backwards compatibility ───────────────────────────────────────────────
|
# ── Backwards compatibility ───────────────────────────────────────────────
|
||||||
|
|
@ -35,6 +52,10 @@ FORGE_BOT_USERNAMES=dev-bot,review-bot,planner-bot,gardener-bot,vault-bot,superv
|
||||||
# CODEBERG_TOKEN automatically (same for REVIEW_BOT_TOKEN, CODEBERG_REPO,
|
# CODEBERG_TOKEN automatically (same for REVIEW_BOT_TOKEN, CODEBERG_REPO,
|
||||||
# CODEBERG_BOT_USERNAMES). No action needed for existing deployments.
|
# CODEBERG_BOT_USERNAMES). No action needed for existing deployments.
|
||||||
# Per-agent tokens default to FORGE_TOKEN when unset (single-token setups).
|
# Per-agent tokens default to FORGE_TOKEN when unset (single-token setups).
|
||||||
|
#
|
||||||
|
# Note: `disinto init` auto-generates all bot tokens/passwords when you
|
||||||
|
# configure [agents.llama] in a project TOML. The credentials are stored
|
||||||
|
# in .env.enc (encrypted) or .env (plaintext fallback).
|
||||||
|
|
||||||
# ── Woodpecker CI ─────────────────────────────────────────────────────────
|
# ── Woodpecker CI ─────────────────────────────────────────────────────────
|
||||||
WOODPECKER_TOKEN= # [SECRET] Woodpecker API token
|
WOODPECKER_TOKEN= # [SECRET] Woodpecker API token
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,8 @@ services:
|
||||||
- /usr/local/bin/claude:/usr/local/bin/claude:ro
|
- /usr/local/bin/claude:/usr/local/bin/claude:ro
|
||||||
environment:
|
environment:
|
||||||
- FORGE_URL=http://forgejo:3000
|
- FORGE_URL=http://forgejo:3000
|
||||||
- FORGE_TOKEN=${FORGE_TOKEN_DEVQWEN:-}
|
- FORGE_TOKEN=${FORGE_TOKEN_LLAMA:-}
|
||||||
|
- FORGE_PASS=${FORGE_PASS_LLAMA:-}
|
||||||
- FORGE_SUPERVISOR_TOKEN=${FORGE_SUPERVISOR_TOKEN:-}
|
- FORGE_SUPERVISOR_TOKEN=${FORGE_SUPERVISOR_TOKEN:-}
|
||||||
- FORGE_PREDICTOR_TOKEN=${FORGE_PREDICTOR_TOKEN:-}
|
- FORGE_PREDICTOR_TOKEN=${FORGE_PREDICTOR_TOKEN:-}
|
||||||
- FORGE_ARCHITECT_TOKEN=${FORGE_ARCHITECT_TOKEN:-}
|
- FORGE_ARCHITECT_TOKEN=${FORGE_ARCHITECT_TOKEN:-}
|
||||||
|
|
|
||||||
|
|
@ -307,6 +307,16 @@ setup_forge() {
|
||||||
[predictor-bot]="FORGE_PREDICTOR_PASS"
|
[predictor-bot]="FORGE_PREDICTOR_PASS"
|
||||||
[architect-bot]="FORGE_ARCHITECT_PASS"
|
[architect-bot]="FORGE_ARCHITECT_PASS"
|
||||||
)
|
)
|
||||||
|
# Llama bot users (local-model agents) — separate from main agents
|
||||||
|
# Each llama agent gets its own Forgejo user, token, and password
|
||||||
|
local -A llama_token_vars=(
|
||||||
|
[dev-qwen]="FORGE_TOKEN_LLAMA"
|
||||||
|
[dev-qwen-nightly]="FORGE_TOKEN_LLAMA_NIGHTLY"
|
||||||
|
)
|
||||||
|
local -A llama_pass_vars=(
|
||||||
|
[dev-qwen]="FORGE_PASS_LLAMA"
|
||||||
|
[dev-qwen-nightly]="FORGE_PASS_LLAMA_NIGHTLY"
|
||||||
|
)
|
||||||
|
|
||||||
local bot_user bot_pass token token_var pass_var
|
local bot_user bot_pass token token_var pass_var
|
||||||
|
|
||||||
|
|
@ -421,12 +431,126 @@ setup_forge() {
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Create llama bot users and tokens (local-model agents)
|
||||||
|
# These are separate from the main agents and get their own credentials
|
||||||
|
echo ""
|
||||||
|
echo "── Setting up llama bot users ────────────────────────────"
|
||||||
|
|
||||||
|
local llama_user llama_pass llama_token llama_token_var llama_pass_var
|
||||||
|
for llama_user in "${!llama_token_vars[@]}"; do
|
||||||
|
llama_token_var="${llama_token_vars[$llama_user]}"
|
||||||
|
llama_pass_var="${llama_pass_vars[$llama_user]}"
|
||||||
|
|
||||||
|
# Check if llama bot user exists
|
||||||
|
local llama_user_exists=false
|
||||||
|
if curl -sf --max-time 5 \
|
||||||
|
-H "Authorization: token ${admin_token}" \
|
||||||
|
"${forge_url}/api/v1/users/${llama_user}" >/dev/null 2>&1; then
|
||||||
|
llama_user_exists=true
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ "$llama_user_exists" = false ]; then
|
||||||
|
echo "Creating llama bot user: ${llama_user}"
|
||||||
|
# Generate a unique password for this user
|
||||||
|
llama_pass="llama-$(head -c 16 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 20)"
|
||||||
|
local create_output
|
||||||
|
if ! create_output=$(_forgejo_exec forgejo admin user create \
|
||||||
|
--username "${llama_user}" \
|
||||||
|
--password "${llama_pass}" \
|
||||||
|
--email "${llama_user}@disinto.local" \
|
||||||
|
--must-change-password=false 2>&1); then
|
||||||
|
echo "Error: failed to create llama bot user '${llama_user}':" >&2
|
||||||
|
echo " ${create_output}" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
# Forgejo 11.x ignores --must-change-password=false on create;
|
||||||
|
# explicitly clear the flag so basic-auth token creation works.
|
||||||
|
_forgejo_exec forgejo admin user change-password \
|
||||||
|
--username "${llama_user}" \
|
||||||
|
--password "${llama_pass}" \
|
||||||
|
--must-change-password=false
|
||||||
|
|
||||||
|
# Verify llama bot user was actually created
|
||||||
|
if ! curl -sf --max-time 5 \
|
||||||
|
-H "Authorization: token ${admin_token}" \
|
||||||
|
"${forge_url}/api/v1/users/${llama_user}" >/dev/null 2>&1; then
|
||||||
|
echo "Error: llama bot user '${llama_user}' not found after creation" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo " ${llama_user} user created"
|
||||||
|
else
|
||||||
|
echo " ${llama_user} user exists (resetting password for token generation)"
|
||||||
|
# User exists but may not have a known password.
|
||||||
|
# Use admin API to reset the password so we can generate a new token.
|
||||||
|
llama_pass="llama-$(head -c 16 /dev/urandom | base64 | tr -dc 'a-zA-Z0-9' | head -c 20)"
|
||||||
|
_forgejo_exec forgejo admin user change-password \
|
||||||
|
--username "${llama_user}" \
|
||||||
|
--password "${llama_pass}" \
|
||||||
|
--must-change-password=false || {
|
||||||
|
echo "Error: failed to reset password for existing llama bot user '${llama_user}'" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Generate token via API (basic auth as the llama user)
|
||||||
|
# First, delete any existing tokens to avoid name collision
|
||||||
|
local existing_llama_token_ids
|
||||||
|
existing_llama_token_ids=$(curl -sf \
|
||||||
|
-u "${llama_user}:${llama_pass}" \
|
||||||
|
"${forge_url}/api/v1/users/${llama_user}/tokens" 2>/dev/null \
|
||||||
|
| jq -r '.[].id // empty' 2>/dev/null) || existing_llama_token_ids=""
|
||||||
|
|
||||||
|
# Delete any existing tokens for this user
|
||||||
|
if [ -n "$existing_llama_token_ids" ]; then
|
||||||
|
while IFS= read -r tid; do
|
||||||
|
[ -n "$tid" ] && curl -sf -X DELETE \
|
||||||
|
-u "${llama_user}:${llama_pass}" \
|
||||||
|
"${forge_url}/api/v1/users/${llama_user}/tokens/${tid}" >/dev/null 2>&1 || true
|
||||||
|
done <<< "$existing_llama_token_ids"
|
||||||
|
fi
|
||||||
|
|
||||||
|
llama_token=$(curl -sf -X POST \
|
||||||
|
-u "${llama_user}:${llama_pass}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"${forge_url}/api/v1/users/${llama_user}/tokens" \
|
||||||
|
-d "{\"name\":\"disinto-${llama_user}-token\",\"scopes\":[\"all\"]}" 2>/dev/null \
|
||||||
|
| jq -r '.sha1 // empty') || llama_token=""
|
||||||
|
|
||||||
|
if [ -z "$llama_token" ]; then
|
||||||
|
echo "Error: failed to create API token for '${llama_user}'" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Store token in .env under the llama-specific variable name
|
||||||
|
if grep -q "^${llama_token_var}=" "$env_file" 2>/dev/null; then
|
||||||
|
sed -i "s|^${llama_token_var}=.*|${llama_token_var}=${llama_token}|" "$env_file"
|
||||||
|
else
|
||||||
|
printf '%s=%s\n' "$llama_token_var" "$llama_token" >> "$env_file"
|
||||||
|
fi
|
||||||
|
export "${llama_token_var}=${llama_token}"
|
||||||
|
echo " ${llama_user} token generated and saved (${llama_token_var})"
|
||||||
|
|
||||||
|
# Store password in .env for git HTTP push (#361)
|
||||||
|
# Forgejo 11.x API tokens don't work for git push; password auth does.
|
||||||
|
if grep -q "^${llama_pass_var}=" "$env_file" 2>/dev/null; then
|
||||||
|
sed -i "s|^${llama_pass_var}=.*|${llama_pass_var}=${llama_pass}|" "$env_file"
|
||||||
|
else
|
||||||
|
printf '%s=%s\n' "$llama_pass_var" "$llama_pass" >> "$env_file"
|
||||||
|
fi
|
||||||
|
export "${llama_pass_var}=${llama_pass}"
|
||||||
|
echo " ${llama_user} password saved (${llama_pass_var})"
|
||||||
|
done
|
||||||
|
|
||||||
# Create .profile repos for all bot users (if they don't already exist)
|
# Create .profile repos for all bot users (if they don't already exist)
|
||||||
# This runs the same logic as hire-an-agent Step 2-3 for idempotent setup
|
# This runs the same logic as hire-an-agent Step 2-3 for idempotent setup
|
||||||
echo ""
|
echo ""
|
||||||
echo "── Setting up .profile repos ────────────────────────────"
|
echo "── Setting up .profile repos ────────────────────────────"
|
||||||
|
|
||||||
local -a bot_users=(dev-bot review-bot planner-bot gardener-bot vault-bot supervisor-bot predictor-bot architect-bot)
|
local -a bot_users=(dev-bot review-bot planner-bot gardener-bot vault-bot supervisor-bot predictor-bot architect-bot)
|
||||||
|
# Add llama bot users to .profile repo creation
|
||||||
|
for llama_user in "${!llama_token_vars[@]}"; do
|
||||||
|
bot_users+=("$llama_user")
|
||||||
|
done
|
||||||
local bot_user
|
local bot_user
|
||||||
|
|
||||||
for bot_user in "${bot_users[@]}"; do
|
for bot_user in "${bot_users[@]}"; do
|
||||||
|
|
@ -534,6 +658,15 @@ setup_forge() {
|
||||||
-d "{\"permission\":\"${bot_perm}\"}" >/dev/null 2>&1 || true
|
-d "{\"permission\":\"${bot_perm}\"}" >/dev/null 2>&1 || true
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# Add llama bot users as write collaborators for local-model agents
|
||||||
|
for llama_user in "${!llama_token_vars[@]}"; do
|
||||||
|
curl -sf -X PUT \
|
||||||
|
-H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
"${forge_url}/api/v1/repos/${repo_slug}/collaborators/${llama_user}" \
|
||||||
|
-d '{"permission":"write"}' >/dev/null 2>&1 || true
|
||||||
|
done
|
||||||
|
|
||||||
# Add disinto-admin as admin collaborator
|
# Add disinto-admin as admin collaborator
|
||||||
curl -sf -X PUT \
|
curl -sf -X PUT \
|
||||||
-H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \
|
-H "Authorization: token ${admin_token:-${FORGE_TOKEN}}" \
|
||||||
|
|
|
||||||
|
|
@ -72,7 +72,9 @@ _generate_local_model_services() {
|
||||||
- \${HOME}/.ssh:/home/agent/.ssh:ro
|
- \${HOME}/.ssh:/home/agent/.ssh:ro
|
||||||
environment:
|
environment:
|
||||||
FORGE_URL: http://forgejo:3000
|
FORGE_URL: http://forgejo:3000
|
||||||
FORGE_TOKEN: \${FORGE_TOKEN:-}
|
# Use llama-specific credentials if available, otherwise fall back to main FORGE_TOKEN
|
||||||
|
FORGE_TOKEN: \${FORGE_TOKEN_LLAMA:-\${FORGE_TOKEN:-}}
|
||||||
|
FORGE_PASS: \${FORGE_PASS_LLAMA:-\${FORGE_PASS:-}}
|
||||||
FORGE_REVIEW_TOKEN: \${FORGE_REVIEW_TOKEN:-}
|
FORGE_REVIEW_TOKEN: \${FORGE_REVIEW_TOKEN:-}
|
||||||
FORGE_BOT_USERNAMES: \${FORGE_BOT_USERNAMES:-}
|
FORGE_BOT_USERNAMES: \${FORGE_BOT_USERNAMES:-}
|
||||||
AGENT_ROLES: "${roles}"
|
AGENT_ROLES: "${roles}"
|
||||||
|
|
|
||||||
|
|
@ -27,6 +27,11 @@ check_pipeline_stall = false
|
||||||
# for local LLM inference. Each agent gets its own container with isolated
|
# for local LLM inference. Each agent gets its own container with isolated
|
||||||
# credentials and configuration.
|
# credentials and configuration.
|
||||||
#
|
#
|
||||||
|
# When enabled, `disinto init` automatically:
|
||||||
|
# 1. Creates a Forgejo bot user matching agents.llama.forge_user
|
||||||
|
# 2. Generates FORGE_TOKEN_<BOT> and FORGE_PASS_<BOT> (stored in .env.enc)
|
||||||
|
# 3. Adds the bot user as a write collaborator on the project repo
|
||||||
|
#
|
||||||
# [agents.llama]
|
# [agents.llama]
|
||||||
# base_url = "http://10.10.10.1:8081"
|
# base_url = "http://10.10.10.1:8081"
|
||||||
# model = "unsloth/Qwen3.5-35B-A3B"
|
# model = "unsloth/Qwen3.5-35B-A3B"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue