Skip to content

Repository files navigation

AppSupervisor

Keep a complete Windows setup in sync with the application or nearby Bluetooth devices that need it.

Latest release Total downloads Build Commits since release

AppSupervisor is a lightweight Windows tray application that watches for a configured process or any selected registered Bluetooth device and then starts, supervises, restarts, and closes the applications, services, devices, and integrations that belong with it.

For example, starting OBS can activate streaming helpers, audio settings, lighting, and Twitch chat modes in one ordered profile. Closing OBS reverses the resources that should be restored and leaves one-shot actions alone.

Caution

This project was fully created by AI. Selected behavior has been tested by the project owner, but the code has not received a comprehensive independent human audit. Review it carefully before use—especially because AppSupervisor runs with administrator privileges and can start or stop applications and Windows services.

Quick start

  1. Download AppSupervisor-win-x64.zip from the latest release and extract it to a permanent writable folder.
  2. Install the .NET 10 Desktop Runtime if it is not already installed.
  3. Run AppSupervisor.exe, approve the UAC prompt, and find AppSupervisor in the notification area.
  4. Double-click the tray icon—or right-click it and choose Configure...—to open the editor.
  5. Create a profile, choose the process or globally registered Bluetooth devices that activate it, add helpers in startup order, then choose Validate and Save & Apply.

AppSupervisor runs in the notification area rather than opening a permanent main window. Its configuration is stored beside the executable in config.json.

The release also includes the optional com.tomaae.appsupervisor.streamDeckPlugin installer for the Stream Deck companion plugin. It is not required to run AppSupervisor or monitor Bluetooth devices.

How profiles work

Event What AppSupervisor does
The selected process starts or any selected Bluetooth device becomes present Activates the profile and processes enabled resources from top to bottom.
A helper or service becomes unavailable unexpectedly Reports the problem and optionally restarts it after the configured timeout.
A health check confirms a failure Notifies the configured destinations and can gracefully restart the affected helper.
The selected process remains closed or all selected Bluetooth devices exceed their presence timeout Waits for the profile's close timeout, then closes, stops, or restores reversible resources.
Supervision is paused or AppSupervisor exits Leaves external applications, services, devices, and integration state untouched.

Configuration tour

The screenshots below use a sanitized copy of a real OBS profile. They contain example credentials and device data only.

1. Choose what activates a profile

A profile watches either one executable name or an ANY-mode list from the global Bluetooth registry. The optional close and restart timeout overrides apply to everything owned by that profile.

OBS profile settings showing the monitored process and timeout controls

2. Build an ordered helper list

Add applications, Windows services, delays, audio interfaces, Home Assistant actions, MQTT publishes, OBS actions, Stream Deck actions, or Twitch actions. Drag or move helpers into startup order, and make a helper depend on an earlier application or service when readiness matters.

The left side keeps the entire sequence visible; the right side shows the settings and test actions for the selected helper.

Ordered OBS helper list with a Home Assistant lighting action selected

3. Automate startup and verify helper health

Each helper can run an ordered Startup macro after launch and can own independent health checks. Individual actions, full macros, checks, notifications, and the complete helper lifecycle can be tested before the profile is activated.

A helper with an ordered Startup macro and an OBS WebSocket health check

A health check controls its own timing, failure threshold, recovery behavior, process gate, and notification destinations:

Listener health-check settings for an OBS WebSocket endpoint

4. Configure shared integrations once

Bluetooth device registration, Home Assistant, MQTT, OBS WebSocket, Twitch, SteamVR monitoring, diagnostic logging, and the read-only local API are global settings shared by profiles. Credentials remain masked in the editor, but Home Assistant, MQTT, and OBS credentials are stored in the local configuration file.

Global Supervisor API, Home Assistant, and OBS WebSocket settings

5. Read supervision state from the tray

The tray icon stays compact while showing the important state. The green play badge means supervision is active, and that same play badge turns yellow while a retryable Startup macro warning is outstanding. The blue clock means helpers are still starting. The orange stop badge appears both during the profile's close timeout and while helpers are closing; either badge can be combined with the green supervising, yellow warning, or red error badge when those conditions overlap. Stream Deck status keys use the same colors and combined states.

Inactive, supervising, paused, error, starting, and stopping tray icon examples

  • Inactive: no profile currently needs resources.
  • Supervising: at least one profile is active.
  • Paused: supervision is paused; external resources are left untouched.
  • Error: one or more active supervision errors need attention.
  • Starting: one or more profile resources are still starting.
  • Stopping: the close timeout is running or one or more profile resources are still closing.

Contents

