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>
62 lines
2.5 KiB
Bash
Executable File
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
|