Commit Graph
1317 Commits
Author SHA1 Message Date
Outis a7a79806dd Merge pull request #457 from outis1one/claude/steam-non-steam-apps-visibility-p1uqc2
wolf: grant /dev/uinput to the Steam container for Steam Input's virt…
2026-09-10 14:51:50 -04:00
Claude 9524c79557 wolf: grant /dev/uinput to the Steam container for Steam Input's virtual controller
Confirmed live: Steam's own controller detection and test screen worked
fine (device listed, buttons fired correctly, Gamepad template applied)
but every actual game — a real Steam title or a non-Steam emulator
shortcut alike — saw no controller at all.

Root cause: Steam Input never hands a game the raw controller device.
It exclusively grabs the raw device for itself and creates its own
synthetic virtual controller via /dev/uinput, then hands that synthetic
device to the game. Reading/testing the raw device (what Steam's own
controller page does) doesn't need uinput; creating the virtual output
device it hands to games does. The 'steam' CATALOG entry only granted
/dev/input/*, /dev/dri/*, /dev/nvidia* via GOW_REQUIRED_DEVICES and had
no devices= list at all — so Steam Input could read the controller but
could never create the virtual device games actually see.

This is the exact same requirement the 'esde' entry already has for
AntiMicroX (also a uinput-based synthetic input tool), including the
same two-part fix noted in its own comment: GOW_REQUIRED_DEVICES alone
only gets the base image's entrypoint script to bind-mount the node —
the container also needs Wolf's own create-time devices= grant to
actually open it. Added /dev/uinput to GOW_REQUIRED_DEVICES and a
devices=['/dev/uinput:/dev/uinput'] entry to both copies of the
'steam' CATALOG dict, matching esde's existing pattern exactly.

update_field() already refreshes an existing app's 'devices' field on
rerun (added specifically for this same esde/AntiMicroX case per its
own comment), so reselecting Steam via 'sudo ./setup.sh wolf' or
'./manage.sh apps' picks this up without a fresh reinstall.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BWYKEERLA1a7gv86Z4W23
2026-09-10 18:49:59 +00:00
Outis 89e8a01d1f Merge pull request #456 from outis1one/claude/pensive-hopper-4c9e7i
anki-deck-periodic.py: fix element-photo fetching hitting Wikipedia's…
2026-09-10 12:59:30 -04:00
Outis f81ce0e0a9 Merge pull request #455 from outis1one/claude/steam-non-steam-apps-visibility-p1uqc2
wolf: fix GE-Proton download URL resolution (malformed URL)
2026-09-10 12:55:57 -04:00
Claude 2070c31cfe anki-deck-periodic.py: fix element-photo fetching hitting Wikipedia's rate limit
Confirmed live by the user: the one-request-per-element loop (per-element
API metadata lookup + per-element image download, no delay between any of
them) got 429'd by Wikimedia partway through a real run of --deck hs.

Fixes:
- Batch pageimage metadata lookups up to 50 titles per MediaWiki query
  instead of one request per element (ensure_element_photos ->
  resolve_pageimage_urls), cutting ~99 requests down to ~2 for a full hs
  run.
- Retry with backoff on 429 (http_get_with_retry), honoring Wikipedia's
  own Retry-After header when present.
- Request thumbnails (piprop=thumbnail) instead of full-resolution
  originals, per Wikimedia's own guidance in the 429 response body.
- Small delay between individual image downloads, which still can't be
  batched (one HTTP request per element's actual image bytes).

Also fixes a real bug caught while unit-testing the new batched
redirect/normalization resolution against a simulated response: the
final-title -> original-input-title lookup had the mapping backwards
(looked up by final title in a dict keyed by input title), which would
have silently dropped every element whose title needed resolving through
a redirect (e.g. Cesium -> Caesium) even after the rate-limit fix.

Still couldn't test against the real Wikipedia API (no network route to
en.wikipedia.org from this sandbox) — verified instead with a local HTTP
server simulating 429-then-200 and a fabricated MediaWiki response with a
redirect chain. Needs a real run to fully confirm.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DyceEVVeQ33EeS6C1PDv5
2026-09-10 16:55:57 +00:00
Claude 52c7609050 wolf: fix GE-Proton download URL resolution (malformed URL)
./manage.sh ge-proton failed live with:
  curl: (3) URL rejected: Malformed input to a URL function

Root cause: the GitHub release JSON is parsed with a plain
grep browser_download_url | grep '\.tar\.gz' | cut -d'"' -f4 pipeline.
Once a release publishes more than one asset whose name contains
".tar.gz" (a second architecture build, a checksum-adjacent file,
etc.), grep returns more than one line and $(...) glues them together
with an embedded newline instead of yielding a single URL — curl then
rejects the whole multi-line string outright. Reproduced synthetically
with a release carrying two .tar.gz assets: the old pipeline emits two
lines where exactly one is expected.

Fixed all three independent copies of this same extraction (manage.sh's
_cache_ge_proton() helper and its ge-proton command, plus install_wolf()'s
own pre-download step) to parse the release JSON for real via python3
instead of pattern-matching the raw text — filtering by an actual
".tar.gz" suffix (not substring) and preferring an x86_64-tagged asset
if more than one still matches, since every container this repo runs
is x86_64. Verified against a synthetic multi-asset release JSON that
the new logic picks exactly one clean URL where the old one produced
two concatenated lines.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BWYKEERLA1a7gv86Z4W23
2026-09-10 16:51:11 +00:00
Outis 38d1fe8f3a Merge pull request #454 from outis1one/claude/pensive-hopper-4c9e7i
anki decks: add multiple-choice shapes variant; add real element phot…
2026-09-10 10:26:38 -04:00
Outis d94c4320a4 Merge pull request #453 from outis1one/claude/steam-non-steam-apps-visibility-p1uqc2
Claude/steam non steam apps visibility p1uqc2
2026-09-10 10:26:07 -04:00
Claude 67282521ac anki decks: add multiple-choice shapes variant; add real element photos to periodic table
shapes_mc (tools/anki-deck-visual.py) is the same shape images as
"shapes" but multiple choice instead of type-the-name, matching the
plain-text A/B/C/D pattern anki-deck-periodic.py's "category" deck
already uses (no clickable UI, since that needs a desktop-only Anki
add-on and breaks on AnkiDroid/AnkiMobile).

periodic prehs/hs (tools/anki-deck-periodic.py) now show each element's
real sample photo alongside every card, fetched once from Wikipedia's
own MediaWiki pageimages API and cached under tools/periodic_images/ —
the one part of this tooling that needs internet access at generation
time; --no-images restores the old text-only cards. Elements 100
(Fermium) through 118 (Oganesson) are excluded, since none has ever
existed in a photographable quantity — every fetch is otherwise
per-element and non-fatal, with progress printed so failures are visible.

This fetch logic could not be exercised against the real Wikipedia API
from this sandbox (no route to en.wikipedia.org here) — --dry-run-tts
now also substitutes a placeholder image so the pipeline is at least
structurally tested end to end. Real-network behavior needs verifying
on an actual run.

Added tools/*.apkg, tools/media_*/, tools/periodic_images/, and
tools/__pycache__/ to .gitignore — all generated/cached locally, never
meant to be committed.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DyceEVVeQ33EeS6C1PDv5
2026-09-10 14:20:24 +00:00
Outis 5cada48172 Merge pull request #452 from outis1one/claude/pensive-hopper-4c9e7i
anki decks: add --skip-ones multiplication/division variant; switch s…
2026-09-10 10:01:26 -04:00
Claude e4e32f20e7 anki decks: add --skip-ones multiplication/division variant; switch shapes/clocks/currency images from SVG to PNG
--skip-ones drops every fact involving 1 (trivial, not worth drilling),
121 cards instead of 144 for both multiplication and division.

SVG images never displayed on AnkiDroid (mobile) despite rendering fine
on desktop Anki — a long-documented, still-open AnkiDroid limitation
(multiple open ankidroid/Anki-Android GitHub issues), not a bug in the
generated markup. Rewrote shape/clock/coin image generation to render
PNG via Pillow instead, preserving the same geometry math (regular
polygons, clock-hand angles including the fractional hour-hand
movement, coin layouts) — card counts and content are unchanged, only
the image format.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DyceEVVeQ33EeS6C1PDv5
2026-09-10 12:28:28 +00:00
Claude 05942fb44a wolf: add fullscreen wrapper for Steam-launched emulators, document controller/crash troubleshooting
Addresses reported symptoms after adding Cemu/Azahar/PCSX2/Dolphin/ES-DE
as non-Steam games in Wolf's Steam app: a launched emulator opens at
roughly half the screen, and the controller doesn't respond.

Half-screen: a non-Steam game launched from Steam is a second top-level
window inside Wolf's single-app Steam Sway session (RUN_SWAY=true) —
Sway's own kiosk config only auto-fullscreens the ONE window it expects
(Steam's own), so anything launched from inside Steam opens at whatever
default size it requests instead. steam-add-nonsteam-game and
steam-setup-frontends now route the launch through a small generated
wrapper (emulators/steam-fullscreen-wrap) that launches the real binary
and repeatedly asks Sway to fullscreen whatever currently has focus for
a few seconds after launch. NOT yet confirmed live against a real Wolf
Steam session — if swaymsg isn't reachable from inside that container,
the wrapper's loop just fails silently (2>/dev/null) and the launch is
unaffected, so this is safe to try either way.

Since every wrapped shortcut now shares the same Exe (the wrapper) with
the real per-emulator target carried in LaunchOptions instead, the
shortcuts.vdf dedupe/appid logic had to move from keying on Exe alone
to the (Exe, LaunchOptions) pair — otherwise adding a second emulator
would have silently overwritten the first one's entry. Also cleans up
a legacy pre-wrapper entry (Exe pointing directly at the same real
binary, no LaunchOptions) when re-adding something added before this
existed, so re-running doesn't leave a stale duplicate tile behind.
Verified with a synthetic shortcuts.vdf: two different emulators keep
distinct entries, re-adding one updates in place, and a legacy
direct-launch entry gets replaced rather than duplicated.

Controller not responding turned out to most likely be Steam Input
applying its own (unconfigured) controller mapping to the new shortcut
rather than passing raw input through — documented the actual fix
(Big Picture -> shortcut -> Controller Options -> Gamepad template or
disable Steam Input) since this is a Steam-side per-shortcut setting
this installer has no way to preconfigure via shortcuts.vdf.

Also documented the third reported symptom (Cemu/Azahar/PCSX2 tiles
appear but crash on launch, while Dolphin/ES-DE work) as a separate,
not-yet-diagnosed issue, with the exact docker exec command to capture
the real error directly (no wrapper, no Steam) since guessing at a fix
without that output isn't reliable.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BWYKEERLA1a7gv86Z4W23
2026-09-10 03:40:42 +00:00
Claude d41da6356d wolf: fix Steam catalog entry missing roms/bios/retro-home mounts
install_wolf()'s own embedded copy of the app CATALOG dict (used the
first time Wolf apps are injected into config.toml) had drifted from
the copy in the generated manage.sh's own 'apps' command: the 'steam'
entry only mounted steam-cache/ and emulators/, missing roms/, saves/,
media/, bios/, retro-home/, retro-home-data/, retroarch/,
esde-custom-systems/, and esde-settings/ — all present in the
manage.sh copy already, per this repo's own existing comment
explaining exactly why they're needed.

