Skip to content

Add producer-friendly FriendNet bridge status and startup - #2

Merged
SeamusMullan merged 1 commit into
mainfrom
friendnet-ease-of-use
Apr 11, 2026
Merged

Add producer-friendly FriendNet bridge status and startup#2
SeamusMullan merged 1 commit into
mainfrom
friendnet-ease-of-use

Conversation

@SeamusMullan

Copy link
Copy Markdown
Member

Summary

  • replace the raw bridge status dump with structured bridge and FriendNet state in the Vue UI and document the expanded /v1/status response
  • wire wavgang-bridge to poll FriendNet over Connect RPC, auto-start a bundled friendnet-server, and report producer-friendly status values
  • build and package the Go helper binaries in CI and hook WavGang's CMake build into the helper staging support from sudara/cmake-includes#18

Test plan

  • cd bridge && go build .
  • cd ui && bun run build && bun run test
  • cmake -S . -B build-agent -DCMAKE_BUILD_TYPE=Release
  • cmake --build build-agent --target WavGang_Standalone -j 4
  • cmake --build build-agent --target WavGang_VST3 -j 4
  • verify helper binaries land next to the built standalone and copied VST3 artefacts

Made with Cursor

This wires the bridge to poll FriendNet, surfaces readable connection state in the UI, and builds the helper binaries needed to make local tester installs behave like one app.

Made-with: Cursor
Copilot AI review requested due to automatic review settings April 11, 2026 20:11

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR upgrades the bridge/UI to report a structured, producer-friendly FriendNet/bridge status, adds FriendNet polling + bundled server autostart in the Go bridge, and updates build/packaging to stage the helper binaries.

Changes:

  • Replaced raw /v1/status dump in the Vue UI with structured bridge + FriendNet status presentation.
  • Implemented /v1/status engine in wavgang-bridge to poll FriendNet over Connect RPC and optionally autostart a bundled friendnet-server.
  • Updated CI and build tooling to build/package the Go helper binaries and integrate helper staging into the CMake build.

Reviewed changes

Copilot reviewed 12 out of 14 changed files in this pull request and generated 10 comments.

Show a summary per file
File Description
ui/src/stores/bridge.ts Switches the store from raw text status to typed JSON + computed UI strings.
ui/src/App.vue Updates UI to show friendly connection status and retry UX, adds styling.
source/HostLauncher.h Adds FriendNet executable lookup API (for bundled layout parity).
source/HostLauncher.cpp Adds FriendNet executable discovery; changes bridge startup reachability retry behavior.
scripts/build-go-binaries.sh Adds a local script to build bridge + FriendNet server (and admin UI embed).
docs/BRIDGE_API.md Documents expanded /v1/status response and bridge/FriendNet configuration.
CMakeLists.txt Integrates helper staging via BridgeHelpers and wvg_enable_local_helpers.
bridge/status.go Introduces structured status snapshot + FriendNet poller via Connect RPC.
bridge/main.go Wires status engine, signal context, FriendNet autostart, and new /v1/status handler.
bridge/launcher.go Adds FriendNet server discovery, layout bootstrap, and autostart logic.
bridge/go.mod / bridge/go.sum Adds Connect RPC + FriendNet protocol dependencies (with local replace).
.gitignore Ignores staged friendnet-server binaries in bridge/.
.github/workflows/build_and_test.yml Builds friendnet admin UI + Go binaries in CI and uploads artifacts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread CMakeLists.txt
include(PamplejuceMacOS)
include(JUCEDefaults)
include(Sanitizers)
include(BridgeHelpers)

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

include(BridgeHelpers) / wvg_enable_local_helpers() are introduced, but there is no BridgeHelpers.cmake (or wvg_enable_local_helpers definition) in the repo checkout outside the cmake submodule. Ensure the cmake submodule pointer is updated in this PR to a revision that provides these modules/functions; otherwise CMake configure will fail for consumers who check out this commit.

Suggested change
include(BridgeHelpers)
include(BridgeHelpers OPTIONAL RESULT_VARIABLE BRIDGEHELPERS_MODULE)
if(COMMAND wvg_enable_local_helpers)
wvg_enable_local_helpers()
elseif(BRIDGEHELPERS_MODULE STREQUAL "NOTFOUND")
message(STATUS "BridgeHelpers.cmake not found; skipping wvg_enable_local_helpers().")
endif()

Copilot uses AI. Check for mistakes.
Comment thread CMakeLists.txt
target_link_libraries(SharedCode INTERFACE ${_LINK_LIBS})
target_link_libraries("${PROJ_NAME}" PRIVATE SharedCode)
add_dependencies("${PROJ_NAME}" wavgang_webui)
wvg_enable_local_helpers("${PROJ_NAME}")

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wvg_enable_local_helpers("${PROJ_NAME}") is called here, but the function isn’t defined anywhere in the repo checkout (outside the cmake submodule). If the submodule revision isn’t updated to include this helper, this will be a hard CMake configure error. Please ensure the required cmake-includes submodule update is included with this change.

Suggested change
wvg_enable_local_helpers("${PROJ_NAME}")
if(COMMAND wvg_enable_local_helpers)
wvg_enable_local_helpers("${PROJ_NAME}")
endif()

Copilot uses AI. Check for mistakes.
Comment thread source/HostLauncher.cpp
Comment on lines +69 to +89
for (int i = 0; i < 5; ++i)
{
if (isReachable (baseUrl))
return;

if (i > 0)
juce::Thread::sleep (150);
}

auto exe = findBridgeExecutable();
if (!exe.existsAsFile())
return;

