- Use pcall for version.lua in bootstrap.lua so missing version module does not crash plugin startup
- Remove plugin/subminer from app-data dirs in uninstall-linux and uninstall-macos targets
- Add Lua compat test asserting bootstrap uses defensive pcall for version load
- Add release-workflow test asserting uninstall targets clean bundled plugin dirs
- Delete completed planning document
Shift SubMiner’s default mpv integration from “install and maintain a plugin in the user’s mpv scripts directory” to “inject the bundled plugin at launch time when SubMiner controls mpv.” Existing user-installed plugins remain supported and take precedence. If SubMiner detects an installed plugin, it will not inject the bundled runtime plugin and will show/log a helpful message with the detected path.
This keeps existing users working, avoids double-loading, and removes the need to update user plugin files for new installs.
## Goals
- New users should not need to install the mpv plugin manually for SubMiner-managed mpv launches.
- Existing users with an installed mpv plugin should keep working without behavior changes.
- SubMiner should avoid loading both the installed plugin and bundled runtime plugin in the same mpv session.
- SubMiner should tell users exactly where an installed plugin was detected so they can remove it if they want app-managed runtime loading.
- Windows app-managed mpv launch should support this.
- macOS/Linux launcher-managed mpv launch should support this.
## Non-Goals
- Do not remove the current first-run plugin installer yet.
- Do not auto-delete, rename, or overwrite user-installed mpv plugin files.
- Do not force `--load-scripts=no` by default.
- Do not make the runtime plugin override an installed user plugin.
- Do not solve arbitrary already-running mpv sessions without IPC or prior SubMiner launch control.
## Runtime Policy
When SubMiner is about to launch mpv:
1. Detect whether a SubMiner plugin is already installed in mpv’s user scripts directory.
2. If an installed plugin is found:
- Do not pass `--script=<bundled plugin path>`.
- Continue passing SubMiner script options needed by the installed plugin.
- Show/log a warning with the detected installed plugin path and detected version if available.
- Defer to the installed plugin for that mpv session.
3. If no installed plugin is found:
- Pass `--script=<bundled runtime plugin path>`.
- Pass runtime script options for binary path, socket path, log level, and any existing launch metadata.
- Use the plugin bundled with the currently running SubMiner app.
## Plugin Detection
Add a shared detector module, likely under `src/shared/` or `src/core/services/`, usable by both Electron main runtime and launcher code.
- Return the first existing candidate using the same precedence mpv would most likely use.
- Prefer `subminer/main.lua` over legacy single-file loaders within the same config root.
- Include the absolute detected path in the warning message.
- If multiple plugin candidates are found, log all candidates at debug level, but use the first one as the active detection result.
## Plugin Versioning
Add version metadata to the bundled Lua plugin.
New file:
```text
plugin/subminer/version.lua
```
Contents shape:
```lua
return{
name="SubMiner mpv plugin",
version="0.12.0",
}
```
Update `plugin/subminer/init.lua` or `bootstrap.lua` to load this version and expose it in logs.
Version detection should try:
1. Read sibling `version.lua` next to detected `main.lua`.
2. Parse a simple `version = "..."` value.
3. If unavailable, return `version: null`.
For legacy single-file installs, return `version: null` unless a parseable marker is present.
Important: old installed plugins will not have version metadata, so `null` is expected and should be messaged as “unknown or legacy version,” not treated as an error.
## Bundled Runtime Plugin Path Resolution
Add a helper for resolving the packaged plugin entrypoint/directory.
For Electron main process:
- Reuse `resolvePackagedFirstRunPluginAssets()` or extract a narrower helper from `src/main/runtime/first-run-setup-plugin.ts`.
- Preferred runtime script path should be the plugin directory if mpv supports directory script loading:
```text
<resourcesPath>/plugin/subminer
```
- Fallback:
```text
<resourcesPath>/plugin/subminer/main.lua
```
For launcher:
- Accept a resolved runtime plugin path where possible.
- If only `appPath` is available, resolve relative packaged resources using existing app path conventions.
- If runtime plugin path cannot be resolved and no installed plugin exists, fail with a clear message telling the user the packaged mpv plugin assets were not found.
## Windows Implementation
Update `src/main/runtime/windows-mpv-launch.ts`.
Current behavior already supports `pluginEntrypointPath`.
Change launch preparation so the caller passes either:
- `pluginEntrypointPath` when no installed plugin is detected.
- `undefined` when an installed plugin is detected.
Add or update a dependency boundary so detection is testable:
- Run installed plugin detection, including portable config checks when `mpv.exe` path is known.
- If installed plugin exists:
- show a non-fatal notification or warning dialog/log entry:
```text
SubMiner detected an installed mpv plugin at:
<path>
This mpv session will use the installed plugin. Remove it to use SubMiner's bundled runtime plugin automatically.
```
- launch without `--script=<runtime plugin>`.
- If no installed plugin exists:
- launch with `--script=<bundled runtime plugin path>`.
Keep current script options:
```text
subminer-binary_path=<process.execPath>
subminer-socket_path=<socket>
```
## macOS/Linux Launcher Implementation
Update `launcher/mpv.ts`.
Affected functions:
- `startMpv()`
- `launchMpvIdleDetached()`
Current launcher passes `--script-opts=...` but does not explicitly pass `--script=...`.
New behavior:
- Detect installed plugin before building mpv args.
- If installed plugin exists:
- do not add `--script=<bundled runtime plugin path>`.
- keep passing SubMiner script opts.
- log warning with detected path/version.
- If no installed plugin exists:
- add `--script=<bundled runtime plugin path>`.
- keep passing SubMiner script opts.
Prefer repeated `--script-opt=key=value` in new code where practical, but do not require a full conversion if it risks broad parser churn. If keeping the existing `--script-opts=...` helper, preserve current escaping behavior.
## User Messaging
Add user-facing copy in two places:
1. Runtime warning/log when launching mpv and installed plugin is detected.
2. First-run/setup UI copy update, if applicable, to explain that plugin installation is now optional for normal SubMiner-managed launch.
Suggested warning:
```text
SubMiner detected an installed mpv plugin at:
<path>
This session will use the installed plugin. Remove that plugin to use the bundled runtime plugin that ships with this SubMiner version.
```
If version is known:
```text
Detected plugin version: <version>
Bundled plugin version: <version>
```
If version is unknown:
```text
Detected plugin version: unknown or legacy
```
## Compatibility Behavior
Existing installed plugin:
- Used as-is.
- Receives script options from the launch command.
- Not modified.
No installed plugin:
- Bundled runtime plugin is injected.
- No files are written to mpv config.
Old installed plugin with no version:
- Used as-is.
- Warning says version is unknown or legacy.
- User can remove it to switch to runtime injection.
Portable Windows mpv:
- If `mpv.exe` path is known and `portable_config` exists beside it, detect installed plugin there before checking `%APPDATA%`.
- If `mpv.exe` path is unknown, only `%APPDATA%` detection is possible.
## Public Interface / Type Changes
Add shared detection exports:
```ts
export interface InstalledMpvPluginDetection {
installed: boolean;
path: string | null;
version: string | null;
source: InstalledMpvPluginSource | null;
message: string | null;
}
export function detectInstalledMpvPlugin(options: {
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.