Confirmed live: this is why ES-DE (or Dolphin) added to Steam as a
non-Steam game saw none of the ROMs/BIOS files the esde app finds, and
had no persisted controller/input config either, since that also lives
under the now-unmounted retro-home/esde-settings paths.

Diffing the two CATALOG copies end-to-end confirmed this was the only
drift — every other app entry (esde, lutris, retroarch, prismlauncher,
kodi, firefox, desktop) already matched.

update_field() already refreshes an existing app's 'mounts' field on
every rerun, so re-selecting Steam via 'sudo ./setup.sh wolf' or
'./manage.sh apps' picks up the fix without a fresh reinstall.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BWYKEERLA1a7gv86Z4W23
2026-09-10 03:30:12 +00:00
Outis 8f83c96ca7 Merge pull request #451 from outis1one/claude/steam-non-steam-apps-visibility-p1uqc2
wolf: fix ES-DE GitLab AppImage download resolving no URL
2026-09-09 23:17:10 -04:00
Claude 750c55a4d4 wolf: fix ES-DE GitLab AppImage download resolving no URL
Two compounding bugs in _wolf_download_emulator_appimage_gitlab() made
the ES-DE AppImage download always fail with "Could not resolve
download URL", confirmed live against the real GitLab API:

1. The asset filter checked url.endswith(".AppImage"), but GitLab's own
   release-asset URL is an opaque .../package_files/<id>/download link
   with no filename in it at all — only the asset's own "name" field
   (e.g. "ES-DE_x64.AppImage") carries the real filename. Filtering on
   the URL suffix matched nothing, even though the latest ES-DE release
   genuinely ships x64/aarch64/SteamDeck AppImage assets.

2. Even with the URL resolved, the download target was built as
   $dir/$(basename "$_url"), which for that same opaque URL evaluates
   to just "download" instead of the real filename — breaking every
   downstream step that looks for a *.AppImage file (the ES-DE.AppImage
   symlink creation, and the "already downloaded" idempotency check on
   a later rerun).

Fixed by filtering on the asset's own "name" field and threading that
name through (tab-separated from the URL) to use as the actual saved
filename. Verified end-to-end against the live GitLab API: resolves to
ES-DE_x64.AppImage and downloads a real, correctly-arched ELF binary.

