Jobsuche: Agent pro Benutzer in isoliertem Docker-Container

Jeder Suchlauf läuft nun in einem frischen Container pro Benutzer, dessen
pro-Benutzer-Home als ~/.claude gemountet ist — Memories und Session-Contexte
liegen damit strikt getrennt pro Benutzer. Die Such-Skills sind Shared-Code
aus dem Image und werden im Container nur nach ~/.claude/skills verlinkt.

Der Host-Runner startet pro Lauf `docker run --rm` und führt bis zu
JOBSUCHE_MAX_PARALLEL (Default 4) Läufe parallel über verschiedene Benutzer
aus (jeder hat eigenen Ollama-Key = getrennte Rate-Limits). Der alte gemeinsame
~/.claude-Pfad wird vom Runner nicht mehr beschrieben.

- source/agent/: neues Agent-Image (Dockerfile + entrypoint + drei Skills)
- scripts/jobsuche-runner.js: agentStarten als docker run, ensureAgentDir, runPool
- scripts/jobsuche-runner.sh + bin/-Kopie: Image-Guard
- package.json: docker:build-agent (lokal, ohne Registry-Push)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-07-14 15:41:22 +02:00
co-authored by Claude
parent 7c92f23351
commit ce844412e5
15 changed files with 2014 additions and 71 deletions
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
# Helper for the Bewerbungs-Tracker REST-API.
# Usage:
# bt.sh GET /applications
# bt.sh GET "/applications?status=Gesendet&limit=20"
# bt.sh POST /applications '{"datum":"2026-07-03","firma":"ACME","stelle":"DevOps"}'
# bt.sh PUT /applications/12 @/path/to/body.json
# bt.sh DELETE /joboffers/5
#
# Config via env (with sensible defaults):
# BEWERBUNG_API_URL base URL (default http://localhost:4327/api/v1)
# BEWERBUNG_API_KEY API key (X-API-Key header)
# BEWERBUNG_USER username whose key to use (default: admin)
#
# MULTI-USER: the tracker has one API key PER USER; the key decides whose data you
# see and change. There is no global key any more. The old fallback (API_TOKEN from
# /opt/jobbi-bewerbung/.env) is gone on purpose: it happened to hold the admin's
# token, so it silently acted as admin — and it goes stale the moment a key is
# rotated in the UI (Einstellungen -> "Neu generieren").
#
# Key resolution:
# 1. $BEWERBUNG_API_KEY if set (this is what the Jobsuche-Runner passes per user)
# 2. otherwise: look up the key of $BEWERBUNG_USER (default admin) in the tracker DB
set -euo pipefail
BASE="${BEWERBUNG_API_URL:-http://localhost:4327/api/v1}"
DB="${BEWERBUNG_DB:-/opt/jobbi-bewerbung/data/bewerbungen.db}"
USER_NAME="${BEWERBUNG_USER:-admin}"
KEY="${BEWERBUNG_API_KEY:-}"
if [[ -z "$KEY" && -f "$DB" ]]; then
KEY="$(node -e "
const s = require('/opt/jobbi-bewerbung/source/node_modules/sqlite3');
const db = new s.Database(process.argv[1]);
db.get(
\"SELECT a.value FROM app_state a JOIN users u ON u.id = a.user_id \" +
\"WHERE u.username = ? AND a.key = 'cfg:API_TOKEN' AND a.value != ''\",
[process.argv[2]],
(e, r) => process.stdout.write(r && r.value ? r.value : '')
);
" "$DB" "$USER_NAME" 2>/dev/null || true)"
fi
if [[ -z "$KEY" ]]; then
echo "bt.sh: kein API-Key für Benutzer '$USER_NAME'." >&2
echo " -> In der App unter Einstellungen -> REST-API einen Token erzeugen," >&2
echo " oder BEWERBUNG_API_KEY / BEWERBUNG_USER setzen." >&2
exit 2
fi
METHOD="${1:?method required (GET/POST/PUT/DELETE)}"
PATH_ARG="${2:?path required, e.g. /applications}"
BODY="${3:-}"
url="${BASE}${PATH_ARG}"
args=(-sS -X "$METHOD" -H "X-API-Key: ${KEY}" -w $'\n<http %{http_code}>\n')
if [[ -n "$BODY" ]]; then
args+=(-H "Content-Type: application/json" --data-binary "$BODY")
fi
curl "${args[@]}" "$url"