Document plugin helper agent workflow
This commit is contained in:
@@ -1,15 +1,19 @@
|
|||||||
---
|
---
|
||||||
name: install-beatsaber-plugin
|
name: install-beatsaber-plugin
|
||||||
description: Install or update a Beat Saber plugin in the plugin-helper repo by using the local helper workflow. Use when the user asks to add, install, update, bump, lock, or manage a Beat Saber plugin release for a BSManager instance. The user must provide an explicit GitHub release URL in the prompt; if no release URL is present, ask for one and do not infer, search for, or substitute a different repository.
|
description: Install or update a Beat Saber plugin in the plugin-helper repo by using the local helper workflow. Use when the user asks to add, install, update, bump, lock, bootstrap BSIPA, or manage a Beat Saber plugin release for a BSManager instance. Prefer upstream GitHub release artifacts for normal plugins; use BeatMods primarily as compatibility/dependency metadata, with CDN artifacts only for inaccessible upstream assets, BeatMods-only packages, or framework/library dependencies.
|
||||||
---
|
---
|
||||||
|
|
||||||
# Install Beat Saber Plugin
|
# Install Beat Saber Plugin
|
||||||
|
|
||||||
Use the repository's own `plugin-helper` commands to manage plugins for BSManager instances. Do not manually copy release files into the game instance except to undo your own mistaken install before rerunning the helper.
|
Use the repository's own `plugin-helper` commands to manage plugins for BSManager instances whenever the helper supports the operation. Do not manually copy release files into the game instance except:
|
||||||
|
|
||||||
|
- to bootstrap BSIPA/core packages before the helper has a first-class bootstrap command
|
||||||
|
- to undo your own mistaken install before rerunning the helper
|
||||||
|
|
||||||
## Hard Guardrail
|
## Hard Guardrail
|
||||||
|
|
||||||
Require an explicit GitHub release URL from the user's prompt before selecting any release or repository.
|
For ordinary GitHub-hosted plugins, require an explicit GitHub release URL from
|
||||||
|
the user's prompt before selecting any release or repository.
|
||||||
|
|
||||||
- If the prompt does not contain a GitHub release URL, stop and ask the user for it.
|
- If the prompt does not contain a GitHub release URL, stop and ask the user for it.
|
||||||
- Do not search the web to discover a repository or "correct" release URL.
|
- Do not search the web to discover a repository or "correct" release URL.
|
||||||
@@ -17,6 +21,17 @@ Require an explicit GitHub release URL from the user's prompt before selecting a
|
|||||||
- If the provided URL is a general releases page, use that repo's release API and choose the latest non-draft, non-prerelease release unless the user asks for a specific tag/version.
|
- If the provided URL is a general releases page, use that repo's release API and choose the latest non-draft, non-prerelease release unless the user asks for a specific tag/version.
|
||||||
- If the provided URL is a tag URL, use that exact tag.
|
- If the provided URL is a tag URL, use that exact tag.
|
||||||
|
|
||||||
|
Exception: if the user explicitly asks to bootstrap a Beat Saber version or
|
||||||
|
install verified mods without providing GitHub URLs, use BeatMods metadata to
|
||||||
|
identify compatible versions and dependency closure. Still prefer upstream
|
||||||
|
GitHub release artifacts when BeatMods exposes a `gitUrl` and a matching
|
||||||
|
release/asset can be found. Use BeatMods CDN artifacts only when the upstream
|
||||||
|
artifact is inaccessible, no matching upstream release asset exists, the package
|
||||||
|
is effectively BeatMods-only, or the package is a framework/library dependency
|
||||||
|
such as .NET assemblies. Record the artifact source plus BeatMods `modVersion`,
|
||||||
|
version id, `zipHash`, dependencies, and supported game version in the repo
|
||||||
|
notes/lock data.
|
||||||
|
|
||||||
Accepted URL shapes include:
|
Accepted URL shapes include:
|
||||||
|
|
||||||
```text
|
```text
|
||||||
@@ -44,6 +59,7 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
|
|||||||
sed -n '1,220p' src/plugin_helper/models.py
|
sed -n '1,220p' src/plugin_helper/models.py
|
||||||
sed -n '1,220p' registry/plugins.toml
|
sed -n '1,220p' registry/plugins.toml
|
||||||
sed -n '1,220p' locks/<instance>.lock.toml
|
sed -n '1,220p' locks/<instance>.lock.toml
|
||||||
|
sed -n '1,220p' docs/SMOKETEST.md
|
||||||
```
|
```
|
||||||
|
|
||||||
3. Determine the instance.
|
3. Determine the instance.
|
||||||
@@ -54,9 +70,42 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
|
|||||||
PYTHONPATH=src python -m plugin_helper instances
|
PYTHONPATH=src python -m plugin_helper instances
|
||||||
```
|
```
|
||||||
|
|
||||||
4. Resolve the release from the user-provided URL only.
|
4. Resolve the release source.
|
||||||
|
|
||||||
For GitHub URLs, derive `<owner>/<repo>` and optional `<tag>` from the URL. Query the GitHub API directly for metadata:
|
For BeatMods bootstrap or verified packages, query BeatMods with a browser-like user agent:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python - <<'PY'
|
||||||
|
import json, urllib.request
|
||||||
|
game_version = "<instance>"
|
||||||
|
url = f"https://beatmods.com/api/mods?status=verified&gameVersion={game_version}&gameName=BeatSaber&platform=steampc"
|
||||||
|
req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0 plugin-helper"})
|
||||||
|
with urllib.request.urlopen(req, timeout=20) as response:
|
||||||
|
data = json.load(response)
|
||||||
|
mods = data["mods"] if isinstance(data, dict) and "mods" in data else data
|
||||||
|
print(json.dumps(mods, indent=2)[:20000])
|
||||||
|
PY
|
||||||
|
```
|
||||||
|
|
||||||
|
BeatMods dependency entries are mod-version ids. Resolve the selected mod's
|
||||||
|
dependency closure before downloading. For each resolved package, prefer its
|
||||||
|
upstream `gitUrl` release artifacts when a matching release asset exists.
|
||||||
|
Fall back to BeatMods CDN only for inaccessible/missing upstream assets,
|
||||||
|
BeatMods-only packages, or framework/library dependencies. CDN URLs are:
|
||||||
|
|
||||||
|
```text
|
||||||
|
https://beatmods.com/cdn/mod/<zipHash>.zip
|
||||||
|
```
|
||||||
|
|
||||||
|
For BSIPA bootstrap, expect the archive to contain root-relative `IPA/` and
|
||||||
|
`IPA.exe` files whether sourced from GitHub or BeatMods. Extract it into the
|
||||||
|
instance root and run `IPA.exe -n` under the same Proton environment used by
|
||||||
|
the smoketest. This creates/copies the root `winhttp.dll` and root `Libs/`
|
||||||
|
substrate that IPA needs.
|
||||||
|
|
||||||
|
For GitHub URLs, resolve the release from the user-provided URL only.
|
||||||
|
|
||||||
|
Derive `<owner>/<repo>` and optional `<tag>` from the URL. Query the GitHub API directly for metadata:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -sS https://api.github.com/repos/<owner>/<repo>/releases
|
curl -sS https://api.github.com/repos/<owner>/<repo>/releases
|
||||||
@@ -85,7 +134,7 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
|
|||||||
|
|
||||||
- `dll-to-plugins`: asset is a single `.dll` that belongs in `Plugins/`.
|
- `dll-to-plugins`: asset is a single `.dll` that belongs in `Plugins/`.
|
||||||
- `bsipa-zip`: zip top-level paths are only `IPA/`, `Libs/`, or `Plugins/`.
|
- `bsipa-zip`: zip top-level paths are only `IPA/`, `Libs/`, or `Plugins/`.
|
||||||
- `root-zip`: zip contains valid game-root paths outside the BSIPA top-level set.
|
- `root-zip`: zip contains valid game-root paths outside the BSIPA top-level set. Use this for BSIPA/bootstrap archives because `IPA.exe`, `IPA.runtimeconfig*.json`, and root `winhttp.dll` are game-root files.
|
||||||
- `zip-to-pending`: only when the release is intended for `IPA/Pending/`.
|
- `zip-to-pending`: only when the release is intended for `IPA/Pending/`.
|
||||||
- `manual`: do not use for installable releases.
|
- `manual`: do not use for installable releases.
|
||||||
|
|
||||||
@@ -140,6 +189,28 @@ https://github.com/<owner>/<repo>/releases/download/<tag>/<asset>
|
|||||||
|
|
||||||
Use `PYTHONPATH=src`; plain `python -m unittest` may fail in this source-layout repo.
|
Use `PYTHONPATH=src`; plain `python -m unittest` may fail in this source-layout repo.
|
||||||
|
|
||||||
|
For live Beat Saber validation, follow `docs/SMOKETEST.md`. Do not rely on
|
||||||
|
`timeout` to kill the full game process tree. Prefer the documented
|
||||||
|
foreground Proton launch with a background watchdog that sleeps for the smoke
|
||||||
|
window, then terminates Beat Saber by process name. Confirm `Logs/_latest.log`
|
||||||
|
has the expected IPA/plugin lines. If the game remains open after the
|
||||||
|
watchdog cleanup, say so and ask the user to close it manually rather than
|
||||||
|
leaving the turn with Beat Saber running.
|
||||||
|
|
||||||
|
For BSIPA/SongCore bootstrap, expected successful log lines include:
|
||||||
|
|
||||||
|
```text
|
||||||
|
Game version <version>
|
||||||
|
Loading plugins from Plugins and found <n>
|
||||||
|
Beat Saber IPA (BSIPA): <version>
|
||||||
|
SongCore (SongCore): <version>
|
||||||
|
```
|
||||||
|
|
||||||
|
Warnings about older mod target game-version metadata can be acceptable when
|
||||||
|
BeatMods verified that exact package for the target Beat Saber version, but
|
||||||
|
record them in the tracker or roadmap. Also record when a BeatMods CDN
|
||||||
|
artifact was used so it can be migrated to upstream GitHub later if possible.
|
||||||
|
|
||||||
9. Final response.
|
9. Final response.
|
||||||
|
|
||||||
Include:
|
Include:
|
||||||
|
|||||||
@@ -0,0 +1,49 @@
|
|||||||
|
# AGENTS.md
|
||||||
|
|
||||||
|
Guidance for coding agents working in this repo.
|
||||||
|
|
||||||
|
## Project Shape
|
||||||
|
|
||||||
|
- This repo manages Beat Saber plugins for BSManager instances.
|
||||||
|
- Default instance roots are:
|
||||||
|
- `/home/pleb/Windows/Users/pleb/BSManager/BSInstances`
|
||||||
|
- `/home/pleb/.local/share/BSManager/BSInstances`
|
||||||
|
- A local BSManager source checkout may be available at
|
||||||
|
`/home/pleb/src/Zagrios/bs-manager`. Use it as a read-only reference when
|
||||||
|
investigating launch behavior, inherited Steam arguments, instance layout, or
|
||||||
|
Proton environment details unless the user explicitly asks for BSManager code
|
||||||
|
changes.
|
||||||
|
- Prefer repo-local state with `--state-dir .state` for planned installs unless
|
||||||
|
the task explicitly targets the user's live default state.
|
||||||
|
|
||||||
|
## Workflow Rules
|
||||||
|
|
||||||
|
- Run commands from the repo root with `PYTHONPATH=src`.
|
||||||
|
- For human-style inspection, prefer the menu with repo-local state:
|
||||||
|
`PYTHONPATH=src python -m plugin_helper --state-dir .state menu`.
|
||||||
|
- When targeting the local Linux BSManager install rather than the Windows
|
||||||
|
mirror, pass `--instances-root /home/pleb/.local/share/BSManager/BSInstances`
|
||||||
|
or choose that path explicitly in the menu.
|
||||||
|
- Use the helper commands instead of manually copying plugin files into an
|
||||||
|
instance.
|
||||||
|
- Treat BSIPA as a bootstrap phase:
|
||||||
|
- `bootstrap` installs the locked BSIPA archive and records generated files.
|
||||||
|
- ordinary plugin plans should depend on healthy bootstrap state.
|
||||||
|
- Be careful with duplicate instance names across Windows and local roots. Use
|
||||||
|
the menu or pass `--instances-root` explicitly when targeting one install.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- Run `PYTHONPATH=src python -m unittest discover -s tests` after code changes.
|
||||||
|
- Run `PYTHONPATH=src python -m compileall -q src tests` for syntax/import
|
||||||
|
checks.
|
||||||
|
- For live game validation, follow `docs/SMOKETEST.md` and tear down Beat Saber
|
||||||
|
processes afterward.
|
||||||
|
|
||||||
|
## Launch Notes
|
||||||
|
|
||||||
|
- BSManager may inherit Beat Saber launch arguments configured in Steam.
|
||||||
|
- Do not assume a black screen is a plugin failure until checking
|
||||||
|
`Logs/_latest.log`, Unity `Player.log`, and the live process command line.
|
||||||
|
- Duplicate launch args such as `--no-yeet fpfc --no-yeet fpfc` can trigger a
|
||||||
|
fatal command-line parse error after BSIPA/plugin loading succeeds.
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
# Beat Saber Smoketest Workflow
|
||||||
|
|
||||||
|
Use this workflow after installing or removing a plugin batch. It is adapted
|
||||||
|
from the Setlist repo's working Proton/BSManager smoketest notes.
|
||||||
|
|
||||||
|
The routine smoketest should be short: about 10 seconds wall time for launch and
|
||||||
|
log check, followed by immediate teardown. If expected plugin log lines do not
|
||||||
|
appear inside that window, treat that as a failure to investigate instead of
|
||||||
|
stretching the timeout.
|
||||||
|
|
||||||
|
## Preconditions
|
||||||
|
|
||||||
|
- Run from the target BSManager instance directory, for example:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
cd "$HOME/.local/share/BSManager/BSInstances/1.44.1"
|
||||||
|
```
|
||||||
|
|
||||||
|
- The instance should contain BSIPA (`IPA/`, `IPA.exe`, `winhttp.dll`, and
|
||||||
|
`Plugins/`).
|
||||||
|
- If those files are absent, the run can still produce Unity `Player.log`
|
||||||
|
output, but it will not produce `Logs/_latest.log` or any IPA plugin-loading
|
||||||
|
evidence. Run `plugin-helper bootstrap --instance <version>` before using
|
||||||
|
this workflow to validate plugins.
|
||||||
|
- On Plasma + Wayland, the shell needs working desktop session variables:
|
||||||
|
`DISPLAY`, `WAYLAND_DISPLAY`, and `XDG_RUNTIME_DIR`. If the IDE terminal is
|
||||||
|
missing them, copy values from the logged-in desktop session.
|
||||||
|
|
||||||
|
## Launch
|
||||||
|
|
||||||
|
Run the Proton launch in the foreground with a short watchdog. Backgrounding
|
||||||
|
the Proton launch itself from an IDE terminal has been observed to exit early
|
||||||
|
before Beat Saber writes useful `_latest.log` lines.
|
||||||
|
|
||||||
|
Important: `timeout` may stop the launcher wrapper without killing the full
|
||||||
|
Beat Saber/Proton process tree. Prefer a watchdog that sleeps for the smoke
|
||||||
|
window and then kills the game process by name.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
export SteamAppId=620980 SteamOverlayGameId=620980 SteamGameId=620980
|
||||||
|
export WINEDLLOVERRIDES='winhttp=n,b'
|
||||||
|
export STEAM_COMPAT_DATA_PATH="$HOME/.local/share/BSManager/SharedContent/compatdata"
|
||||||
|
export STEAM_COMPAT_INSTALL_PATH="$PWD"
|
||||||
|
export STEAM_COMPAT_CLIENT_INSTALL_PATH="$HOME/.local/share/Steam"
|
||||||
|
export STEAM_COMPAT_APP_ID=620980
|
||||||
|
export SteamEnv=1
|
||||||
|
export OXR_PARALLEL_VIEWS=1
|
||||||
|
|
||||||
|
(
|
||||||
|
sleep 10
|
||||||
|
pkill -TERM -f "[B]eat Saber.exe" || pkill -TERM -f "[B]eat Saber" || true
|
||||||
|
sleep 2
|
||||||
|
pkill -KILL -f "[B]eat Saber.exe" || pkill -KILL -f "[B]eat Saber" || true
|
||||||
|
) &
|
||||||
|
smoke_watchdog_pid=$!
|
||||||
|
trap 'kill "$smoke_watchdog_pid" 2>/dev/null || true' EXIT
|
||||||
|
|
||||||
|
steam-run "$HOME/.local/share/Steam/steamapps/common/Proton - Experimental/proton" \
|
||||||
|
run "$PWD/Beat Saber.exe" --no-yeet fpfc 2>&1 | tee /tmp/bs-smoke.log
|
||||||
|
|
||||||
|
wait "$smoke_watchdog_pid" 2>/dev/null || true
|
||||||
|
trap - EXIT
|
||||||
|
```
|
||||||
|
|
||||||
|
Useful launch arguments:
|
||||||
|
|
||||||
|
| Argument | Use |
|
||||||
|
| --- | --- |
|
||||||
|
| `--verbose` | Opens the BSIPA console window. |
|
||||||
|
| `--debug` | Promotes debug logs to console output. |
|
||||||
|
| `--trace` | Enables very noisy BSIPA/internal traces. |
|
||||||
|
| `fpfc` | First-Person Flying Controller, useful for non-VR menu testing. |
|
||||||
|
| `--auto_play` | Built-in autoplayer for gameplay checks. |
|
||||||
|
|
||||||
|
## Log Checks
|
||||||
|
|
||||||
|
In another terminal, or immediately after the launch returns:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
tail -F Logs/_latest.log
|
||||||
|
```
|
||||||
|
|
||||||
|
For a batch install, check for:
|
||||||
|
|
||||||
|
- BSIPA startup reaches plugin loading.
|
||||||
|
- the expected plugin names and versions appear in `Logs/_latest.log`.
|
||||||
|
- there are no missing assembly or dependency-resolution failures.
|
||||||
|
- there are no repeated unhandled exceptions from newly installed plugins.
|
||||||
|
- the game reaches the main menu.
|
||||||
|
|
||||||
|
After the first successful launch, `plugin-helper bootstrap-check --instance
|
||||||
|
<version>` should pass. Ordinary plugin plans for lockfiles that include BSIPA
|
||||||
|
depend on that recorded bootstrap state and the latest IPA log.
|
||||||
|
|
||||||
|
For Setlist specifically:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
grep Setlist Logs/_latest.log
|
||||||
|
```
|
||||||
|
|
||||||
|
Expected Setlist signals include `platformUserId=...`, playlist lines with
|
||||||
|
`hasSyncUrl=...`, and `beatLeaderOwnerConfirmed=...`. When testing real playlist
|
||||||
|
sync, adding a map to an owned BeatLeader-synced playlist should log an HTTP
|
||||||
|
success line.
|
||||||
|
|
||||||
|
## Teardown
|
||||||
|
|
||||||
|
When the expected log lines are present before the watchdog fires, stop Beat
|
||||||
|
Saber immediately from a second terminal instead of waiting for the smoke window
|
||||||
|
to expire.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
pkill -TERM -f "[B]eat Saber.exe" || pkill -TERM -f "[B]eat Saber" || true
|
||||||
|
sleep 2
|
||||||
|
pkill -KILL -f "[B]eat Saber.exe" || pkill -KILL -f "[B]eat Saber" || true
|
||||||
|
```
|
||||||
|
|
||||||
|
The bracketed pattern avoids matching the shell command that is running the
|
||||||
|
cleanup. If Beat Saber still remains open, close the game window manually and
|
||||||
|
then check for leftovers:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
ps -eo pid,ppid,stat,comm,args | rg -i '[B]eat Saber|[P]roton.*Beat Saber|[w]ineserver|[s]team-run'
|
||||||
|
```
|
||||||
|
|
||||||
|
If the smoketest fails, kill leftover Beat Saber, Wine, or Proton processes
|
||||||
|
before retrying, then inspect `/tmp/bs-smoke.log`, Unity `Player.log` in the
|
||||||
|
Proton compatdata, and the timestamped files under `Logs/`.
|
||||||
|
|
||||||
|
## Plugin Development Notes From Setlist
|
||||||
|
|
||||||
|
These are relevant when `plugin-helper` installs locally built personal plugins.
|
||||||
|
|
||||||
|
- PC BSIPA plugins are .NET Framework class libraries loaded by BSIPA. Linux
|
||||||
|
`dotnet build` output is CIL and can be loaded by the Proton game instance.
|
||||||
|
- BSMT-style builds may copy the DLL directly into `<BeatSaberDir>/Plugins/`;
|
||||||
|
use `-p:DisableCopyToPlugins=True` when a build should not mutate the game
|
||||||
|
tree.
|
||||||
|
- `BeatSaberDir` in project user files or hint paths should point at the exact
|
||||||
|
BSManager instance being targeted.
|
||||||
|
- If a plugin build produces a shipped artifact, bump the plugin version before
|
||||||
|
the successful build so the IPA log line identifies the artifact under test.
|
||||||
|
- Setlist depends on BeatLeader being installed and signed in, PlaylistManager,
|
||||||
|
and BeatSaberPlaylistsLib. Its normal install target is `Plugins/Setlist.dll`.
|
||||||
Reference in New Issue
Block a user