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

manifest SidecarPluginManifest
pluginDir string
logger ILogger

Fields

MinSupportedSidecarProtocolVersion

Oldest sidecar contract version this host still accepts.

public const int MinSupportedSidecarProtocolVersion = 1

Field Value

int

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

int

Properties

IconSvg

SVG icon for the protocol tab.

public string IconSvg { get; }

Property Value

string

Id

Short identifier (e.g., "grpc", "signalr").

public string Id { get; }

Property Value

string

Name

Protocol name shown in UI tabs.

public string Name { get; }

Property Value

string

Settings

Settings the sidecar declared in its initialize reply (#693).

public IReadOnlyList<BowirePluginSetting> Settings { get; }

Property Value

IReadOnlyList<BowirePluginSetting>

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

serverUrl string
showInternalServices bool
ct CancellationToken

Returns

Task<List<BowireServiceInfo>>

Initialize(IServiceProvider?)

Called after registration to inject the app's service provider (embedded mode).

public void Initialize(IServiceProvider? serviceProvider)

Parameters

serviceProvider IServiceProvider

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

serverUrl string
service string
method string
jsonMessages List<string>
showInternalServices bool
metadata Dictionary<string, string>
ct CancellationToken

Returns

Task<InvokeResult>

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

serverUrl string
service string
method string
jsonMessages List<string>
showInternalServices bool
metadata Dictionary<string, string>
ct CancellationToken

Returns

IAsyncEnumerable<string>

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

serverUrl string
service string
method string
showInternalServices bool
metadata Dictionary<string, string>
ct CancellationToken

Returns

Task<IBowireChannel>

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

ct CancellationToken

Returns

Task

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.