Functionality summary

  • Activates profiles when their monitored process starts or any selected registered Bluetooth device is present, then processes applications, services, delays, Windows audio interfaces, Home Assistant, MQTT, OBS, Stream Deck, and Twitch actions in the configured order.
  • Restarts applications or services that stop unexpectedly, with configurable close and restart timeouts and a universal five-attempt automatic-recovery limit.
  • Gracefully closes applications by default, with optional force-kill only when explicitly enabled.
  • Supports regular executables, Steam applications, Microsoft Store/MSIX applications, and Windows services.
  • Launches every helper with the helper executable's directory as its working directory so relative files resolve consistently.
  • Recognizes Launch4j helpers with a bundled Java runtime, launching their wrapper and supervising the persistent javaw.exe process.
  • Runs ordered per-application Startup macros on profile activation after confirming the helper is available, including delays, hotkeys, and window placement actions.
  • Can test a selected helper through its normal launch, Startup macro, and close lifecycle without activating its profile.
  • Provides per-application listener and VRChat OSCQuery health checks, including optional recovery after a confirmed failure.
  • Discovers and globally registers Bluetooth Classic and Low Energy devices, then exposes debounced device presence as a reusable profile trigger.
  • Monitors expected SteamVR controllers, trackers, and base stations without starting or controlling SteamVR.
  • Runs Twitch broadcaster chat messages, ads, and reversible chat-mode changes when a profile activates.
  • Sends per-resource alerts through popup dialogs, Windows notifications, or XSOverlay.
  • Suppresses repeated copies of the same active supervision error and exposes concrete active-error details in the tray tooltip.
  • Includes a graphical configuration editor with searchable, loading-aware pickers and recognizable resource-list icons.
  • Validates configuration before applying it and keeps the last valid configuration active if a reload fails.
  • Writes configurable per-session diagnostic logs beside the executable and configuration.
  • Can register itself to start elevated when the user signs in to Windows.
  • Can expose cached supervision status through an optional passwordless, read-only local HTTP API.

Detailed functionality

Profiles and resources

Each profile watches one process or an ANY-mode list of globally registered Bluetooth devices. A profile can optionally depend on another profile; while that prerequisite is not active with all reached startup resources ready, AppSupervisor does not poll the dependent profile's process or Bluetooth trigger at all. When its effective trigger becomes present, AppSupervisor activates the profile's enabled resources from top to bottom. When the effective trigger remains absent for the configured Close timeout, AppSupervisor closes or stops those resources.

Resources can include:

  • Helper applications.
  • Windows services.
  • Nonblocking delays between startup entries.
  • Home Assistant actions.
  • MQTT publishes.
  • OBS actions.
  • Stream Deck actions.
  • Twitch broadcaster actions.
  • Windows audio interface actions.

A resource may depend on one earlier application or service being ready. Profiles operate independently, so a delay or slow resource in one profile does not hold up another. Pausing or exiting AppSupervisor leaves external applications and services untouched.

The selected process or Bluetooth device list only controls whether its profile is active; appearing or disappearing does not itself produce a notification. Notification destinations configured on a helper apply only to that helper, and destinations configured on a health check apply only to that check. AppSupervisor-level configuration and startup messages use their own popup channel instead of borrowing destinations from profile resources.

Helper applications

Applications can be launched directly from an executable, through Steam, or through a Microsoft Store/MSIX app entry. Direct launches may include command-line arguments.

AppSupervisor identifies helpers by executable path. For a conservatively detected Launch4j wrapper with an adjacent jre\bin\javaw.exe, it launches the configured wrapper and arguments but identifies and controls the helper through that persistent bundled runtime. If multiple independent instances are found, it closes them before starting one fresh instance. Closing is graceful unless Allow force-kill after all graceful close attempts fail is enabled.

Per-application options include:

  • Restarting after an unexpected exit.
  • Keeping the application closed until an active profile needs it.
  • Minimizing its windows after launch.
  • Detecting unresponsive windows and restarting after repeated failures.
  • Choosing notification destinations.

The editor's Test helper button exercises the selected helper through the same production lifecycle without activating its profile. It uses the configured direct, Steam, or Store launch mechanism and direct-launch arguments, waits for startup confirmation, runs the complete Startup macro, and becomes Stop test only after startup work finishes. Stopping uses the normal graceful close, retry, tray-exit, and optional force-kill behavior without waiting for the profile close timeout. The test is unavailable while the selected profile is active or still completing shutdown. Closing the editor or AppSupervisor while a test is active first attempts to close the test helper.

Startup macros

Each helper application can have an ordered Startup macros sequence. AppSupervisor runs the sequence whenever a profile activates the helper: immediately when one existing helper instance is confirmed, or after a requested launch or relaunch is confirmed.

Available actions are:

  • A nonblocking delay.
  • A captured multi-key hotkey.
  • Moving a window to coordinates relative to a selected monitor's working area.
  • Resizing a window.
  • Minimizing, maximizing, or restoring a window.
  • Bringing a window to the front without explicitly activating it.

The editor displays detected monitor hardware names and stable Windows display identifiers. Move and resize actions can read the running helper window's current position or size, and every non-delay action can be tested individually. The complete sequence can also be tested before saving.

Hotkeys use Windows SendInput, so they are injected system-wide rather than sent to one window. AppSupervisor does not explicitly activate the helper, but the active application may also observe the shortcut and the helper's resulting command may change focus.

Minimize uses the same implementation as Minimize windows after starting: it minimizes all visible top-level windows belonging to the selected helper process, accepts multiple windows, and rechecks every 250 ms until minimization has succeeded across one second of checks, with a ten-second timeout. The macro waits for this work before continuing. Test action and Test macro use the same minimization and retry behavior. Like regular minimization, this uses Windows' normal minimize behavior rather than a focus-preserving queued command.