(void) exe.startAsProcess();

for (int i = 0; i < 8; ++i)
{
juce::Thread::sleep (150);
if (isReachable (baseUrl))
return;
}

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ensureBridgeRunning() is documented as safe to call on the message thread, but this retry loop runs synchronous reachability checks and can block (even before trying to launch the bridge). Since this is called from PluginEditor’s constructor, it can freeze the UI. Consider moving polling off the message thread or making this function fire-and-forget.

Suggested change
for (int i = 0; i < 5; ++i)
{
if (isReachable (baseUrl))
return;
if (i > 0)
juce::Thread::sleep (150);
}
auto exe = findBridgeExecutable();
if (!exe.existsAsFile())
return;
(void) exe.startAsProcess();
for (int i = 0; i < 8; ++i)
{
juce::Thread::sleep (150);
if (isReachable (baseUrl))
return;
}
juce::Thread::launch ([baseUrl]
{
for (int i = 0; i < 5; ++i)
{
if (isReachable (baseUrl))
return;
if (i > 0)
juce::Thread::sleep (150);
}
auto exe = HostLauncher::findBridgeExecutable();
if (!exe.existsAsFile())
return;
(void) exe.startAsProcess();
for (int i = 0; i < 8; ++i)
{
juce::Thread::sleep (150);
if (isReachable (baseUrl))
return;
}
});

Copilot uses AI. Check for mistakes.
Comment thread source/HostLauncher.cpp
Comment on lines +84 to +88
for (int i = 0; i < 8; ++i)
{
juce::Thread::sleep (150);
if (isReachable (baseUrl))
return;

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This post-launch wait loop sleeps on the calling thread (potentially ~1.2s). If ensureBridgeRunning() is invoked on the message thread (as it currently is), this can cause a visible UI stall. Prefer an async wait (Timer/background thread) or remove the blocking sleep/retry.

Copilot uses AI. Check for mistakes.
Comment thread ui/src/App.vue
Comment on lines +33 to +35
<li :class="{ ok: bridgeLine.includes('OK'), bad: !bridgeLine.includes('OK') }">
{{ bridgeLine }}
</li>

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bridge status class is determined by bridgeLine.includes('OK'), which is brittle (changing copy breaks styling) and currently treats the initial "Bridge: …" state as bad. Prefer driving classes from structured state (loading, status.bridge_ok) and adding an explicit waiting state.

Copilot uses AI. Check for mistakes.
Comment thread ui/src/App.vue
Comment on lines +38 to +42
:class="{
ok: friendnetLine.includes('connected'),
bad: friendnetLine.includes('not') || friendnetLine.includes('wrong') || friendnetLine.includes('did not'),
wait: friendnetLine.includes('Checking'),
}"

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FriendNet status styling is based on substring checks against the rendered sentence (e.g. includes('connected'), includes('did not')). This is fragile and can misclassify states if wording changes. Prefer mapping classes directly from status.friendnet.state (and loading) instead of parsing the headline string.

Copilot uses AI. Check for mistakes.
Comment thread bridge/main.go
Comment on lines +42 to +46
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()

startBundledFriendNet()
go eng.runPoller(ctx)

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

signal.NotifyContext() intercepts SIGINT/SIGTERM, but the HTTP server is started with http.ListenAndServe and never shut down on <-ctx.Done(). This can prevent Ctrl+C/SIGTERM from stopping the process cleanly. Consider using an http.Server and calling Shutdown() when the context is canceled.

Copilot uses AI. Check for mistakes.
Comment thread bridge/launcher.go
Comment on lines +138 to +142
cmd := exec.Command(exe, "-config", cfgPath, "-nocli")
cmd.Dir = dataDir
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err := cmd.Start(); err != nil {

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The bridge autostarts friendnet-server as a child process here, but there’s no corresponding shutdown/cleanup on bridge exit. This can leave friendnet-server orphaned after wavgang-bridge stops. Consider tying the child lifecycle to the bridge’s signal context (terminate on shutdown) or otherwise ensuring the subprocess is cleaned up.

Copilot uses AI. Check for mistakes.
Comment thread bridge/status.go

// FriendnetStatus is producer-oriented state for the FriendNet server RPC.
type FriendnetStatus struct {
State string `json:"state"` // stopped | starting | reachable | unreachable | misconfigured

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The doc comment for FriendnetStatus.State doesn’t match the actual states emitted by the implementation/UI/docs (e.g. unknown, auth_required). Update the comment (or constrain the state values) so the type documentation matches the JSON contract.

Suggested change
State string `json:"state"` // stopped | starting | reachable | unreachable | misconfigured
// State is the JSON contract for FriendNet status:
// unknown | auth_required | stopped | starting | reachable | unreachable | misconfigured
State string `json:"state"`

Copilot uses AI. Check for mistakes.
Comment thread bridge/status.go
Comment on lines +97 to +101
rpc := e.rpcURL
cli := newRPCClient(rpc)
fnCtx, cancel := context.WithTimeout(ctx, 4*time.Second)
defer cancel()

Copilot AI Apr 11, 2026

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refreshFriendnet() constructs a new RPC client (and underlying http.Client) on every poll. Since http.Client manages connection pooling, recreating it repeatedly can cause unnecessary connection churn and GC pressure. Prefer creating/reusing a shared http.Client / RPC client on the statusEngine and reusing it across polls.

Copilot uses AI. Check for mistakes.
@SeamusMullan
SeamusMullan merged commit 7f30b08 into main Apr 11, 2026
5 of 9 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