Skip to content

macrodeck-plugin run

Launches a plugin as a child process with the environment the supervisor would give it, and streams its output live.

Terminal window
macrodeck-plugin run --project src/HelloDeck --stub-host
Started a disposable stub host at http://127.0.0.1:49789.
Started process 18593 (mode: SelfRegistering, host: http://127.0.0.1:49789). Press Ctrl-C to stop.
[plugin] info: Microsoft.Hosting.Lifetime[14]
[plugin] Now listening on: "http://127.0.0.1:49790"
...
[plugin] info: MacroDeck.Plugin.Hosting.Transport.PluginConnectionHostedService[0]
[plugin] Registered with the host as '"com.example.hello-deck"'.
Session established (negotiated plugin protocol v3).
[plugin] info: HelloDeck.PluginIntegration[0]
[plugin] Initialized.
[log:information] HelloDeck.PluginIntegration: Initialized.
...
^CStopping the plugin...
The plugin did not exit within its 10s grace period; killing it.

An isolated loop with no Macro Deck installed. The stub is a real in-process MacroDeckTestHost, not a mock.

Terminal window
macrodeck-plugin run --project src/HelloDeck

Against the running Macro Deck. Approve the pairing prompt that appears in Macro Deck - see Pairing with a real host.

Terminal window
macrodeck-plugin run --project src/HelloDeck --watch

Against the running Macro Deck, rebuilt on every save. Leave a developer preview open and it follows along - see Watching for changes.

Terminal window
macrodeck-plugin run --artifact ./artifacts/com.example.hello-deck-1.0.0-osx-arm64.macroDeckPlugin --stub-host

Run exactly what you packed, rather than the Debug build.

Terminal window
macrodeck-plugin run --project src/HelloDeck --stub-host --mode managed

Reproduce a managed (supervisor-launched) plugin. Managed mode works only against the stub.

Terminal window
macrodeck-plugin run --executable ./bin/Debug/net10.0/HelloDeck.dll --host-url http://127.0.0.1:5000

An already-built executable or framework-dependent .dll, against a host you name explicitly.

For breakpoints, child-process attach and the direct-project launch against a desktop host, see Debugging plugins.

Exactly one of --project, --executable or --artifact is required.

Option Default Description
--project <path> - A plugin’s .csproj, or its directory.
--executable <path> - An already-built executable or framework-dependent .dll.
--artifact <path> - A packed .macroDeckPlugin artifact.
--host-url <url> the running host A real host’s URL; cannot be combined with --stub-host.
--stub-host off Run against a disposable in-process stub host.
--mode <managed|self-registering> self-registering Registration mode; managed needs --stub-host.
--pairing <true|false> true Self-registering only: let interactive pairing supply the credential when no token is given.
--enrollment-token <token> placeholder on the stub Self-registering only: required against a real host when --pairing false.
--plugin-id <id> the manifest’s id Managed only: the injected plugin id.
--secret <secret> generated on the stub Managed only.
--state-directory <dir> temp dir Self-registering only: where the persisted credential is stored.
--data-directory <dir> temp dir Managed only.
--instance-id <id> fresh id Both modes.
--launch-id <id> fresh id Managed only, diagnostic; never asserted on the wire.
--listen-url <url> http://127.0.0.1:0 Where the plugin listens; port 0 lets the OS pick.
--watch off With --project against a real host: run the plugin under dotnet watch. See Watching for changes.

Temp directories run created itself are deleted when it exits.

Terminal window
macrodeck-plugin run --project src/HelloDeck --host-url http://127.0.0.1:5000 # this host
macrodeck-plugin run --project src/HelloDeck --stub-host # no discovery at all
  • The running Macro Deck host is the default. While it runs, the host writes the loopback port it bound to macro-deck-host.port (or macro-deck-host-development.port for a Development build) in the system temp directory, and deletes it on exit. run reads it and points the plugin at http://127.0.0.1:<port> - the same URL the supervisor injects as MACRO_DECK_PLUGIN_HOST_URL.
  • Both names are probed. If a Development and a Production host run side by side, the most recently written port file wins - the host you started last.
  • No readable port file fails with host-not-found (exit 3). run never falls back to the stub silently.
Terminal window
macrodeck-plugin run --project src/HelloDeck --watch

--watch runs the project with dotnet watch run, using the environment run composes:

  • A saved change that .NET Hot Reload supports - a new text, a changed layout, a different mock value in a preview scenario - is applied to the running plugin. Open developer previews are rebuilt in place, without a restart, and the plugin’s other open views, such as widgets and configuration editors, are opened again with the new code. See Real views.
  • Any other change rebuilds the project and restarts the plugin. Macro Deck keeps the open preview on screen, marked as waiting, and reopens it when the plugin is back.

The plugin runs from the project directory, like dotnet run, and ignores launchSettings.json so a profile cannot point it at another host or state directory. The state directory is kept for the whole command, so you pair once per run --watch; pass --state-directory to keep the credential across runs.

--watch needs --project (watch-needs-project, exit 2) and a real host (watch-needs-real-host, exit 2): the stub host has no previews to follow the plugin.

A self-registered session against a real host brings the host’s copy of the plugin’s bundled icon packs in line with manifest.json when the session starts: packs are added, replaced or removed by key, so the icons are in the icon picker and on buttons without installing the plugin.

With --watch, run also watches icon-packs/ and the manifest’s bundledIconPacks. An icon-pack add or remove, or a replaced pack file, is synced right away without restarting the plugin, buttons re-render with the new icons, and open plugin views reload.

  • Only self-registered sessions sync. Nothing is synced against --stub-host or in managed mode.
  • A pack larger than 8 MiB cannot be synced live. Build and install the plugin to try it.
  • run asks the plugin to sync through MACRO_DECK_PLUGIN_BUNDLED_ICON_PACKS (sync, or watch with --watch). A host that predates bundled icon packs answers that it does not support them, and the plugin runs on without them.
  • With --project, run also sets MACRO_DECK_PLUGIN_BUNDLED_ICON_PACKS_ROOT to the project directory, so the packs are read from the project even though the plugin runs from its build output.

self-registering is the default, and the only mode a real host accepts. A managed launch needs a launch bootstrap token that only a real supervisor can mint, so --mode managed without --stub-host fails with managed-needs-stub-host (exit 2).

With pairing on (the default) and no token, run asks the host whether pairing is available and reports:

Diagnostic Meaning
developer-mode-disabled Developer Mode is off, so no prompt can appear. run keeps going; the plugin pairs as soon as you turn it on, with no restart.
pairing-prompt-expected The host could not be asked (unreachable or too old). A prompt is expected but not guaranteed.
(neither) Developer Mode is on; a prompt will appear.

Once a request is pending, the plugin writes Waiting for pairing approval in Macro Deck... to stderr, and reports the outcome the same way after you approve or reject it.

Terminal window
macrodeck-plugin run --project src/HelloDeck --pairing false --enrollment-token <token>
$ macrodeck-plugin run --project src/HelloDeck --pairing false
error enrollment-token-required: --mode self-registering against a real host needs --enrollment-token when --pairing is off.

Every inherited MACRO_DECK_PLUGIN_* variable and ASPNETCORE_URLS is scrubbed, then set fresh for the resolved mode, mirroring what the supervisor injects. Against a real host in self-registering mode run also sets MACRO_DECK_PLUGIN_BUNDLED_ICON_PACKS (see Bundled icon packs), which the supervisor never sets.

In managed mode, MACRO_DECK_PLUGIN_ID comes from the manifest.json next to the launch target, because the host rejects an injected id that disagrees with the manifest. --plugin-id overrides it. Only when neither exists does run generate a development id, and it warns:

warning plugin-id-generated: No manifest.json next to the launch target, so a development id was generated. ...
  • [plugin] lines are the plugin’s stdout and go to run’s stdout; [plugin:stderr] lines go to stderr.
  • [log:...] lines are the stub host’s forwarded logs.
  • --verbosity quiet hides [log:...] lines and progress narration. The plugin’s own output always prints.

Ctrl-C runs the supervisor’s shutdown sequence against the stub host:

  1. session.goodbye.
  2. Close with code 4004 (SupervisorShutdown).
  3. Wait for the manifest’s shutdown.gracefulTimeoutSeconds (clamped to 1-60s; 10s if absent).
  4. Kill the process tree if it is still running.

Against a real host, run owns no session to say goodbye on, so only steps 3 and 4 apply.

Code When
the plugin’s own The plugin exited on its own.
2 Usage error: wrong number of launch targets, --host-url with --stub-host, managed-needs-stub-host, enrollment-token-required, watch-needs-project, watch-needs-real-host.
3 host-not-found, a project that fails to build, an unreadable artifact, or a process that fails to start.
4 Ctrl-C, whether the plugin exited gracefully or had to be killed.

See the shared exit codes.