Other window actions require exactly one eligible visible top-level helper window. Move and Resize retry unavailable, rejected, and unstable geometry for up to ten seconds. They succeed only after the requested bounds remain unchanged across repeated lifecycle passes for at least two seconds; if the application rearranges its loading window, AppSupervisor reapplies the requested geometry and restarts that stability check. Other window actions continue to wait nonblockingly for a transiently unavailable process or window while the helper remains supervised.

Any Startup macro action that ultimately fails produces a warning through the helper's configured notification destinations. It does not set the red tray error state or restart the helper, and safe later actions continue. While warnings remain, the tray menu shows Retry warning... or Retry warnings.... Its window keeps the original failure order and can retry or dismiss selected warnings or all warnings. Retry all runs actions in that same order. A successful retry removes its warning; a failed retry leaves it available, and dismissal clears it until that macro action fails again.

The existing Minimize windows after starting option remains the simpler persistent minimization behavior. When a Startup macro contains a Minimize action, that option is disabled and ignored to prevent the two mechanisms from competing.

Windows services

The editor lists installed third-party services by service name and warns when an Automatic service is selected because applying an enabled entry changes it to Manual startup. AppSupervisor starts configured services with their profile, optionally restarts them after an unexpected stop, and requests a normal stop when the profile closes.

Health checks

Health checks belong to a helper application and have their own timing, failure threshold, recovery action, and notification settings.

  • Listener verifies that the helper owns a configured TCP or UDP port. It can run only while another selected process is active.
  • VRChat OSCQuery checks VRChat OSCQuery availability and selected avatar parameters. It can also report when most available parameters remain unchanged for too long.

Automatic VRChat OSCQuery checks begin only after VRChat.exe has run continuously for three minutes, preventing startup discovery from being treated as a health failure. While VRChat is running, Pick... can immediately discover the current avatar's available parameter leaf names without waiting for that automatic-check gate. Applying picker choices preserves configured names that the current avatar does not expose.

A confirmed failure can optionally trigger a graceful restart of the helper. One-shot tests do not change live failure state or restart external processes.

Automatic recovery retry budget

Every continuous automatic restart or recovery sequence is limited to 5 attempts. A failed attempt must wait 5 seconds before the next attempt can begin, and the fifth failure stops that automatic sequence. The error notification and tray error identify the current attempt and explicitly report when the 5 of 5 limit is exhausted. Delayed retries are scheduled at their due time instead of keeping the fast lifecycle loop active.

The policy covers helper launches and unexpected-exit restarts, Windows service start/continue recovery, repeated health-check restarts, Windows audio apply/restore work, Home Assistant activation/persistence/reversal, MQTT activation/inverse publishes, OBS actions, Stream Deck activation/restoration, and Twitch activation/restoration. A profile's configured Restart timeout remains the grace period before an unexpectedly missing application or service is first restarted; retries after an actual failed attempt still have the universal 5-second minimum separation.

The count resets only after the relevant outcome is confirmed: an application process is discoverable, a service reaches Running, a health check produces a successful probe after restart, or an integration/audio action (including configured verification) completes successfully. Cancelling the old demand and beginning a new profile lifecycle also starts a fresh sequence. Health probes continue observing an unhealthy helper after restart attempts are exhausted, so a later successful probe can clear the error and reset that check's budget without reloading AppSupervisor.

Home Assistant

Profiles can run turn_on, turn_off, and button.press actions against compatible entities. light.turn_on actions also set a brightness from 1% through 100%; verification and persistence check that percentage as well as the on state. Stateful actions can be verified after execution and kept persistent while the profile is active. When the profile closes, turn_on and turn_off actions are reversed; buttons run only during activation.

Home Assistant uses a shared URL and long-lived access token. The token is stored in the local configuration files, so those files must be treated as credentials.

MQTT

Profiles can publish a UTF-8 payload to an exact MQTT topic as an ordinary ordered resource. Each publish chooses QoS 0, 1, or 2 and whether the broker should retain it. Optional activation verification subscribes before publishing and waits for an exact UTF-8 payload on a configured state topic, so a fast device response is not missed. A state timeout is reported through the resource's notification destinations and follows the normal bounded retry policy.

The On deactivation choice defines the resource's state semantics:

  • One-shot publishes only during activation. Use this for events or commands that have no meaningful inverse.
  • Publish configured inverse payload publishes an explicit reverse topic, payload, QoS, and retain setting after the profile closes. It can optionally verify an exact inverse state on the state topic.
  • Restore captured retained state subscribes before activation and refuses to publish unless the state topic first supplies a retained message. AppSupervisor saves the exact received bytes, then publishes them with retain enabled and verifies the exact bytes after deactivation. It never guesses a previous state or falls back to a configured approximation.

The editor's Test action runs a one-shot once. Reversible actions remain applied for five seconds and then use the same explicit inverse or exact retained-state restoration. If activation was accepted but later verification fails, the preview still attempts its deterministic inverse and clearly reports any cleanup failure.