(Also had to drop an f-string in the same python snippet — pre-3.12
Python disallows a backslash inside an f-string's {} expression, and
separately this whole snippet is wrapped in a bash single-quoted
string, so it can't contain single quotes at all either. Plain string
concatenation avoids both constraints.)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BWYKEERLA1a7gv86Z4W23
2026-09-10 03:16:27 +00:00
Outis a03f976eb1 Merge pull request #450 from outis1one/claude/steam-non-steam-apps-visibility-p1uqc2
wolf: fix auto steam-setup-frontends invocation ($0 resolution)
2026-09-09 23:16:17 -04:00
Outis 2820471654 Merge pull request #448 from outis1one/claude/pensive-hopper-4c9e7i
Add anki-progress service + anki-deck-*.py generation tools
2026-09-09 23:15:40 -04:00
Claude 92e9ee19a7 wolf: fix auto steam-setup-frontends invocation ($0 resolution)
install_wolf()'s new auto-wire-up called it as 'bash manage.sh
steam-setup-frontends', which sets $0 inside manage.sh to the bare
string 'manage.sh' (no path). steam-setup-frontends re-invokes itself
per emulator via "$0" steam-add-nonsteam-game ..., and a bare
'manage.sh' with no '/' triggers a $PATH lookup instead of running the
local file — confirmed live: every emulator already downloaded (Azahar,
PCSX2, Cemu, Dolphin) failed with 'manage.sh: command not found' and
the run ended with a false "No emulator AppImages found" even though
they were sitting right there in emulators/.

Invoking it as './manage.sh' instead keeps $0 as './manage.sh', which
resolves correctly for the nested re-invocation, matching how every
other caller in this file already runs it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BWYKEERLA1a7gv86Z4W23
2026-09-10 03:06:00 +00:00
Outis 15f594e4eb Merge pull request #449 from outis1one/claude/steam-non-steam-apps-visibility-p1uqc2
wolf: auto-add downloaded emulators to Steam's library on rerun
2026-09-09 23:01:18 -04:00
Claude cf879f0090 wolf: auto-add downloaded emulators to Steam's library on rerun
Downloading an emulator AppImage (Cemu, Azahar, PCSX2, Dolphin, ES-DE,
RetroArch) only ever dropped the file into emulators/ — getting it into
Steam's own shortcuts.vdf as a non-Steam game still needed a separate,
manually-typed manage.sh command per emulator, and steam-setup-frontends
only ever covered ES-DE/RetroArch, leaving Cemu out entirely.

- steam-setup-frontends now scans emulators/ and adds every AppImage it
  finds (deduping a fixed-name symlink like ES-DE.AppImage against the
  real versioned file it points at), instead of only handling ES-DE and
  RetroArch by name.
- install_wolf() now calls it automatically at the end of a run whenever
  Steam already has a signed-in profile, so the flow is just: run
  setup.sh wolf, sign into Steam via Moonlight, run it again — no
  separate manage.sh command to remember.
- Docs (manage.sh help text, printed install summary, generated
  README.md) updated to match.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013BWYKEERLA1a7gv86Z4W23
2026-09-10 02:47:18 +00:00
Claude 102b0405b4 Drop family framing from anki-progress; add anki-deck-*.py tools
anki-progress.sh and its embedded app.py assumed a family/kids use case
(dashboard title, ntfy topic default, Authelia warning text, comments)
that was never actually stated — nothing in this repo should assume who
the accounts belong to. Retitled to plain "Anki Progress" throughout,
default ntfy topic changed from family-anki to anki-progress, and every
"family member" reference reworded to "account".

