Class SidecarBowireProtocol
- Namespace
- Kuestenlogik.Bowire.Plugins.Sidecar
- Assembly
- Kuestenlogik.Bowire.dll
Adapter that implements IBowireProtocol by proxying
every call into a sidecar process over JSON-RPC. One instance per
SidecarPluginManifest. See
docs/architecture/sidecar-plugins.md for the wire spec.
public sealed class SidecarBowireProtocol : IBowireProtocol, IBowireDeferredSettings
- Inheritance
-
SidecarBowireProtocol
- Implements
- Inherited Members
Remarks
Process spawn is **lazy** — the constructor only stores the manifest.
The first DiscoverAsync(string, bool, CancellationToken) / InvokeAsync(string, string, string, List<string>, bool, Dictionary<string, string>?, CancellationToken)
call spawns the executable and sends initialize. Hosts that
scan the registry without calling anything (e.g. plugin list)
don't pay the cost of starting every sidecar.
Surface: discover, invoke, invokeStream, and full-duplex channels
(OpenChannelAsync(string, string, string, bool, Dictionary<string, string>?, CancellationToken)). A sidecar that doesn't implement
channels advertises capabilities.channels = false in its
initialize reply, in which case OpenChannelAsync(string, string, string, bool, Dictionary<string, string>?, CancellationToken)
returns null without a round-trip.
The initialize handshake carries a wire-contract version both
ways (SidecarProtocolVersion): the host rejects a sidecar
whose version falls outside
[MinSupportedSidecarProtocolVersion,
SidecarProtocolVersion] and tolerates a legacy sidecar
that advertises none (with a warning).
Constructors
SidecarBowireProtocol(SidecarPluginManifest, string, ILogger?)
public SidecarBowireProtocol(SidecarPluginManifest manifest, string pluginDir, ILogger? logger = null)
Parameters
manifestSidecarPluginManifestpluginDirstringloggerILogger
Fields
MinSupportedSidecarProtocolVersion
Oldest sidecar contract version this host still accepts.
public const int MinSupportedSidecarProtocolVersion = 1
Field Value
SidecarProtocolVersion
The sidecar JSON-RPC wire-contract version this host speaks (#416).
Bump only on a breaking envelope change. A sidecar advertises the
version it implements in its initialize reply; the host accepts
anything in [MinSupportedSidecarProtocolVersion, this].
public const int SidecarProtocolVersion = 1
Field Value
Properties
IconSvg
SVG icon for the protocol tab.
public string IconSvg { get; }
Property Value
Id
Short identifier (e.g., "grpc", "signalr").
public string Id { get; }
Property Value
Name
Protocol name shown in UI tabs.
public string Name { get; }
Property Value
Settings
Settings the sidecar declared in its initialize reply (#693).
public IReadOnlyList<BowirePluginSetting> Settings { get; }
Property Value
Remarks
Empty until the handshake has happened, because until then there is no truthful answer — the declaration lives in a process that has not been started. A caller that wants the real list calls PrepareSettingsAsync(CancellationToken) first; that is what the Settings dialog does. Keeping the declaration in the sidecar rather than mirroring it into the manifest means there is one place to change it when it changes.
Methods
DiscoverAsync(string, bool, CancellationToken)
Discover available services and methods.
public Task<List<BowireServiceInfo>> DiscoverAsync(string serverUrl, bool showInternalServices, CancellationToken ct = default)
Parameters
serverUrlstringshowInternalServicesboolctCancellationToken
Returns
Initialize(IServiceProvider?)
Called after registration to inject the app's service provider (embedded mode).
public void Initialize(IServiceProvider? serviceProvider)
Parameters
serviceProviderIServiceProvider
InvokeAsync(string, string, string, List<string>, bool, Dictionary<string, string>?, CancellationToken)
Invoke a unary or client-streaming call.
public Task<InvokeResult> InvokeAsync(string serverUrl, string service, string method, List<string> jsonMessages, bool showInternalServices, Dictionary<string, string>? metadata = null, CancellationToken ct = default)
Parameters
serverUrlstringservicestringmethodstringjsonMessagesList<string>showInternalServicesboolmetadataDictionary<string, string>ctCancellationToken
Returns
InvokeStreamAsync(string, string, string, List<string>, bool, Dictionary<string, string>?, CancellationToken)
Invoke a server-streaming or duplex call.
public IAsyncEnumerable<string> InvokeStreamAsync(string serverUrl, string service, string method, List<string> jsonMessages, bool showInternalServices, Dictionary<string, string>? metadata = null, CancellationToken ct = default)
Parameters
serverUrlstringservicestringmethodstringjsonMessagesList<string>showInternalServicesboolmetadataDictionary<string, string>ctCancellationToken
Returns
OpenChannelAsync(string, string, string, bool, Dictionary<string, string>?, CancellationToken)
Open an interactive channel (for duplex/client-streaming).
public Task<IBowireChannel?> OpenChannelAsync(string serverUrl, string service, string method, bool showInternalServices, Dictionary<string, string>? metadata = null, CancellationToken ct = default)
Parameters
serverUrlstringservicestringmethodstringshowInternalServicesboolmetadataDictionary<string, string>ctCancellationToken
Returns
PrepareSettingsAsync(CancellationToken)
Do whatever is needed to make Settings
answer truthfully — for a sidecar, spawn the process and complete the
initialize handshake.
public Task PrepareSettingsAsync(CancellationToken ct = default)
Parameters
Returns
Remarks
Implementations must not throw: a plugin that cannot be prepared reports no settings, which is the same thing it reported before. The Settings dialog lists every other plugin either way.