MQTT uses one global broker host and TCP port, MQTT 3.1.1, optional username/password authentication, and optional TLS. TLS uses normal Windows certificate validation; there is no insecure certificate-bypass option. The editor masks the password and runtime error messages redact it, but the password is stored in the local configuration files. Treat those files as credentials.

OBS WebSocket

Profiles can switch the OBS program scene, mute or unmute an OBS input, and show or hide a source in a scene. OBS actions are ordinary ordered resources, so a profile may monitor obs64.exe and run them after OBS starts. Each action runs once during profile activation; profile deactivation never restores or toggles the resulting OBS state.

OBS uses the standard WebSocket 5.x protocol over ws with a shared host, port, and optional password. The password is stored in the local configuration files, so those files must be treated as credentials.

Stream Deck actions

Profiles can run actions placed on Stream Deck's dedicated MCP Actions profile. Choose Add Stream Deck action in the Resources menu, select a discovered action, and optionally test it before saving. Buttons and switches run once during profile activation; an opted-in switch is invoked once more during profile deactivation to toggle it back. This is a native Stream Deck action invocation through Elgato's MCP server; AppSupervisor does not simulate a physical or virtual key press.

The picker labels one-state actions as buttons and two-state actions as switches using Stream Deck's reported action metadata. It shows the same full category, developer, and action identity used by Stream Deck, and prefixes a configured key title when one is available. Buttons run once. Switches can optionally enable Toggle switch back when profile deactivates, which invokes the same switch again during profile deactivation. With that option enabled, Test action holds the toggled state for five seconds and then restores it asynchronously.

In Stream Deck 7.4 or later, enable MCP Deck under Stream Deck Preferences and place the desired actions on the generated MCP Actions profile. Then install Elgato's local bridge once:

npm install -g @elgato/mcp-server

AppSupervisor starts one shared local stdio bridge only when discovery or execution is requested, serializes calls through it, and performs no polling. The separate AppSupervisor Stream Deck companion plugin described below provides tray-style status and monitored-app launch keys without using MCP.

Windows audio interfaces

Profiles can set the master volume and mute state of an active Windows playback or recording interface. The interface picker includes Default output and Default input choices that follow the current Windows multimedia defaults. Before applying the requested state, AppSupervisor captures the current volume and mute values. By default it restores both values when the profile deactivates; clear Restore original volume and mute when profile deactivates when the requested state should remain in place. Test for 5 seconds temporarily applies the configured state and always attempts to restore the original values.

Windows can replace an audio endpoint ID after a driver update, reconnect, or device re-enumeration. AppSupervisor therefore stores the endpoint's device-instance ID, physical container ID, direction, and friendly names as recovery signals. It prefers exact and stable identity matches, and accepts a name-only fallback only when exactly one active interface matches; ambiguous matches require selecting the interface again in the editor.

Bluetooth device presence

Use Integrations → Global — Bluetooth device presence to discover nearby or paired Bluetooth Classic and Low Energy devices and register them once for the whole application. A profile can then select Bluetooth device presence as its activation trigger and check one or more registered devices. Selection uses ANY mode: one present device keeps the profile active, and several profiles may reuse the same registrations.

Discovery shows a Looking for Bluetooth devices... activity overlay while it uses Windows association-endpoint names and actively requests standard and extended Bluetooth Low Energy advertisements and scan responses. It reads both Windows' decoded local name and the raw complete/shortened-name data sections. Every result remains available for registration. A device whose adapter/driver path exposes no usable name receives an editable Unidentified Classic/LE device label plus its address suffix instead of presenting the address as a name. Pairing an intended device in Windows can make additional naming metadata available. A name edited in AppSupervisor is preserved by later scans.

The grid combines signal strength reported by Windows endpoint discovery with raw BLE advertisement RSSI and shows a broad Very near, Near, Far, or Very far estimate based on the strongest valid signal observed during that scan. The estimate is intentionally approximate because walls, interference, transmit power, and antenna orientation affect RSSI. The Manufacturer column prefers Windows manufacturer metadata, then resolves manufacturer-specific advertisement company IDs through a bundled offline snapshot of the Bluetooth assigned-numbers registry. An advertising-data owner may differ from the consumer-facing device brand; devices for which Windows and the advertisement both omit manufacturer data remain unknown rather than receiving a guess from a potentially randomized BLE address.

Registrations store AppSupervisor's own stable device ID plus the remote device's 48-bit Bluetooth address and transport. They do not depend on the USB adapter's Windows identity, so replacing a Bluetooth dongle does not invalidate profile references. Discovery and monitoring never pair, connect to, or control a remote device.

Connected devices and devices that Windows reports as present count as nearby. Bluetooth Classic devices normally need to be connected or discoverable; Low Energy devices can be observed through advertisements. The global scan interval controls how often Windows discovery runs, and the presence timeout is applied independently to each registration. A multi-device trigger becomes absent only after every selected device has exceeded that timeout; the profile's close timeout begins afterward. The presence timeout must be at least the scan interval.

