Files
jobbi-bewerbung/agent/skills/it-stellensuche/scripts/blacklist-set.sh
T
thomasandClaude ce844412e5 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>
2026-07-14 15:41:22 +02:00

62 lines
2.5 KiB
Bash
Executable File

#!/usr/bin/env bash
# Prints the Bewerbungs-Tracker BLACKLIST in a matchable, tab-separated form, so
# a job-search run can discard candidates that are blocked BEFORE listing or
# importing them (server also rejects blocked offers with 409 on POST /joboffers,
# but we don't want to even present them).
#
# Output: one blacklist entry per line, tab-separated:
# id <TAB> typ <TAB> firma <TAB> stelle <TAB> ort <TAB> domain <TAB> url_norm <TAB> firma_norm <TAB> firma_slug <TAB> stelle_norm <TAB> ort_norm <TAB> grund
#
# typ = url | domain | firma | firma_stelle | auto
#
# firma_slug = robuster Firmen-Schlüssel (Umlaut-Translit, End-Rechtsform raus,
# mit "-" verbunden) — identisch zu applied-set.sh und firmaSlug()
# in lib/blacklist.js. Damit greift die Firmen-Sperre auch bei
# abweichender Schreibweise/Rechtsform.
#
# A candidate is BLOCKED when one entry matches:
# typ=url -> candidate URL (normalized) == url_norm
# typ=domain -> candidate URL host == domain (also matches subdomains)
# typ=firma -> SAME company: candidate firma_slug == firma_slug, OR one
# firma_slug is a leading hyphen-prefix of the other (>=2
# tokens); firma_norm only as fallback for legacy rows.
# typ=firma_stelle -> same company (firma_slug, wie oben) AND stelle_norm equal
# (ort_norm only narrows further when set)
# typ=auto -> treat like the concrete fields it carries (url/firma_stelle)
#
# The server already provides the *_norm fields (lowercased, tracking params
# stripped, gender/legal-form removed), so compare against those.
#
# Reuses the bewerbungs-tracker skill's helper for auth/base-URL.
set -euo pipefail
BT="${BT_SCRIPT:-$HOME/.claude/skills/bewerbungs-tracker/scripts/bt.sh}"
if [[ ! -x "$BT" ]]; then
echo "blacklist-set.sh: bewerbungs-tracker helper not found at $BT" >&2
exit 2
fi
bl="$("$BT" GET '/joboffers/blacklist')"
BL_JSON="$bl" python3 <<'PY'
import os, json, html, sys
def load(name):
raw = os.environ[name].split('<http')[0]
try:
return json.loads(raw)
except Exception:
return []
def clean(v):
return html.unescape(str(v if v is not None else '')).replace('\t', ' ').strip()
rows = load('BL_JSON')
for e in rows:
print('\t'.join(clean(e.get(k)) for k in (
'id', 'typ', 'firma', 'stelle', 'ort', 'domain',
'url_norm', 'firma_norm', 'firma_slug', 'stelle_norm', 'ort_norm', 'grund')))
print(f'# {len(rows)} Blacklist-Eintraege', file=sys.stderr)
PY