Also adds tools/anki-deck-math.py, tools/anki-deck-periodic.py, and
tools/anki-deck-visual.py — the Anki deck-generation scripts developed
earlier in this session, now committed as standalone, self-documented
tools (same tools/*.{sh,py} convention as tools/dedupe-finder.py) rather
than living only in chat. Each script's own header docstring carries the
full one-time setup (venv, genanki + piper-tts, downloading a voice) and
usage — anki-deck-periodic.py and anki-deck-visual.py point back to
anki-deck-math.py's copy rather than repeating it three times. Content is
generic (multiplication/division/addition/subtraction/fractions/decimals,
the periodic table, shapes/clocks/coin-counting) — nothing here assumes
who's using it or why.

Re-verified after the rename: the embedded app.py still passes its full
logic test suite once written out by the installer, and all three
tools/anki-deck-*.py scripts still build correct decks under
--dry-run-tts after their docstrings were rewritten.

Adds a README.md section pointing at the three scripts, and updates the
anki-progress Services table entry to drop "family" from its wording.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DyceEVVeQ33EeS6C1PDv5
2026-09-09 19:34:48 +00:00
Claude 9c7a054d97 Add anki-progress service — family Anki study-progress dashboard
Read-only dashboard + ntfy notifications for an anki-sync-server
instance, following this repo's standard Docker-service template
(multi-instance, port scanning, DRY_RUN, update/fresh/cancel).

Dashboard shows reviews today/this week, accuracy, and current streak
per account. A background loop detects when a study session starts
(first review after a configurable inactivity gap, default 30 min) and
sends one ntfy notification a configurable delay later (default 10 min,
per request) if the session is still going — not on every review, and
not twice for the same session.

Reads every account's collection.anki2 with SQLite's read-only mode
(file:...?mode=ro) — never opens for write, so it can't corrupt or lock
out the live sync server or a syncing client. Verified this concurrently
against a real writer with no lock conflict, plus the streak/session/
notify-state logic against synthetic review timelines covering gapped
streaks, multi-session boundaries, and the no-duplicate-notification
requirement, before ever writing the installer around it.

Requires an anki-sync-server instance (hard dependency, checked at
install time, chains only that one direction per this repo's
"Chaining into another service" convention) and auto-detects a local
ntfy install to reach it directly over caddy_net instead of requiring
a public URL. Follows security-dashboard.sh's Authelia pattern: local
Authelia used automatically, remote Authelia offered otherwise, since
this exposes every family member's personal study activity.

Updates the Services table in README.md per the three-step rule.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DyceEVVeQ33EeS6C1PDv5
2026-09-09 19:18:48 +00:00
Outis c63237a3db Merge pull request #447 from outis1one/claude/pensive-hopper-4c9e7i
Fix anki-sync-server container failing to start: data/ ownership
2026-09-09 12:01:00 -04:00
Claude 575c4ac185 Fix anki-sync-server container failing to start: data/ ownership
afrima/anki-sync-server is built on gcr.io/distroless/static-debian12:
nonroot — the process always runs as that image's fixed nonroot UID/GID
(65532), never as ACTUAL_USER, and distroless ships no shell so nothing
inside the container can chown its own data dir at startup.

The installer's final chown gave the whole instance directory to
ACTUAL_USER, including ./data, which the container then can't write to
— it fails outright the moment it tries to create anything under /data
(e.g. a new user's collection), not just at sync time. Confirmed live.

Fix: re-chown ./data to 65532:65532 specifically, applied *after* the
existing ACTUAL_USER chown (not before — that call recurses over the
whole instance dir and would just clobber it). docker-compose.yml/.env/
README.md stay owned by ACTUAL_USER as before. The same fix is applied
in the "update" path so an already-broken existing install self-heals
on the next non-destructive update, without touching its port, Caddy
config, or accounts.

Verified live: fresh install now leaves data/ owned by 65532:65532
while the rest of the instance dir stays ACTUAL_USER; simulating a
pre-fix broken install (data/ owned by root) and running "update"
correctly repairs it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DyceEVVeQ33EeS6C1PDv5
2026-09-09 15:50:10 +00:00
Outis a339d5fbb0 Merge pull request #446 from outis1one/claude/pensive-hopper-4c9e7i
Add anki-sync-server service — self-hosted Anki flashcard sync
2026-09-09 11:02:35 -04:00
Claude c2cf5bfe69 Add sync-account management menu to anki-sync-server
Re-running the installer against an existing install now offers "Manage
sync accounts" (add / remove / rotate a password) as a first-class menu
option, instead of requiring a hand-edit of .env kept in lockstep with
docker-compose.yml.

_anki_rewrite_account_block() regenerates the SYNC_USERn lines in both
files from the current account list, always renumbered contiguously from
1, and is shared by the initial install and every management mutation so
they can't drift apart. Only SYNC_USER/ANKI_SYNC_* lines are touched —
port, Caddy wiring, and everything else in either file is left alone.

Verified against a stubbed docker/ss sandbox: add, remove (mid-list, with
renumbering), and password rotation all produce the expected .env/
docker-compose.yml diffs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DyceEVVeQ33EeS6C1PDv5
2026-09-09 14:58:32 +00:00
Claude 92f8503d48 Add anki-sync-server service — self-hosted Anki flashcard sync
Docker-based sync backend for the Anki app (afrima/anki-sync-server, the
official Rust sync server). Supports multiple independent accounts per
instance, the repo's multi-instance pattern, port collision avoidance,
Caddy wiring, and update/fresh/cancel reinstall detection.

No Authelia gate — this is a raw sync API the Anki client talks to, not
a browser session, so a forward_auth portal in front of it would just
break every sync request; SYNC_USER1/SYNC_USER2/... is its own auth
boundary.

Companion services/anki-sync-server.md covers client setup (Desktop,
AnkiDroid, AnkiMobile) and the Quizlet import/export walkthrough.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014DyceEVVeQ33EeS6C1PDv5
2026-09-09 14:07:31 +00:00
Outis d95bd7fd3c Merge pull request #445 from outis1one/claude/gitea-webhook-base-url-9ofg1n
Fix inactivity sync being skipped when remember_me is unchanged
2026-09-09 09:45:38 -04:00
Claude 3cd9a1ece3 Fix _authelia_set_remember_me skipping inactivity sync on a no-op remember_me
The "already equal" early-exit compared only remember_me against the typed
value, so re-entering an unchanged remember_me (the exact case for anyone
who'd set it before the earlier fix existed) skipped the inactivity write
entirely, leaving inactivity stuck at its old mismatched value. Now only
skips when both keys already match the typed duration.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0148pWopbt3tEKZWHYuHTb3c
2026-09-09 13:20:32 +00:00
Outis 5c13054cdd Merge pull request #444 from outis1one/claude/gitea-webhook-base-url-9ofg1n
Fix Authelia remember_me not actually keeping sessions alive
2026-09-09 09:16:51 -04:00
Outis 7d5674aad8 Merge pull request #443 from outis1one/claude/wolf-controller-setup-vl05t5
wolf: fix stale command list in the post-install summary, surface the…
2026-09-09 09:16:19 -04:00
Claude 2d82b2b278 Fix Authelia remember_me not actually keeping sessions alive
inactivity (idle timeout) was independent of remember_me and stayed at a
much shorter default (2h), so a long remember_me got silently overridden
by ordinary daily gaps between visits. install_authelia()'s template now
defaults inactivity to match remember_me, and the "Change remember me
duration" menu option now writes both keys together instead of just one.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_0148pWopbt3tEKZWHYuHTb3c
2026-09-08 22:43:20 +00:00
Claude 08a2617b06 wolf: fix stale command list in the post-install summary, surface the Steam Input workflow
The final echo summary still advertised a removed `apps` command and left out
everything added since (cores, backup, controllers, steam-add-nonsteam-game,
steam-setup-frontends, cemu-clone-controller, cemu-sync-controllers,
install-completion, etc.). Also add a short pointer to the Steam-as-4-controller-hub
workflow (documented in depth further down in README.md) right in the install
summary, since it's currently only discoverable by reading the generated README.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01V99t5754SyXdnMTpVe2ba5
2026-09-07 22:29:55 +00:00
Outis 39387b5e0f Merge pull request #442 from outis1one/claude/wolf-cemu-four-controllers-373xn8
Claude/wolf cemu four controllers 373xn8
2026-09-05 15:00:15 -04:00
Claude e67ac50c61 wolf: document the ES-DE/RetroArch-in-Steam workflow in the generated README
The steam-setup-frontends command and the ES-DE/RetroArch AppImage
download step had no matching section in the ~/docker/wolf/README.md
content this file generates, unlike every other manage.sh command. Adds
one, alongside the existing Cemu-in-Steam section: why you'd want it,
how the mounts/cores are already shared, and the two honest caveats
(returning to Steam from ES-DE only works via this path, and whether
controller mappings sync across separately-paired Wolf clients is
expected but unconfirmed).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Z4nqULUEipWNgBsPoSuAb
2026-09-05 18:58:04 +00:00
Claude 3ebbeba672 wolf: add steam-setup-frontends to wait for Steam sign-in then auto-wire ES-DE/RetroArch
Steam Guard's QR-code sign-in can't be scripted (needs a phone approving
a prompt), so this polls for it instead: starts Wolf if needed, waits
for Steam's userdata/ to appear (or proceeds immediately if already
signed in), then re-invokes the existing steam-add-nonsteam-game command
for whichever of ES-DE.AppImage/RetroArch.AppImage was downloaded during
install. Points to ./manage.sh cores all for the shared cores/shaders/
overlays directory rather than duplicating that download logic.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Z4nqULUEipWNgBsPoSuAb
2026-09-04 21:30:24 +00:00
Claude 07394769ca wolf: add optional ES-DE/RetroArch AppImage downloads for Steam Input
Both projects ship official standalone Linux AppImages separate from
the esde/retroarch Wolf catalog containers this repo already runs.
Adding either one to Steam as a non-Steam game (steam-add-nonsteam-game,
now reaching the same roms/saves/bios/retro-home/retroarch mounts as
the esde/retroarch containers) lets Steam Input assign a 4th controller
its own identity by device path, past Wolf's 3-concrete-pad-type ceiling.

ES-DE is hosted on GitLab rather than GitHub, so this adds a GitLab
Releases API counterpart to the existing GitHub-based download helper.
RetroArch's own buildbot doesn't publish through either API, so this
uses hizzlekizzle/RetroArch-AppImage, the community nightly-build
project the AppImage catalogs themselves point to (flagged as
third-party, same treatment this file already gives the Dolphin
community build). Both downloads are opt-in (default no) and symlink
to a fixed filename so steam-add-nonsteam-game's case-sensitive
substring match finds them regardless of the vendor's own asset name.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Z4nqULUEipWNgBsPoSuAb
2026-09-04 21:04:00 +00:00
Claude ae939c4085 wolf: mount ROMs/saves/BIOS/retro-home/retroarch into the Steam container
Lets a manually-downloaded ES-DE or RetroArch AppImage, added via
./manage.sh steam-add-nonsteam-game, see the same library, cores, and
ES-DE settings/custom systems (TI-99, Wii U) the esde/retroarch
containers already have, instead of starting from an empty config.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015Z4nqULUEipWNgBsPoSuAb
2026-09-04 14:15:33 +00:00
Outis 28996eff57 Merge pull request #441 from outis1one/claude/wolf-pair-port-conflict-7nz8qg
wolf: fix cemu-sync-controllers silently finding zero joysticks
2026-09-03 22:48:40 -04:00
Claude 93efe0d607 wolf: fix cemu-sync-controllers silently finding zero joysticks
docker exec needs an explicit -i flag to forward stdin into the
container process; without it, the heredoc piped into `python3 -`
never reached the containerized script, which ran empty and printed
nothing. cemu-sync-controllers then misread that empty output as
"SDL reported zero joysticks" — a false negative with a working set of
4 controllers already confirmed live in ES-DE, not an actual SDL or
GUID problem. Root-caused against the user's own live output: the
exact same probe script, run directly (not via the manage.sh command),
found real device nodes (js0-js4) present in the same container at the
same time cemu-sync-controllers reported zero.

Audited every other docker exec call in this file for the same
stdin-via-heredoc pattern; this was the only one missing -i.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLX1yYKJExGSXmgUhxKQG6
2026-09-04 02:48:08 +00:00
Outis 79861c72f3 Merge pull request #440 from outis1one/claude/wolf-pair-port-conflict-7nz8qg
wolf: add ./manage.sh cemu-sync-controllers — full auto-clone via SDL
2026-09-03 22:40:33 -04:00
Claude 2422ce1385 wolf: add ./manage.sh cemu-sync-controllers — full auto-clone via SDL
cemu-clone-controller still needed one manual Cemu GUI step per new
device (click +, select it, map one button, Save) just to learn its
real uuid. This eliminates that too: instead of getting the uuid from
Cemu, it asks SDL directly (SDL_JoystickGetDeviceGUID/GetGUIDString via
ctypes against libSDL2 — the exact library Cemu itself links against)
for the live GUID of every controller connected to the active session,
then clones the proven-working mapping onto each one automatically.

Confirmed live end-to-end on the user's real box: querying SDL this
way inside a running ES-DE session reproduced the exact uuids Cemu had
already written by hand for two different controllers (Nintendo Switch
Pro and Xbox One S) — byte-for-byte identical to their real
controllerProfiles/controllerN.xml. This is what makes trusting SDL as
the uuid source safe, after an earlier attempt to reverse-engineer the
GUID's CRC16 portion by hand (tried 7 different CRC16 variants) failed
to match either device.

Also improved cemu-clone-controller's sibling: the synced profile's
display_name now comes from SDL's own live device name (when the probe
reports one) rather than always inheriting the mapping template's name,
which was a real but purely cosmetic issue caught while re-testing.

Verified end-to-end in an isolated harness against the real captured
controller0.xml/controller1.xml: idempotent skip on already-synced
slots (no needless backups, existing files left byte-identical), a new
slot correctly cloned with the live name and same 24-pair mapping set,
and valid XML output.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLX1yYKJExGSXmgUhxKQG6
2026-09-04 02:25:22 +00:00
Outis 6fc6c3b84d Merge pull request #439 from outis1one/claude/wolf-pair-port-conflict-7nz8qg
Claude/wolf pair port conflict 7nz8qg
2026-09-03 20:52:47 -04:00
Claude e6522eadec wolf: add ./manage.sh cemu-clone-controller
Cemu's own Wolf-facing controller slots (up to 4) still each need a
separate manual bind through Cemu's Input Settings even after
./manage.sh controllers makes them distinct SDL devices — that dialog's
Save button is documented elsewhere in this README as getting cut off
the viewport, making binding all 4 slots by hand painful.

Validated the underlying assumption against two real, separately
hand-mapped profiles pulled live from a working install (Nintendo
Switch Pro on controller0.xml, Xbox One S on controller1.xml): both
contain the exact same 24 <mapping>/<button> pairs, just in a different
order — real proof Cemu's mapping format is device-agnostic and
order-independent, not just the README's prior unverified claim.

New command clones a proven-working <mappings> block onto a new
device's controllerN.xml, needing only that device's own real <uuid>
(which still has to come from Cemu itself — a hand-computed SDL GUID
risks not matching what SDL actually reports for the live device, so
this never guesses one). Getting that uuid only needs a single-button
minimal bind in Cemu's UI, not full mapping, since only the uuid gets
kept from it.

Verified end-to-end against the real captured controller0.xml/
controller1.xml content: cloning controller0's mappings onto
controller1's real uuid reproduces the exact same 24-pair set Cemu
itself wrote, and the output validates as well-formed XML.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLX1yYKJExGSXmgUhxKQG6
2026-09-04 00:45:22 +00:00
Claude 2dcfaafc87 wolf: add Cemu TV-audio-stuck diagnostic block to the generated README
Copyable follow-up for the existing Cubeb/PulseAudio hang note: checks
whether a full Wolf restart has actually happened, whether PulseAudio
inside the Desktop container has any sinks at all, and pulls Cemu's own
log.txt plus its current <Audio> settings.xml block — narrows "stuck on
Disabled" down to a container-level audio problem vs. Cemu's own
device-switch path before guessing at a fix.

Verified the heredoc escaping by rendering the write_readme block through
a real bash heredoc and confirming $WOLF_DIR interpolates while every
other $ stays literal in the output.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLX1yYKJExGSXmgUhxKQG6
2026-09-03 19:23:20 +00:00
Outis 33e4f64d69 Merge pull request #438 from outis1one/claude/wolf-pair-port-conflict-7nz8qg
Claude/wolf pair port conflict 7nz8qg
2026-09-03 14:58:11 -04:00
Claude cbfc28c2dd wolf: fix ES-DE ROMDirectory staying blank despite the self-healing patch
Root-caused against the user's own live output: the settings file had
<string name="ROMDirectory" value="" /> — the key was present, just
empty. The self-healing check only tested whether the string
"ROMDirectory" appeared anywhere in the file, so a key that exists with
a blank value (ES-DE can write this itself if its own first-run "select
ROM directory" step goes unanswered in a headless Moonlight session,
saving an empty path back over GOW's template) looked "already
populated" and got skipped — leaving ROM discovery broken even after
the fix had run.

Now ROMDirectory is independently forced to /ROMs whenever its value is
blank, on top of (not instead of) the existing RunInBackground patch —
no longer gated on the key's mere presence. Verified against the exact
reported bug (ROMDirectory present but blank) plus the existing missing-
file, already-correct, and idempotent-rerun scenarios in isolated /tmp
harnesses before touching the real script.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLX1yYKJExGSXmgUhxKQG6
2026-09-03 18:57:29 +00:00
Claude a212be09c3 wolf: add ./manage.sh steam-add-nonsteam-game, mount emulators/ into Steam
Adding an emulator (Cemu, etc.) to Steam as a non-Steam game previously
needed Steam's own Big Picture file-browser flow through Moonlight. This
writes the shortcut directly into Steam's binary shortcuts.vdf instead,
matching this repo's existing no-manual-wizard pattern (ge-proton,
install-ea-app). Motivated by wanting to test whether Steam Input's
per-device controller tracking (distinct device paths, not SDL GUIDs)
can hand Cemu 4 explicitly-assigned controllers where ES-DE/Cemu's own
SDL-based handling can't tell identical controllers apart.

- New generic binary VDF (KeyValues) parser/serializer: round-trips any
  existing shortcuts.vdf entries byte-for-byte and only inserts/replaces
  the one entry matching the given Exe path, so it's safe against a file
  that already has real, hand-configured shortcuts. Verified in isolated
  /tmp harnesses: fresh file, idempotent re-add, a second distinct entry,
  and preserving a synthetic pre-existing GUI-set entry (icon,
  LaunchOptions, tags) untouched.
- Steam's own CATALOG entry had no mount for emulators/ at all (unlike
  esde/retroarch) — added emulators:/home/retro/Applications so an
  AppImage is actually reachable from inside the Steam container. Like
  the ES-DE settings mount, this only takes effect on a freshly created
  WolfSteam container (Wolf reuses existing ones) — noted in the README.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLX1yYKJExGSXmgUhxKQG6
2026-09-03 18:52:31 +00:00
Outis 8a9241f0ff Merge pull request #437 from outis1one/claude/wolf-pair-port-conflict-7nz8qg
wolf: make ES-DE RunInBackground fix fully self-healing, no manual step
2026-09-03 14:40:09 -04:00
Claude 5893346625 wolf: make ES-DE RunInBackground fix fully self-healing, no manual step
Previous version only patched an already-populated es_settings.xml,
requiring a manual "connect via Moonlight once" step before the fix
could apply — the user correctly pointed out the script can't know
whether that step has been done, so a manual dependency isn't a real
fix. This embeds GOW's own real es_settings.xml template (fetched from
games-on-whales/gow, RunInBackground pre-flipped false) and writes it
directly whenever the on-disk file is missing or is the old broken
stub (detected by the absence of ROMDirectory, which only a real
GOW/ES-DE-written file has). An already-populated file still only gets
the one RunInBackground line patched, leaving everything else alone.

Verified against all 4 scenarios in isolated /tmp harnesses before
touching the file: missing file, the exact broken stub from the user's
regression, GOW's real full template, and idempotent re-runs.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VLX1yYKJExGSXmgUhxKQG6
2026-09-03 18:35:44 +00:00