Continuous Bluetooth scanning runs only when at least one enabled Bluetooth-triggered profile exists. It tracks only registrations referenced by enabled profiles; keeping discovered devices in the global registry or configuring Bluetooth only in disabled profiles does not start the presence scanner.

Defaults are a 15-second scan interval and 45-second presence timeout. A scan observes Windows endpoints and LE advertisements concurrently for approximately 10 seconds, then waits for the remainder of the interval. Scanning runs asynchronously in the background; the one-second profile evaluation reads cached presence instead of waiting for discovery. An enabled but currently inactive Bluetooth profile still needs scanning to detect activation. The editor's explicit discovery button also works without an enabled Bluetooth profile.

Signal shows — and Proximity estimate shows Unknown when neither Windows endpoint metadata nor advertisements supply a usable RSSI value. These are not precise distance measurements. A device that rotates its private Bluetooth address may no longer match its saved registration; persistent identity across address rotation is not guaranteed.

Older single-device monitorBluetoothDeviceId settings load automatically into the new monitorBluetoothDeviceIds list. Saving writes the list form; existing one-device profiles retain their behavior.

Bluetooth presence requires a working Windows Bluetooth adapter and driver. If Windows aborts every Classic and Low Energy watcher, AppSupervisor reports the discovery failure and leaves existing cached presence to expire normally.

SteamVR device monitoring

AppSupervisor can monitor configured controllers, trackers, and Lighthouse/base-station devices while SteamVR is already running. Discovery records controller handedness and SteamVR tracker assignments such as left foot, left knee, or waist, so offline and recovery notifications identify the missing role. It does not start or restart SteamVR and does not control devices.

Monitoring retains the saved or last successfully observed assignment when a scan cannot provide a usable role, including while the device is connected. Unrelated settings reloads also preserve the observed label. To explicitly change or clear a saved assignment, rediscover it and use Save & Apply; a changed saved role replaces the remembered label.

After repeated connection failures, it sends the selected notifications and shows an offline-device window. Alerts for a device can be silenced for the rest of the current SteamVR session, including any later disconnections after a recovery; recovery is still detected automatically. The window closes once every device shown in it has been silenced.

Generic/FBT trackers are monitored only after SteamVR reports them connected at least once in the current SteamVR session. A tracker intentionally left powered off for the whole session therefore does not produce an offline alert. Hand controllers and tracking references such as base stations remain mandatory from session startup.

Show low batteries after a profile stops is a separate opt-in SteamVR setting, off by default. Its Low battery at or below (%) threshold defaults to 25% and accepts 1–100%. AppSupervisor samples connected controllers and trackers during active profile runs, initially and then at most once every five minutes. Battery reads share the existing device capture when offline monitoring is enabled; with only battery warnings enabled, captures run every five minutes. No battery capture runs when no profile is active.

Once a profile's close timeout and resource shutdown finish, a regular, nonblocking window lists its last known low batteries with device name, assignment, serial number, percentage, charging status, and reading time. Readings remain available if the devices disconnect or SteamVR closes before the profile finishes stopping. Each profile run receives at most one report and only if SteamVR supplied low-battery data during that run. Unsupported readings are ignored; a newer valid reading replaces an older one. The battery warning does not use notification destinations. Pause, sleep, configuration reload, and application exit discard battery-session state without producing shutdown warnings.

Twitch

Profiles can send a chat message, run a 30–180 second advertisement, or temporarily change emote-only, followers-only, slow, and subscribers-only chat modes. Messages and ads run once when the profile activates. Chat modes capture their previous Twitch values and restore those exact values after the profile deactivates.

Twitch uses one global broadcaster connection and allows Twitch resources in only one enabled profile at a time. Authorization uses Twitch's public-client device flow: the broadcaster approves access in a browser once, AppSupervisor validates the session hourly and automatically rotates expiring tokens even when no Twitch action runs, and the replacement credentials are written atomically to a per-user file protected by Windows DPAPI. Existing Windows Credential Manager authorization is migrated automatically. AppSupervisor includes its public Twitch application identity; users do not configure a Client ID or client secret. Twitch can still require authorization again if the broadcaster revokes access, changes the account password, or AppSupervisor does not run for more than Twitch's 30-day public-client refresh-token inactivity limit. When renewed consent is required, startup maintenance shows a reconnect window before a Twitch profile action is attempted; Reconnect Twitch immediately opens browser authorization and completes the connection in that window without routing through the configuration editor. A failure to persist a rotated credential is reported immediately and includes the underlying Windows storage error in the diagnostic log.

Notifications

Helper applications, Windows services, health checks, Windows audio interfaces, Home Assistant actions, MQTT actions, OBS actions, Stream Deck actions, Twitch actions, and SteamVR devices can report through:

  • Popup dialogs.
  • Windows notifications.
  • XSOverlay notifications.

If XSOverlay is unavailable, its notifications fall back to Windows notifications.

An identical ordinary supervision error from the same resource is published only once while that error remains active. A distinct error can still notify once, and recovery clears the suppression so a later incident can notify again. The red-X tray tooltip identifies a concrete active error, includes a count when more errors are active, and retains any simultaneous helper startup or shutdown activity that fits within the Windows tooltip limit.

