Skip to content

Feature/cli simplification - #406

Merged
hendrikmennen merged 12 commits into
mainfrom
feature/cli-simplification
Oct 19, 2026
Merged

hendrikmennen merged 12 commits into
mainfrom
feature/cli-simplification

Conversation

@hendrikmennen

@hendrikmennen hendrikmennen commented Oct 19, 2026 •

Copy link
Copy Markdown
Contributor

Add CLI

Adds a command-line interface to OneWare Studio. The commands run inside the existing OneWareStudio executable, so there is no separate CLI binary, launcher script or packaging change. Plugins can add their own commands.

oneware stop                                Stop the running Studio
oneware extensions search|list|install|update|uninstall
oneware cloud login|logout
oneware --help / --version
oneware [path | oneware://…]                Start the GUI (unchanged)

How it works

CLI dispatch (studio/OneWare.Studio.Desktop/Cli)

  • Program.Main calls StudioCli.IsCliInvocation(args) before Avalonia starts. The CLI runs when the first positional argument looks like a command name (letters, digits, -, _) and is not an existing file or folder, or when --help/--version is passed. Anything else (no arguments, a path, a oneware:// URL) starts the GUI as before.
  • Startup options (--oneware-dir, --modules, --autolaunch, -C/--working-directory, …) moved to OneWareStartupCommandLine and are shared by the GUI and the CLI.
  • Built-in CLI modules are StudioCliModule (stop), ExtensionStoreCliModule and OneWareCloudCliModule. Plugin CLI modules are discovered by reflection; this is skipped for extensions commands so a broken plugin can still be removed.

Windows: oneware.exe

OneWareStudio.exe is a GUI-subsystem app (WinExe), so it can't write to the terminal it was started from or return an exit code there. The build and publish steps create oneware.exe: a copy of the same small .NET launcher with the PE Subsystem field patched to console (SetPeConsoleSubsystem inline task, one changed byte). It loads the same OneWareStudio.dll. Without a command, oneware.exe starts OneWareStudio.exe as a separate process. The MSI already puts the install folder on PATH.

Plugin API (OneWare.Essentials, additions only)

  • IOneWareCliModule / OneWareCliModuleBase: register services and top-level System.CommandLine commands. Command names must be plain words.
  • IIpcCommandService: GUI modules register handlers for commands sent to the running Studio.
  • StudioInstanceChannel: the single-instance lock plus IPC channel, so CLI modules can call IsStudioRunning(), SendCommandAsync(name, arg) and WaitForExitAsync().
  • ISettingsService.SaveValues(path, values): updates individual keys in the settings file without overwriting the rest (used by the CLI while Studio may be running).

Single instance and IPC (reworked)

  • The lock file and pipe are now per user: %TEMP%\OneWare and pipe oneware-studio-ipc-<user> on Windows, $XDG_RUNTIME_DIR/oneware (fallback /tmp/oneware-<user>, mode 0700) on Linux, $TMPDIR/OneWare on macOS. Unix sockets live in that directory, and PipeOptions.CurrentUserOnly is used everywhere. Previously, two users on the same Linux machine blocked each other.
  • Removed the PID/stale-lock logic. While the lock is held, other processes can't read it, and the OS releases it when the process dies.
  • Messages are cmd:<name>[\n<arg>]. Plain paths, oneware:// URLs and the old activateWindow message still work.
  • IPC messages that arrive during startup are queued until startup content has loaded, instead of being dropped.
  • The server keeps the next listener open before closing the current one, so quick consecutive messages (for example two file opens) are not lost on Unix.
  • A second instance now exits with code 1 when it can't forward its request.

Commands that affect a running Studio

  • oneware stop sends shutdown and waits up to 10 s for the lock to be released. If Studio is waiting on an unsaved-changes dialog, the command says so and exits 1.
  • oneware cloud login|logout writes the account setting and notifies a running Studio (cloud-login <userId> / cloud-logout), so its next settings save no longer reverts the change.

Plugin loading

PluginService assembly and native-library loading moved into PluginAssemblyLoader / PluginNativeLibraryResolver so the CLI can load plugins without the GUI.

Compatibility notes

  • No changes to the Linux packaging (Flatpak/snap/.desktop) or the MSI layout; the executable name and the oneware:// association are unchanged.
  • Essentials API changes are additions only. But: System.CommandLine 3.0.0-rc.1 (a pre-release) is now referenced by Essentials and is part of the plugin API through IOneWareCliModule. Core previously used 2.0.2.
  • ISettingsService.SaveValues has no default implementation, so third-party ISettingsService implementations (unlikely) would break.

Testing

  • New unit tests: StudioInstanceChannelTests (message format, lock probe, send/receive including a 20-message burst), IpcCommandServiceTests, StudioCliTests (CLI vs. GUI argument detection), ExtensionStoreCliModuleTests, PluginNativeLibraryResolverTests.
  • Full solution build and dotnet test OneWare.slnx pass.
  • Checked that a win-x64 publish produces oneware.exe with subsystem 3 (only one byte differs from OneWareStudio.exe).
  • Manual: --help, an unknown command (exit 1), -C <dir> stop, and stop with no Studio running or with an unreachable instance.

Follow-ups (not in this PR)

  • Move to a stable System.CommandLine release before shipping the plugin API.
  • Decide how extensions install/uninstall should behave while Studio is running.

@hendrikmennen
hendrikmennen merged commit d525eb9 into main Oct 19, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants