raw fetch hinter Login: Session per raw login, mitgeliefert und optional, plus externe Andockstelle für einen Abrufdienst #169

Closed
opened 2026-10-02 20:01:45 +00:00 by torben · 5 comments
Owner

Neu gefasst am 2026-10-09 in der Design-Sitzung zu #87/#200 (Stand 9.0.0, 7cfafd7). Entscheidungen des Betreibers vom 2026-10-10 sind eingearbeitet. Bisher war dieses Paket nur eine Andockstelle für einen externen Dienst. Jetzt liefert der Stack den Abruf hinter Login selbst mit, und die Andockstelle bleibt für externe Dienste.

Abgeschlossen (2026-10-11). Gebaut als 10.0.0-beta.8, Commit 5994b2a, nach Freigabe des Mass-Update Gate (43 Dateien). Verifiziert durch die CI-Läufe 667 (verify samt Replay als frische Instanz, pwsh) und 668, beide grün. Vorher lokal grün: pytest gegen eine leere Umgebung (2890 bestanden, 4 übersprungen), docs verify, instructions verify.

Schlussphase: Doku-Nachzug c06bde2, nur docs/, außerhalb des Version-Gates, ohne Bump. docs/explanation/configuration-tiers.md sagt jetzt, dass die Session das einzige Secret ohne Verweis ist und warum raw login sie selbst in die Secret-Datei schreibt. Außerdem sagt sie, dass die Schutzmaßnahmen rund um 1Password die Session nicht abdecken. In docs/reference/configuration.md nennt die Zeile der Stufe secret die Sessions. Verifiziert durch CI-Lauf 669, grün. Die übrigen berührten Dokumente habe ich gegen den Code gelesen, sie stimmen: raw/CONTRACT.md, sites/CONTRACT.md, instructions/preflight.md gegen INSTALL.md, wiki-ingest, tools/README.md, die How-tos und die Records in tools/CONTRACT.md. Die beiden nicht blockierenden Prüfungen gegen ein echtes Playwright und unter nativem Windows sind nach #212 ausgelagert.

Problem

raw fetch lud ohne Login: kein Cookie, kein JavaScript (#120). Für Paywall-Seiten (Beispiel heise+) gab es nur raw fetch --html: Der Mensch speichert die Seite aus seinem eingeloggten Browser. Das ist pro Artikel Handarbeit am Desktop, für einen Endnutzer zu umständlich.

Ziel

Der Endnutzer loggt sich einmal in einem sichtbaren Browserfenster ein. Danach holt raw fetch Artikel dieser Website mit seiner Session, bis sie abläuft. Dann sagt das Werkzeug ihm, dass er sich neu einloggen muss. Der Stack speichert kein Passwort, und der Agent sieht keine Zugangsdaten.

Entscheidungen des Betreibers (2026-10-10)

  1. Mitliefern als optionale Komponente der Distribution, nicht als eigenes Repo.
  2. Nur Session, keine gespeicherten Passwörter, kein Login-Adapter pro Website, kein automatisches Neu-Einloggen.
  3. Gerendertes DOM ist zugelassen, aber nur per Handler-Deklaration und mit Header rendered: true.
  4. Rechtliches bleibt außen vor. Ob eine Website den automatisierten Abruf durch den Abonnenten erlaubt, hat der Nutzer im Blick. Der Stack prüft das nicht, vermerkt nichts dazu in Handlern und gibt keine Rechtsberatung. Die Doku sagt das in einem Satz.

Entwurf (gebaut)

  1. raw login <host> - nur der Mensch. Das Kommando öffnet ein sichtbares Browserfenster (Playwright) auf der Login-Seite, die der Site-Handler nennt. Der Nutzer loggt sich ein, Captcha und 2FA eingeschlossen, und schließt das Fenster. Gespeichert wird nur der Session-Zustand (Cookies, Storage-State) als Secret in der Secret-Datei im Nutzerverzeichnis, außerhalb des Arbeitsbaums. Nach 1Password wird die Session nicht geschrieben. Ein Agent ruft raw login nie auf. Das steht im Kommando-Record (NEVER), in wiki-ingest und in der Ausgabe von raw fetch.
    Bau: Name des Secrets ist session_<host>, wobei jede Folge anderer Zeichen zu einem _ wird (www.heise.de → session_www_heise_de). Der Wert ist der Storage-State als JSON-String, und WIKITOOL_SECRET_SESSION_<HOST> hat wie bei jedem Namensverweis Vorrang. Geschrieben wird über das neue settings.write_secret. Das schreibt über write_file (mkstemp, also 0600) und behält alle anderen Einträge. raw login verlangt, dass der Host schon auf session geroutet ist. Damit gibt es auch die instance_id, über die die Secret-Datei gefunden wird. Das Fenster wird einmal pro Sekunde ausgelesen, höchstens 15 Minuten lang. Schließt der Nutzer das Fenster oder den ganzen Browser, gilt der letzte gelesene Stand.
  2. raw fetch mit Session: Ist der Host auf die Session geroutet, lädt raw fetch die Seite mit den gespeicherten Cookies über den eingebauten Abruf, also ohne Browser und mit den Bytes, wie sie ankommen. Nur ein Handler mit render: true schickt den Abruf durch einen Headless-Browser, und der Header sagt dann rendered: true (Entscheidung 3).
    Bau, Zuständigkeit: Der Handler beschreibt nur, wie eine Session geht: den Block session mit login und dem Zeichen einer eingeloggten Seite. Ob ein Host mit Session geholt wird, entscheidet die Instanz mit fetch.routes (Entwurf 6). Ein Handler allein schaltet also nichts um, sonst bekäme jeder Nutzer ohne Abo für jede Seite des Hosts Exit 42. Die Cookies schickt ein http.cookiejar (Domain, Pfad, secure, Ablauf, auch bei Redirects). web_capture.fetch bekommt dafür ein optionales cookies, und ohne Session ist der Aufruf genau derselbe wie vorher. Der Header nennt session: <host> nach site:. render gilt auch ohne Session, für Seiten, die nur per JavaScript Inhalt haben. Ein externer Abrufer wird nie gerendert. --scope holt jede Seite des Bündels auf demselben Weg, und _scope.json nennt session, fetcher oder rendered. Den Hinweis auf --html gibt raw fetch nur noch beim anonymen Abruf aus.
  3. Abgelaufen oder fehlend: Der Handler erkennt am HTML, ob die Seite eingeloggt ankam. Fehlt die Session oder ist sie abgelaufen → exit 42, nichts in incoming/, Ausgabe: „Ask the user to run tools/wikitool raw login <host>“. Ein stilles Speichern des Teasers gibt es nicht.
    Bau: Im Block session stehen signed_in (ein Selektor, der nur auf einer eingeloggten Seite trifft, z. B. ein Logout-Link) und signed_out (trifft nur auf dem Teaser, z. B. die Paywall-Box). Mindestens eines von beiden ist Pflicht, und stehen beide da, muss beides gelten. Ohne gespeicherte Session geht keine Anfrage raus. Geprüft wird jede HTML-Antwort, auch jede Unterseite bei --scope.
  4. Optionale Komponente: Playwright kommt in eine eigene tools/requirements-browser.txt, die der Nutzer über eine Option des Preflights installiert. Der Agent installiert nichts (#140). Der Import ist lazy (tools/chemenu/browser.py). Fehlt die Komponente, enden nur raw login und Abrufe mit render: true mit dem Hinweis auf die Preflight-Option. Alles andere läuft. doctor meldet sie, ohne zu FAILen.
    Bau: Die Option heißt --with browser (sh und ps1, auch im Asset-Modus durchgereicht). Sie installiert die Anforderungen und playwright install chromium mit PLAYWRIGHT_BROWSERS_PATH=tools/.venv/ms-playwright, also ins Installationsverzeichnis und nicht in den Cache im Home-Verzeichnis. browser.py setzt denselben Pfad. Der Stempel .venv/.chemenu-browser.sha256 merkt sich die Wahl, und spätere einfache Läufe halten die Komponente aktuell. Der Abschnitt läuft nach dem Schreiben von .wikitool-tools.json mit complete: true. Ein Fehlschlag endet mit exit 42, lässt aber jedes andere Kommando lauffähig. Fehlt die Komponente, enden raw login und ein gerenderter Abruf mit exit 42, nicht mit 1, weil nur der Nutzer installiert. instructions/preflight.md sagt, dass der Agent --with browser nie selbst übergibt. Die doctor-Zeile browser ist in beiden Fällen OK und nur dann WARN, wenn die Instanz die Komponente braucht: ein auf session gerouteter Host oder ein Handler mit render.
  5. Externe Andockstelle: Eine Instanz kann einem Host statt der eingebauten Session einen externen Befehl zuordnen. Aufruf als command + [url] ohne Shell; exit 0 und HTML auf stdout = Erfolg; sonst exit 1 von raw fetch, stderr des Befehls wird mit ausgegeben. Kein Ausweichen auf den eingebauten Abruf. Header fetched_by: wikitool raw fetch (fetcher: <name>).
    Bau: Timeout 120 s, höchstens 25 MiB, stdin geschlossen. Eine leere Ausgabe gilt als Fehler. Der Header hat kein http_status, weil es keinen gibt, und content_type ist text/html laut Vertrag des Abrufers.
  6. Konfiguration nach #207, gebaut als Abschnitt fetch:
    • routes (Stufe instance): Objekt Host → "session" oder Name eines Abrufers;
    • fetchers (Stufe checkout, names_command): Objekt Name → Befehl als Array. config set verweigert das Feld mit exit 42, und in wikitool.json ist es ein Fehler;
    • die Session: Stufe secret, ohne Feld in den Einstellungen (Name siehe Entwurf 1).
      Bau: Eine Route session ohne Handler mit session-Block und eine Route auf einen Abrufer, den dieser Checkout nicht definiert, enden vor jedem Abruf mit exit 1. Ein Klon ohne das Programm fällt so laut aus, statt still anonym zu holen.
  7. MCP bleibt außen vor (wie #120 Entscheidung 12).

Bau: Einen Handler für heise.de liefert der Stack nicht mit. Ohne Abo lassen sich die Markierungen nicht prüfen, also schreibt die Instanz ihren eigenen (Anleitung in docs/how-to/fetch-pages-behind-a-login.md).

Nicht blockierende Prüfungen, ausgelagert nach #212:

  • Gegen ein echtes Playwright liefen browser.login und browser.render nie. CI installiert die Komponente nicht, und die Tests ersetzen den Browser.
  • Ob Playwright samt Browser unter dem nativen Windows-Preflight (pwsh 7, #140) sauber installiert, ist ungeprüft. Der ps1-Zweig lief nur unter pwsh auf Linux, lokal und im CI-Job pwsh.

Bis #212 gilt: INSTALL.md sagt „native Windows is not yet verified“. Schlägt die Windows-Prüfung fehl, wird raw login dort mit exit 1 und klarer Meldung verweigert, und der Rest bleibt.

Betroffene Dateien (gebaut)

  • neu: tools/chemenu/commands/raw/login.py (Kommando + Record, Gruppe „Raw material and uploads“), tools/chemenu/fetch_routes.py (Route, Abruf, Prüfung „eingeloggt“, externer Abrufer), tools/chemenu/web_session.py (Secret-Name, Laden/Speichern, Cookie-Jar), tools/chemenu/browser.py (Playwright lazy: login, render, missing), tools/requirements-browser.txt
  • tools/chemenu/commands/raw/fetch.py (Route, exit 42, Header, Record), tools/chemenu/web_capture.py (cookies, Header-Felder session/rendered, http_status nur wenn vorhanden)
  • tools/chemenu/sites/__init__.py, sites/data.py, sites/site.schema.json (Session, render, login_site)
  • tools/chemenu/config_shapes.py (FETCH), tools/chemenu/settings.py (write_secret), tools/chemenu/reference_doc.py, tools/chemenu/cli_contract.py
  • tools/chemenu/commands/doctor.py (Zeile browser)
  • tools/preflight.sh, tools/preflight.ps1 (--with browser)
  • raw/CONTRACT.md (§ Getting a URL in, § What does not belong here), sites/CONTRACT.md, instructions/preflight.md, instructions/wiki-ingest/SKILL.md, INSTALL.md, tools/README.md, neu docs/how-to/fetch-pages-behind-a-login.md (mit dem Satz zu den Nutzungsbedingungen), docs/how-to/ingest-a-source.md, docs/how-to/README.md, docs/reference/configuration.md (§ Fetch routes, Zeile der Stufe secret), docs/explanation/configuration-tiers.md (Schlussphase), generiert: tools/CONTRACT.md, docs/reference/commands/
  • Tests: neu test_raw_fetch_routes.py, test_raw_login.py; ergänzt test_site_files.py, test_doctor.py, test_preflight.py, test_preflight_pwsh.py, test_cli.py, test_config_shapes.py (die beiden festgeschriebenen Listen um raw login und fetch erweitert)

Akzeptanzkriterien

  • Mit gespeicherter Session liefert der Testserver den Volltext. incoming/<stem>.html ist byte-identisch mit seiner Antwort, und der Header nennt die Session, nicht ihren Inhalt. - test_with_a_stored_session_the_full_text_arrives_byte_identical, test_the_session_is_read_from_the_secrets_file_outside_the_tree
  • Ohne oder mit abgelaufener Session: exit 42, die Ausgabe nennt raw login <host>, incoming/ bleibt unverändert, und der Teaser wird nicht gespeichert. - test_without_a_valid_session_exit_42_… (keine Session, von der Website beendet, abgelaufenes Cookie)
  • Der Session-Inhalt erscheint in keiner Ausgabe, in keiner Datei im Arbeitsbaum und im Git-Index. - die beiden ersten Tests und test_login_stores_the_session_outside_the_tree_and_prints_none_of_it. Der Wert steht nur in der Secret-Datei, und die liegt außerhalb des Baums.
  • Ein Handler mit render: true ergibt den Header rendered: true; ohne Deklaration wird nie gerendert. - test_a_handler_declaring_render_gives_a_rendered_header, test_without_the_declaration_nothing_is_ever_rendered (Browser ersetzt; echtes Playwright → #212)
  • Ohne Browser-Komponente laufen alle bestehenden Tests unverändert grün. raw login endet mit dem Preflight-Hinweis, doctor meldet die Komponente als nicht verfügbar, ohne zu FAILen. - test_login_without_the_browser_component_…, test_a_missing_browser_component_…
  • Externer Abrufer: stdout landet byte-identisch in incoming/<stem>.html, und der eingebaute Abruf wird nicht ausgeführt. Exit ≠ 0 ergibt exit 1 mit dessen stderr, nichts in incoming/ und keinen Ersatzversuch. - test_an_external_fetcher_writes_its_stdout_byte_identical, test_a_failing_fetcher_exits_1_with_its_stderr_and_no_fallback
  • Eine URL, deren Host weder Session noch Abrufer zugeordnet ist, ergibt byte-identische Ausgaben wie vorher. - die bestehenden Tests von raw fetch unverändert, dazu test_a_host_no_route_names_is_fetched_exactly_as_before
  • Ein Abrufer-Befehl in der eingecheckten Instanz-Datei wird verweigert (Regel aus #207). - test_a_fetcher_command_in_the_committed_file_is_refused, dazu test_config_set_writes_a_route_and_leaves_a_fetcher_to_the_user
  • pytest, docs verify, instructions verify grün; CI grün. - CI-Läufe 667 und 668 auf 5994b2a, nach dem Doku-Nachzug 669 auf c06bde2

Versionsteil

--minor. Neues Kommando, neue optionale Komponente mit Lazy-Import, keine Verhaltensänderung ohne Konfiguration. Drop-in in beide Richtungen. Die Begründung ist dieselbe wie bei #199 § Versionsteil. Bau: gebumpt auf dem laufenden Kandidaten, 10.0.0-beta.7 → 10.0.0-beta.8.

Bezug

  • #120: raw fetch, Header, Determinismus
  • #87: Notizzettel für die Infrastruktur außerhalb (RSS-Auslöser). Für einen externen Dienst bleibt Entwurf 5 die Andockstelle.
  • #200, #210: Site-Handler des Stacks und der Instanz (Login-URL, Erkennung, render)
  • #207: drei Stufen der Konfiguration
  • #212: Prüfung gegen echtes Playwright und unter nativem Windows

Kette

Kette „Beschaffung und Konfiguration“

> Neu gefasst am 2026-10-09 in der Design-Sitzung zu #87/#200 (Stand `9.0.0`, `7cfafd7`). Entscheidungen des Betreibers vom 2026-10-10 sind eingearbeitet. Bisher war dieses Paket nur eine Andockstelle für einen externen Dienst. Jetzt liefert der Stack den Abruf hinter Login selbst mit, und die Andockstelle bleibt für externe Dienste. > > **Abgeschlossen (2026-10-11).** Gebaut als `10.0.0-beta.8`, Commit `5994b2a`, nach Freigabe des Mass-Update Gate (43 Dateien). **Verifiziert durch die CI-Läufe [667](https://gitea.nehmer.net/torben/chemenu/actions/runs/667) (`verify` samt Replay als frische Instanz, `pwsh`) und [668](https://gitea.nehmer.net/torben/chemenu/actions/runs/668), beide grün.** Vorher lokal grün: `pytest` gegen eine leere Umgebung (2890 bestanden, 4 übersprungen), `docs verify`, `instructions verify`. > > **Schlussphase:** Doku-Nachzug `c06bde2`, nur `docs/`, außerhalb des Version-Gates, ohne Bump. `docs/explanation/configuration-tiers.md` sagt jetzt, dass die Session das einzige Secret ohne Verweis ist und warum `raw login` sie selbst in die Secret-Datei schreibt. Außerdem sagt sie, dass die Schutzmaßnahmen rund um 1Password die Session nicht abdecken. In `docs/reference/configuration.md` nennt die Zeile der Stufe `secret` die Sessions. Verifiziert durch CI-Lauf [669](https://gitea.nehmer.net/torben/chemenu/actions/runs/669), grün. Die übrigen berührten Dokumente habe ich gegen den Code gelesen, sie stimmen: `raw/CONTRACT.md`, `sites/CONTRACT.md`, `instructions/preflight.md` gegen `INSTALL.md`, `wiki-ingest`, `tools/README.md`, die How-tos und die Records in `tools/CONTRACT.md`. Die beiden nicht blockierenden Prüfungen gegen ein echtes Playwright und unter nativem Windows sind nach #212 ausgelagert. ## Problem `raw fetch` lud ohne Login: kein Cookie, kein JavaScript (#120). Für Paywall-Seiten (Beispiel heise+) gab es nur `raw fetch --html`: Der Mensch speichert die Seite aus seinem eingeloggten Browser. Das ist pro Artikel Handarbeit am Desktop, für einen Endnutzer zu umständlich. ## Ziel Der Endnutzer loggt sich **einmal** in einem sichtbaren Browserfenster ein. Danach holt `raw fetch` Artikel dieser Website mit seiner Session, bis sie abläuft. Dann sagt das Werkzeug ihm, dass er sich neu einloggen muss. Der Stack speichert kein Passwort, und der Agent sieht keine Zugangsdaten. ## Entscheidungen des Betreibers (2026-10-10) 1. **Mitliefern** als optionale Komponente der Distribution, nicht als eigenes Repo. 2. **Nur Session, keine gespeicherten Passwörter**, kein Login-Adapter pro Website, kein automatisches Neu-Einloggen. 3. **Gerendertes DOM ist zugelassen**, aber nur per Handler-Deklaration und mit Header `rendered: true`. 4. **Rechtliches bleibt außen vor.** Ob eine Website den automatisierten Abruf durch den Abonnenten erlaubt, hat der Nutzer im Blick. Der Stack prüft das nicht, vermerkt nichts dazu in Handlern und gibt keine Rechtsberatung. Die Doku sagt das in einem Satz. ## Entwurf (gebaut) 1. **`raw login <host>` - nur der Mensch.** Das Kommando öffnet ein sichtbares Browserfenster (Playwright) auf der Login-Seite, die der Site-Handler nennt. Der Nutzer loggt sich ein, Captcha und 2FA eingeschlossen, und schließt das Fenster. Gespeichert wird nur der Session-Zustand (Cookies, Storage-State) als Secret in der Secret-Datei im Nutzerverzeichnis, außerhalb des Arbeitsbaums. Nach 1Password wird die Session nicht geschrieben. Ein Agent ruft `raw login` nie auf. Das steht im Kommando-Record (NEVER), in `wiki-ingest` und in der Ausgabe von `raw fetch`. **Bau:** Name des Secrets ist `session_<host>`, wobei jede Folge anderer Zeichen zu einem `_` wird (`www.heise.de` → `session_www_heise_de`). Der Wert ist der Storage-State als JSON-String, und `WIKITOOL_SECRET_SESSION_<HOST>` hat wie bei jedem Namensverweis Vorrang. Geschrieben wird über das neue `settings.write_secret`. Das schreibt über `write_file` (mkstemp, also `0600`) und behält alle anderen Einträge. `raw login` verlangt, dass der Host schon auf `session` geroutet ist. Damit gibt es auch die `instance_id`, über die die Secret-Datei gefunden wird. Das Fenster wird einmal pro Sekunde ausgelesen, höchstens 15 Minuten lang. Schließt der Nutzer das Fenster oder den ganzen Browser, gilt der letzte gelesene Stand. 2. **`raw fetch` mit Session:** Ist der Host auf die Session geroutet, lädt `raw fetch` die Seite mit den gespeicherten Cookies über den eingebauten Abruf, also ohne Browser und mit den Bytes, wie sie ankommen. Nur ein Handler mit `render: true` schickt den Abruf durch einen Headless-Browser, und der Header sagt dann `rendered: true` (Entscheidung 3). **Bau, Zuständigkeit:** Der Handler beschreibt nur, *wie* eine Session geht: den Block `session` mit `login` und dem Zeichen einer eingeloggten Seite. *Ob* ein Host mit Session geholt wird, entscheidet die Instanz mit `fetch.routes` (Entwurf 6). Ein Handler allein schaltet also nichts um, sonst bekäme jeder Nutzer ohne Abo für jede Seite des Hosts Exit 42. Die Cookies schickt ein `http.cookiejar` (Domain, Pfad, `secure`, Ablauf, auch bei Redirects). `web_capture.fetch` bekommt dafür ein optionales `cookies`, und ohne Session ist der Aufruf genau derselbe wie vorher. Der Header nennt `session: <host>` nach `site:`. `render` gilt auch ohne Session, für Seiten, die nur per JavaScript Inhalt haben. Ein externer Abrufer wird nie gerendert. `--scope` holt jede Seite des Bündels auf demselben Weg, und `_scope.json` nennt `session`, `fetcher` oder `rendered`. Den Hinweis auf `--html` gibt `raw fetch` nur noch beim anonymen Abruf aus. 3. **Abgelaufen oder fehlend:** Der Handler erkennt am HTML, ob die Seite eingeloggt ankam. Fehlt die Session oder ist sie abgelaufen → **exit 42**, nichts in `incoming/`, Ausgabe: „Ask the user to run `tools/wikitool raw login <host>`“. Ein stilles Speichern des Teasers gibt es nicht. **Bau:** Im Block `session` stehen `signed_in` (ein Selektor, der nur auf einer eingeloggten Seite trifft, z. B. ein Logout-Link) und `signed_out` (trifft nur auf dem Teaser, z. B. die Paywall-Box). Mindestens eines von beiden ist Pflicht, und stehen beide da, muss beides gelten. Ohne gespeicherte Session geht keine Anfrage raus. Geprüft wird jede HTML-Antwort, auch jede Unterseite bei `--scope`. 4. **Optionale Komponente:** Playwright kommt in eine eigene `tools/requirements-browser.txt`, die der Nutzer über eine Option des Preflights installiert. Der Agent installiert nichts (#140). Der Import ist lazy (`tools/chemenu/browser.py`). Fehlt die Komponente, enden nur `raw login` und Abrufe mit `render: true` mit dem Hinweis auf die Preflight-Option. Alles andere läuft. `doctor` meldet sie, ohne zu FAILen. **Bau:** Die Option heißt `--with browser` (sh und ps1, auch im Asset-Modus durchgereicht). Sie installiert die Anforderungen und `playwright install chromium` mit `PLAYWRIGHT_BROWSERS_PATH=tools/.venv/ms-playwright`, also ins Installationsverzeichnis und nicht in den Cache im Home-Verzeichnis. `browser.py` setzt denselben Pfad. Der Stempel `.venv/.chemenu-browser.sha256` merkt sich die Wahl, und spätere einfache Läufe halten die Komponente aktuell. Der Abschnitt läuft **nach** dem Schreiben von `.wikitool-tools.json` mit `complete: true`. Ein Fehlschlag endet mit exit 42, lässt aber jedes andere Kommando lauffähig. Fehlt die Komponente, enden `raw login` und ein gerenderter Abruf mit **exit 42**, nicht mit 1, weil nur der Nutzer installiert. `instructions/preflight.md` sagt, dass der Agent `--with browser` nie selbst übergibt. Die `doctor`-Zeile `browser` ist in beiden Fällen OK und nur dann WARN, wenn die Instanz die Komponente braucht: ein auf `session` gerouteter Host oder ein Handler mit `render`. 5. **Externe Andockstelle:** Eine Instanz kann einem Host statt der eingebauten Session einen externen Befehl zuordnen. Aufruf als `command + [url]` ohne Shell; exit 0 und HTML auf stdout = Erfolg; sonst exit 1 von `raw fetch`, stderr des Befehls wird mit ausgegeben. Kein Ausweichen auf den eingebauten Abruf. Header `fetched_by: wikitool raw fetch (fetcher: <name>)`. **Bau:** Timeout 120 s, höchstens 25 MiB, stdin geschlossen. Eine leere Ausgabe gilt als Fehler. Der Header hat kein `http_status`, weil es keinen gibt, und `content_type` ist `text/html` laut Vertrag des Abrufers. 6. **Konfiguration nach #207, gebaut als Abschnitt `fetch`:** - `routes` (Stufe `instance`): Objekt Host → `"session"` oder Name eines Abrufers; - `fetchers` (Stufe `checkout`, `names_command`): Objekt Name → Befehl als Array. `config set` verweigert das Feld mit exit 42, und in `wikitool.json` ist es ein Fehler; - die Session: Stufe `secret`, ohne Feld in den Einstellungen (Name siehe Entwurf 1). **Bau:** Eine Route `session` ohne Handler mit `session`-Block und eine Route auf einen Abrufer, den dieser Checkout nicht definiert, enden vor jedem Abruf mit exit 1. Ein Klon ohne das Programm fällt so laut aus, statt still anonym zu holen. 7. **MCP bleibt außen vor** (wie #120 Entscheidung 12). **Bau:** Einen Handler für heise.de liefert der Stack nicht mit. Ohne Abo lassen sich die Markierungen nicht prüfen, also schreibt die Instanz ihren eigenen (Anleitung in `docs/how-to/fetch-pages-behind-a-login.md`). **Nicht blockierende Prüfungen, ausgelagert nach #212:** - Gegen ein echtes Playwright liefen `browser.login` und `browser.render` nie. CI installiert die Komponente nicht, und die Tests ersetzen den Browser. - Ob Playwright samt Browser unter dem nativen Windows-Preflight (pwsh 7, #140) sauber installiert, ist ungeprüft. Der ps1-Zweig lief nur unter pwsh auf Linux, lokal und im CI-Job `pwsh`. Bis #212 gilt: `INSTALL.md` sagt „native Windows is not yet verified“. Schlägt die Windows-Prüfung fehl, wird `raw login` dort mit exit 1 und klarer Meldung verweigert, und der Rest bleibt. ## Betroffene Dateien (gebaut) - neu: `tools/chemenu/commands/raw/login.py` (Kommando + Record, Gruppe „Raw material and uploads“), `tools/chemenu/fetch_routes.py` (Route, Abruf, Prüfung „eingeloggt“, externer Abrufer), `tools/chemenu/web_session.py` (Secret-Name, Laden/Speichern, Cookie-Jar), `tools/chemenu/browser.py` (Playwright lazy: `login`, `render`, `missing`), `tools/requirements-browser.txt` - `tools/chemenu/commands/raw/fetch.py` (Route, exit 42, Header, Record), `tools/chemenu/web_capture.py` (`cookies`, Header-Felder `session`/`rendered`, `http_status` nur wenn vorhanden) - `tools/chemenu/sites/__init__.py`, `sites/data.py`, `sites/site.schema.json` (`Session`, `render`, `login_site`) - `tools/chemenu/config_shapes.py` (`FETCH`), `tools/chemenu/settings.py` (`write_secret`), `tools/chemenu/reference_doc.py`, `tools/chemenu/cli_contract.py` - `tools/chemenu/commands/doctor.py` (Zeile `browser`) - `tools/preflight.sh`, `tools/preflight.ps1` (`--with browser`) - `raw/CONTRACT.md` (§ Getting a URL in, § What does not belong here), `sites/CONTRACT.md`, `instructions/preflight.md`, `instructions/wiki-ingest/SKILL.md`, `INSTALL.md`, `tools/README.md`, neu `docs/how-to/fetch-pages-behind-a-login.md` (mit dem Satz zu den Nutzungsbedingungen), `docs/how-to/ingest-a-source.md`, `docs/how-to/README.md`, `docs/reference/configuration.md` (§ Fetch routes, Zeile der Stufe `secret`), `docs/explanation/configuration-tiers.md` (Schlussphase), generiert: `tools/CONTRACT.md`, `docs/reference/commands/` - Tests: neu `test_raw_fetch_routes.py`, `test_raw_login.py`; ergänzt `test_site_files.py`, `test_doctor.py`, `test_preflight.py`, `test_preflight_pwsh.py`, `test_cli.py`, `test_config_shapes.py` (die beiden festgeschriebenen Listen um `raw login` und `fetch` erweitert) ## Akzeptanzkriterien - [x] Mit gespeicherter Session liefert der Testserver den Volltext. `incoming/<stem>.html` ist byte-identisch mit seiner Antwort, und der Header nennt die Session, nicht ihren Inhalt. - `test_with_a_stored_session_the_full_text_arrives_byte_identical`, `test_the_session_is_read_from_the_secrets_file_outside_the_tree` - [x] Ohne oder mit abgelaufener Session: exit 42, die Ausgabe nennt `raw login <host>`, `incoming/` bleibt unverändert, und der Teaser wird nicht gespeichert. - `test_without_a_valid_session_exit_42_…` (keine Session, von der Website beendet, abgelaufenes Cookie) - [x] Der Session-Inhalt erscheint in keiner Ausgabe, in keiner Datei im Arbeitsbaum und im Git-Index. - die beiden ersten Tests und `test_login_stores_the_session_outside_the_tree_and_prints_none_of_it`. Der Wert steht nur in der Secret-Datei, und die liegt außerhalb des Baums. - [x] Ein Handler mit `render: true` ergibt den Header `rendered: true`; ohne Deklaration wird nie gerendert. - `test_a_handler_declaring_render_gives_a_rendered_header`, `test_without_the_declaration_nothing_is_ever_rendered` (Browser ersetzt; echtes Playwright → #212) - [x] Ohne Browser-Komponente laufen alle bestehenden Tests unverändert grün. `raw login` endet mit dem Preflight-Hinweis, `doctor` meldet die Komponente als nicht verfügbar, ohne zu FAILen. - `test_login_without_the_browser_component_…`, `test_a_missing_browser_component_…` - [x] Externer Abrufer: stdout landet byte-identisch in `incoming/<stem>.html`, und der eingebaute Abruf wird nicht ausgeführt. Exit ≠ 0 ergibt exit 1 mit dessen stderr, nichts in `incoming/` und keinen Ersatzversuch. - `test_an_external_fetcher_writes_its_stdout_byte_identical`, `test_a_failing_fetcher_exits_1_with_its_stderr_and_no_fallback` - [x] Eine URL, deren Host weder Session noch Abrufer zugeordnet ist, ergibt byte-identische Ausgaben wie vorher. - die bestehenden Tests von `raw fetch` unverändert, dazu `test_a_host_no_route_names_is_fetched_exactly_as_before` - [x] Ein Abrufer-Befehl in der eingecheckten Instanz-Datei wird verweigert (Regel aus #207). - `test_a_fetcher_command_in_the_committed_file_is_refused`, dazu `test_config_set_writes_a_route_and_leaves_a_fetcher_to_the_user` - [x] `pytest`, `docs verify`, `instructions verify` grün; CI grün. - CI-Läufe 667 und 668 auf `5994b2a`, nach dem Doku-Nachzug 669 auf `c06bde2` ## Versionsteil **`--minor`.** Neues Kommando, neue optionale Komponente mit Lazy-Import, keine Verhaltensänderung ohne Konfiguration. Drop-in in beide Richtungen. Die Begründung ist dieselbe wie bei #199 § Versionsteil. **Bau:** gebumpt auf dem laufenden Kandidaten, `10.0.0-beta.7` → `10.0.0-beta.8`. ## Bezug - #120: `raw fetch`, Header, Determinismus - #87: Notizzettel für die Infrastruktur außerhalb (RSS-Auslöser). Für einen externen Dienst bleibt Entwurf 5 die Andockstelle. - #200, #210: Site-Handler des Stacks und der Instanz (Login-URL, Erkennung, `render`) - #207: drei Stufen der Konfiguration - #212: Prüfung gegen echtes Playwright und unter nativem Windows ## Kette Kette „Beschaffung und Konfiguration“ - Vorgänger: #209 - Nachfolger: keiner - letztes Glied
torben added the prio/waitingsize/Marea/kbkind/build labels 2026-10-02 20:01:45 +00:00
torben changed title from raw fetch: externer Abrufbefehl pro Instanz (.wikitool-fetch.json) als Andockstelle für einen Auth-Dienst to raw fetch hinter Login: Session per `raw login`, mitgeliefert und optional, plus externe Andockstelle für einen Abrufdienst 2026-10-09 21:43:20 +00:00
torben added prio/plannedsize/Lkind/decisionstatus/blocked and removed prio/waitingsize/Mkind/build labels 2026-10-09 21:43:21 +00:00
Author
Owner

Changelog: Neu gefasst (2026-10-09). Bisher war das Issue nur eine Andockstelle für einen externen Dienst. Neu sind der mitgelieferte Abruf mit Session per raw login (optionale Browser-Komponente), die Erkennung einer abgelaufenen Session mit exit 42 über den Site-Handler (#200) und die Konfiguration über die drei Stufen aus #207. Die eigene Datei .wikitool-fetch.json entfällt. Die externe Andockstelle bleibt als Entwurf 5. Der Auslöser „Dienst aus #87 existiert“ entfällt: prio/waiting → prio/planned, kind/build → kind/decision (vier offene Fragen), size/M → size/L, status/blocked durch #207 und #200. Letztes Glied der Kette „Beschaffung und Konfiguration“.

**Changelog:** Neu gefasst (2026-10-09). Bisher war das Issue nur eine Andockstelle für einen externen Dienst. Neu sind der mitgelieferte Abruf mit Session per `raw login` (optionale Browser-Komponente), die Erkennung einer abgelaufenen Session mit exit 42 über den Site-Handler (#200) und die Konfiguration über die drei Stufen aus #207. Die eigene Datei `.wikitool-fetch.json` entfällt. Die externe Andockstelle bleibt als Entwurf 5. Der Auslöser „Dienst aus #87 existiert“ entfällt: `prio/waiting` → `prio/planned`, `kind/build` → `kind/decision` (vier offene Fragen), `size/M` → `size/L`, `status/blocked` durch #207 und #200. Letztes Glied der Kette „Beschaffung und Konfiguration“.
torben added kind/build and removed kind/decision labels 2026-10-10 06:18:02 +00:00
Author
Owner

Changelog: Die vier Fragen sind entschieden (2026-10-10): mitliefern, nur Session, gerendertes DOM nur mit rendered: true. Rechtliches bleibt außen vor, das hat der Nutzer im Blick, und der Stack gibt keine Rechtsberatung. Klargestellt ist, dass die Session in die Secret-Datei kommt und nicht nach 1Password. Neu ist ein Kriterium für rendered: true. kind/decision → kind/build, blockiert bleibt es durch #207/#200. Die Kettenzeile ist auf die Form aus #205 gekürzt.

**Changelog:** Die vier Fragen sind entschieden (2026-10-10): mitliefern, nur Session, gerendertes DOM nur mit `rendered: true`. Rechtliches bleibt außen vor, das hat der Nutzer im Blick, und der Stack gibt keine Rechtsberatung. Klargestellt ist, dass die Session in die Secret-Datei kommt und nicht nach 1Password. Neu ist ein Kriterium für `rendered: true`. `kind/decision` → `kind/build`, blockiert bleibt es durch #207/#200. Die Kettenzeile ist auf die Form aus #205 gekürzt.
torben removed the status/blocked label 2026-10-11 18:16:21 +00:00
Author
Owner

Changelog: status/blocked entfernt. Beide Blocker sind geschlossen: #207 (2026-10-10, 9.1.0-beta.1) und #200 (2026-10-11, 10.0.0-beta.3). Die Bau-Sitzung beginnt auf 10.0.0-beta.7 (aed8467).

**Changelog:** `status/blocked` entfernt. Beide Blocker sind geschlossen: #207 (2026-10-10, `9.1.0-beta.1`) und #200 (2026-10-11, `10.0.0-beta.3`). Die Bau-Sitzung beginnt auf `10.0.0-beta.7` (`aed8467`).
Author
Owner

Changelog: Gebaut und publiziert als 10.0.0-beta.8, Commit 5994b2a, verifiziert durch die CI-Läufe 667 und 668, beide grün. Alle Akzeptanzkriterien sind abgehakt. Neu im Body sind die Bau-Entscheidungen, jeweils mit Bau: markiert:

  • Abschnitt fetch mit routes (instance) und fetchers (checkout, names_command).
  • Handler-Block session mit login und signed_in/signed_out, dazu render.
  • Der Handler beschreibt die Session, ob ein Host sie nutzt, entscheidet die Route.
  • Secret session_<host>.
  • preflight --with browser, der Browser liegt in tools/.venv/ms-playwright.
  • Exit 42 bei fehlender Komponente.
  • doctor-Zeile browser.
  • Neue Anleitung docs/how-to/fetch-pages-behind-a-login.md.

Weiterhin offen und nicht blockierend: der Test unter nativem Windows und ein Lauf gegen ein echtes Playwright.

**Changelog:** Gebaut und publiziert als `10.0.0-beta.8`, Commit `5994b2a`, verifiziert durch die CI-Läufe 667 und 668, beide grün. Alle Akzeptanzkriterien sind abgehakt. Neu im Body sind die Bau-Entscheidungen, jeweils mit **Bau:** markiert: - Abschnitt `fetch` mit `routes` (instance) und `fetchers` (checkout, `names_command`). - Handler-Block `session` mit `login` und `signed_in`/`signed_out`, dazu `render`. - Der Handler beschreibt die Session, ob ein Host sie nutzt, entscheidet die Route. - Secret `session_<host>`. - `preflight --with browser`, der Browser liegt in `tools/.venv/ms-playwright`. - Exit 42 bei fehlender Komponente. - `doctor`-Zeile `browser`. - Neue Anleitung `docs/how-to/fetch-pages-behind-a-login.md`. Weiterhin offen und nicht blockierend: der Test unter nativem Windows und ein Lauf gegen ein echtes Playwright.
Author
Owner

Changelog: Abgeschlossen. In der Schlussphase kam ein Doku-Nachzug hinzu: c06bde2, nur docs/, ohne Bump, CI-Lauf 669 grün. Er erklärt die Session als Secret ohne Verweis und sagt, dass die Schutzmaßnahmen rund um 1Password sie nicht abdecken. Die beiden nicht blockierenden Prüfungen (echtes Playwright, natives Windows) liegen jetzt in #212. Der Body ist im Endstand.

**Changelog:** Abgeschlossen. In der Schlussphase kam ein Doku-Nachzug hinzu: `c06bde2`, nur `docs/`, ohne Bump, CI-Lauf 669 grün. Er erklärt die Session als Secret ohne Verweis und sagt, dass die Schutzmaßnahmen rund um 1Password sie nicht abdecken. Die beiden nicht blockierenden Prüfungen (echtes Playwright, natives Windows) liegen jetzt in #212. Der Body ist im Endstand.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: torben/chemenu#169