Startup macro failures are warnings rather than ordinary supervision errors. They do not turn the tray icon red and can be retried or dismissed from the warning command that appears in the tray menu while any remain active.

Configuration editor

Open the editor from Configure... in the tray menu or by double-clicking the tray icon. It supports adding, duplicating, removing, enabling, reordering, and configuring profiles and resources.

Use Export profile... to save the selected profile as a versioned *.appsupervisor-profile.json document, or Import profile... to add one of those documents as a new profile. The transfer preserves the profile trigger, timeouts, ordered resources, resource dependencies, helper health checks, startup macros, notification choices, and every resource-specific setting. Profile and resource internal IDs are regenerated during import and resource dependency links are remapped; a duplicate visible name receives an (Imported) suffix. A dependency on another profile cannot be embedded in a standalone export, so the imported profile leaves it unselected and shows a warning to reselect it before enabling the profile.

Portable profile files contain only the selected profile. Application-wide Bluetooth registration, Home Assistant, MQTT, OBS, Twitch, SteamVR, Stream Deck, API, and logging configuration is never included, so integration credentials and connection settings remain local. Values that belong to the profile but may be computer-specific—such as the activation process name or Bluetooth registry references, executable paths, Windows service names, audio endpoint identities, Stream Deck action IDs, Home Assistant entities, MQTT topics and payloads, OBS object names, health-check endpoints, and monitor names/coordinates—are preserved rather than silently discarded. The export records applicable warnings, both export and import show them, and every imported profile starts disabled. Register and reselect every required Bluetooth device on the importing computer when applicable, review the other transferred values, then enable the profile and choose Save & Apply; normal validation still prevents an unusable enabled profile from replacing the active configuration.

The editor provides pickers for running processes, executables, Steam applications, Microsoft Store applications, Windows services, Windows playback and recording interfaces, Bluetooth devices, SteamVR devices, and live VRChat OSCQuery parameter names. Running, Steam, and Store application pickers show a loading overlay that prevents selecting incomplete results and provide text filtering after results are ready. The running-process picker excludes Windows service processes and hides Microsoft/Windows applications by default; the Store picker similarly hides Microsoft/system applications unless requested. The running and Store picker status text reports visible and filtered counts.

Steam and Microsoft Store catalog discovery retries transient failures up to four total attempts before reporting a distinct Application discovery error. If discovery fails while applying a reload, the previous valid configuration and its supervision remain active. The unified resource list uses each helper executable's icon when available and dedicated type pictograms for services, delays, Windows audio, Home Assistant, MQTT, OBS, Stream Deck, and Twitch resources.

Connections such as Home Assistant, MQTT, OBS WebSocket, and Twitch, plus Bluetooth registration and SteamVR monitoring, are configured globally rather than inside a profile.

Grid row heights are fixed throughout the application to prevent accidental resizing; column widths remain adjustable.

The Diagnostic logs tab immediately after Integrations provides a parsed viewer for every available current-format session log and the legacy AppSupervisor.log, ordered newest first. Its record table separates time, severity, and message, while the detail pane retains multiline exception and continuation text. Selecting the tab always rediscovers and reloads the logs; changing the session or using Refresh also reloads without blocking the editor.

Validate checks the complete configuration without saving. Save & Apply validates and writes it, then replaces the running configuration without showing a success notification. If the new configuration cannot be applied, the previous valid configuration remains active and AppSupervisor shows an informative failure notification.

Diagnostic logging

Each AppSupervisor run creates a uniquely named AppSupervisor_yyMMdd-HHmmss.log session log beside AppSupervisor.exe and config.json. Records use local ISO 8601 timestamps with the UTC offset and stable TRACE, INFO, WARN, and ERROR labels. Multiline content is indented, and ordinary prose is wrapped for readable viewing without splitting quoted paths or long unbroken values.

Choose the minimum Log level under Integrations → Global — Diagnostic logging. Info is the default, Trace includes detailed execution flow, and Warning or Error reduce routine output. On the first write in a session, AppSupervisor removes current-format and legacy AppSupervisor.log files older than five days. Logging is best-effort and never interrupts supervision or shutdown.

The viewer reads growing files with sharing that does not block logging or rotation. Missing, rotated, inaccessible, partially written, and malformed logs produce contained status or malformed rows rather than an editor failure. To keep browsing responsive even after unusually noisy sessions, it reads the newest 16 MB of a selected file and displays at most the newest 10,000 parsed records; the status line reports either limit when applied.

Supervisor API

Enable Enable read-only WS API under Integrations, then choose Save & Apply. AppSupervisor serves HTTP JSON at:

http://127.0.0.1:17834/

The listener accepts connections only from the same computer. It has no password, allows cross-origin reads, disables response caching, and accepts only GET; unsupported methods return HTTP 405. It never performs work on behalf of a request. Responses serialize the last immutable snapshot published by the existing one-second supervision timer, so requesting API data does not inspect processes, services, listeners, windows, health probes, or integrations.

The API handles at most 16 connections concurrently. Each accepted connection has a five-second deadline for receiving the request and sending its response; stalled connections are closed automatically.

Internal IDs

Routes use stable internal IDs rather than visible names:

  • Profiles use profileId.
  • Helpers use their existing resourceId.
  • API responses expose both values as internalId and include ready-to-use relative endpoint fields.

New IDs are compact hexadecimal strings, so spaces and visible names never appear in API URLs. Existing profiles that predate profileId receive one when the configuration is loaded and saved. Duplicating a profile generates a new ID.

An abbreviated configuration therefore looks like:

{
  "profiles": [
    {
      "profileId": "518b32a93ca941a6a65aaf3dde50668d",
      "name": "VR profile",
      "applications": [
        {
          "resourceId": "3fd8f94e25614cb181f09648aaad4e38",
          "path": "C:\\Tools\\helper.exe"
        }
      ]
    }
  ]
}

Endpoints

Method and path Response
GET / All profiles with name, internalId, enabled, cached status, and endpoint.
GET /<profileId> One profile and all its helpers, including active/enabled state, trigger type and process/device references, configured health-check and macro counts, and helper endpoints.
GET /<profileId>/<resourceId> Helper identity, cached activity, lifecycle settings, configuration counts, and links to its health-check and macro endpoints.
GET /<profileId>/<resourceId>/healthcheck Every configured health check, including application-responsiveness monitoring, timing, recovery settings, and cached status/detail.
GET /<profileId>/<resourceId>/macro Whether a Startup macro is configured, its cached execution status, and its ordered actions.

Unknown profiles, helpers, or child endpoints return HTTP 404. A helper executable filename may also be accepted in place of resourceId when it is unique within the profile, but clients should always use the returned internalId; ambiguous filenames return HTTP 409.

updatedUtc identifies when the one-second timer published the returned snapshot. A helper's active value means the profile startup sequence has activated that resource; it is not a fresh process query.

For Bluetooth profiles, monitorBluetoothDeviceIds contains the full ANY-mode selection. The legacy monitorBluetoothDeviceId response field exposes only the first selected ID (or an empty string) for older clients; new clients should use the list.

Status values are:

  • Profile: disabled, paused, active, or inactive.
  • Helper: disabled, active, or inactive.
  • Health check: disabled, inactive, checking, healthy, or unhealthy.
  • Startup macro: notConfigured, idle, running, or warning.

Example responses

GET /:

{
  "updatedUtc": "2026-08-17T12:00:00Z",
  "paused": false,
  "profiles": [
    {
      "name": "VR profile",
      "internalId": "518b32a93ca941a6a65aaf3dde50668d",
      "enabled": true,
      "status": "active",
      "endpoint": "/518b32a93ca941a6a65aaf3dde50668d"
    }
  ]
}

GET /518b32a93ca941a6a65aaf3dde50668d:

{
  "updatedUtc": "2026-08-17T12:00:00Z",
  "name": "VR profile",
  "internalId": "518b32a93ca941a6a65aaf3dde50668d",
  "enabled": true,
  "status": "active",
  "triggerType": "process",
  "monitorProcess": "VRChat.exe",
  "monitorBluetoothDeviceId": "",
  "monitorBluetoothDeviceIds": [],
  "helpers": [
    {
      "name": "helper.exe",
      "internalId": "3fd8f94e25614cb181f09648aaad4e38",
      "enabled": true,
      "active": true,
      "status": "active",
      "healthChecksConfigured": 1,
      "macroActionsConfigured": 2,
      "endpoint": "/518b32a93ca941a6a65aaf3dde50668d/3fd8f94e25614cb181f09648aaad4e38"
    }
  ]
}

The helper endpoint contains healthCheckEndpoint and macroEndpoint. Follow those links instead of constructing child paths manually.

Windows curl.exe smoke test

The following PowerShell script discovers internal IDs and requests every endpoint for the first configured helper:

$ErrorActionPreference = 'Stop'
$baseUrl = 'http://127.0.0.1:17834'

function Get-CurlJson([string]$url) {
    $body = & curl.exe --silent --show-error --fail-with-body $url
    if ($LASTEXITCODE -ne 0) {
        throw "curl failed for $url"
    }
    $body | ConvertFrom-Json
}

$root = Get-CurlJson "$baseUrl/"
$profile = $root.profiles | Select-Object -First 1
if ($null -eq $profile) { throw 'No profiles returned.' }

$profileState = Get-CurlJson "$baseUrl/$($profile.internalId)"
$helper = $profileState.helpers | Select-Object -First 1
if ($null -eq $helper) { throw 'The selected profile has no helpers.' }

$helperUrl = "$baseUrl/$($profile.internalId)/$($helper.internalId)"
Get-CurlJson $helperUrl | ConvertTo-Json -Depth 20
Get-CurlJson "$helperUrl/healthcheck" | ConvertTo-Json -Depth 20
Get-CurlJson "$helperUrl/macro" | ConvertTo-Json -Depth 20

Error behavior can be checked directly:

# 404: unknown profile
curl.exe -sS -o NUL -w '%{http_code}\n' http://127.0.0.1:17834/not-a-profile

# 405: the API is read-only
curl.exe -sS -o NUL -w '%{http_code}\n' -X POST http://127.0.0.1:17834/

Stream Deck companion plugin

The companion Stream Deck plugin provides two actions:

  • Status behaves like an accessible copy of the tray icon. Its image and short title follow the same waiting, supervising, starting, stopping, paused, and error states as the Windows notification-area icon. Pressing the key opens the configuration editor.
  • Launch monitored app launches the monitored executable for the enabled process-triggered profile selected in the action's property inspector. AppSupervisor skips the launch when that process is already running. Disabled and Bluetooth-triggered profiles are not offered.

Multiple companion keys share one connection. A launch key sends only its selected profile ID; AppSupervisor validates that ID against the active configuration and launches its configured monitored process. Browsing or picking a monitored process now retains its full executable path for reliable launching. Existing filename-only configurations remain compatible, although Windows must be able to resolve the filename for the launch to succeed.

The connection is automatic and does not require enabling the Supervisor API. The unelevated plugin hosts a current-user Windows named pipe and elevated AppSupervisor connects to it, pushing only deduplicated state and profile-catalog changes; neither side polls for status. When AppSupervisor is unavailable, its keys show Offline. The pipe connection is the sole online/offline signal, so elevation-launcher events cannot overwrite a live status. AppSupervisor waits for the pipe without consuming CPU and reconnects after either application restarts.

To install the release plugin (Windows, Stream Deck 6.6 or later):

  1. Download com.tomaae.appsupervisor.streamDeckPlugin from the latest release.
  2. Double-click the downloaded file and accept the Stream Deck installation prompt.
  3. In the Stream Deck application, expand AppSupervisor and drag Status or Launch monitored app onto a key.
  4. For a launch key, select an enabled process-triggered profile in its property inspector.
  5. Start the matching AppSupervisor build. Status keys adopt the tray status; launch keys show the selected profile name.

The companion plugin does not require MCP Deck or the Elgato MCP Server. Those are used only by the separate Stream Deck actions integration.

Building the installer from source requires Node.js 24 or later. Run .\StreamDeckPlugin\Build.ps1; it installs the locked dependencies, runs plugin tests, bundles and validates the plugin, and writes the installer under artifacts/StreamDeck/. Interactive runs wait for Enter before closing; automation can pass -NoPause.

Administrator and startup behavior

AppSupervisor requires administrator privileges to manage configured services consistently. It prevents multiple supervisor instances and can create a current-user scheduled task that launches it with elevated privileges at sign-in.

Configuration storage

Configuration is stored in config.json beside AppSupervisor.exe. If the file is missing, AppSupervisor creates an empty valid configuration. The last verified configuration is also saved as config.json.old during normal shutdown.

Both files are excluded from Git and omitted from release packages. Do not share or commit them if they contain a Home Assistant access token, MQTT password, or OBS WebSocket password. Twitch OAuth credentials are not written to these files; they are stored separately under the current user's Local App Data and encrypted with Windows DPAPI so only that Windows user can decrypt them.

Portable *.appsupervisor-profile.json exports are intended for sharing individual profiles and never contain the application-wide integrations object. They can still contain profile-owned values such as executable paths, arguments, Twitch chat messages, entity/object names, or device identifiers, so inspect a profile export before sharing it publicly.

Running a packaged build

The release package is Windows x64 and framework-dependent. Install the .NET 10 Desktop Runtime, keep all packaged files together, then run AppSupervisor.exe and approve the UAC prompt. AppSupervisor runs in the notification area rather than opening a main window.

The package contains:

AppSupervisor.exe
AppSupervisor.NotificationHost.exe
LICENSE
THIRD-PARTY-NOTICES.txt

To upgrade, exit AppSupervisor from its tray menu, back up config.json and config.json.old, then extract the new ZIP into the existing installation folder. The release contains no personal configuration or credentials. Keep your configuration files and all packaged files together; start AppSupervisor again after extraction. Install the companion Stream Deck file separately if you use its Status or monitored-app launch keys.

Building from source

Requirements

  • Windows on x64 hardware.
  • .NET 10 SDK (GitHub Actions currently uses 10.0.302).
  • PowerShell for the packaging script.
  • Administrator access for runtime integration testing involving services.

Restore, test, and build

From the repository root:

dotnet restore .\AppSupervisor.slnx --runtime win-x64
dotnet test .\AppSupervisor.slnx --configuration Release --no-restore
dotnet build .\AppSupervisor.slnx --configuration Release --no-restore

Create a release package

.\Publish.ps1

The script restores the solution, runs the Release tests, publishes the Windows x64 executables, audits the output, and creates:

artifacts/AppSupervisor/
artifacts/AppSupervisor-win-x64.zip

When run in an interactive console, the script waits for Enter before closing so its final output or error remains visible. Automated callers can use .\Publish.ps1 -NoPause; redirected CI runs never pause.

License

Copyright 2026 Tomaae.

Licensed under the Apache License 2.0.

About

AppSupervisor is a Windows tray app that automatically starts, monitors, recovers, and closes applications, services, devices, and integrations based on configurable triggers.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages