Files
buzz/mobile/lib/main.dart
Will Pfleger 65e864301d feat: send a User-Agent on relay WebSocket upgrades (#9246)
Relay WebSocket clients in this repo now send a `User-Agent` of the form
`<name>/<version>` on the upgrade request. The version comes from the
build.

| Client | User-Agent |
|---|---|
| `buzz-acp` | `buzz-acp/<crate version>` |
| `buzz-cli` | `buzz-cli/<crate version>` |
| `buzz-ws-client` (default) | `buzz-ws-client/<crate version>` |
| `buzz-pairing-cli` | `buzz-pairing-cli/<crate version>` |
| `buzz-audio-client` (default) | `buzz-audio-client/<crate version>` |
| mobile (relay and huddle sockets) | `Buzz-Mobile/<app version>` |

- **`buzz-ws-client`**: every connect path sends the default. A
`User-Agent` in the caller's `connect_with_headers` headers replaces it,
so other consumers can identify themselves without any new API.
`buzz-cli` uses this to send its own name.
- **`buzz-audio-client`**: keeps a `User-Agent` already on the
host-supplied request, and adds its default otherwise.
- **Mobile**: `openRelayWebSocket` sets the HTTP client's `userAgent`,
which also covers the huddle transport. Startup waits up to 500 ms for
the app version from `package_info_plus` before the app runs, so the
first socket carries it; if the lookup fails or times out, the value is
`Buzz-Mobile`.

Each client has a test asserting the header on the upgrade request.

---------

Signed-off-by: Will Pfleger <wpfleger@block.xyz>
Co-authored-by: Hayt <211b96e6a2b7f45fd4047988976c7bbbeeda0c15f3ae7b32eec20834b5a55118@buzz.block.builderlab.xyz>
2026-11-08 20:43:55 -05:00

58 lines
2.0 KiB
Dart

import 'dart:async';
import 'package:flutter/material.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'app.dart';
import 'features/age_gate/age_signal_push_bootstrap.dart';
import 'features/agents/agents_page.dart';
import 'features/invites/invite_join_provider.dart';
import 'shared/agents/agents_surface.dart';
import 'shared/push/push_bridge.dart';
import 'shared/relay/relay_closed_policy.dart';
import 'shared/relay/relay_websocket.dart';
import 'shared/theme/theme_provider.dart';
void main() => runBuzzApp(const App());
Future<void> runBuzzApp(Widget app) async {
WidgetsFlutterBinding.ensureInitialized();
installBuzzPushMethodHandler();
// Relay upgrades name the app version, so learn it before any socket opens.
final userAgent = loadRelayUserAgent(
() async => (await PackageInfo.fromPlatform()).version,
);
// Pre-load preferences so the first frame uses the saved theme/accent.
final prefs = await SharedPreferences.getInstance();
await userAgent;
runApp(buildRootProviderScope(prefs: prefs, child: app));
}
/// The app's root scope, shared with tests so its production wiring is
/// asserted rather than re-declared.
ProviderScope buildRootProviderScope({
required SharedPreferences prefs,
required Widget child,
}) => ProviderScope(
// A relay statement deadline cannot resolve on retry — the same expensive
// query would re-run and hit the same limit. Return null (no retry) for
// deadline errors and fall through to the default exponential backoff for
// everything else.
retry: relayProviderRetry,
overrides: [
agentCreationSurfaceProvider.overrideWithValue(
(context, link) async => unawaited(showAgents(context, draft: link)),
),
savedPrefsProvider.overrideWithValue(prefs),
inviteJoinRecoveryProvider.overrideWith(
(ref) =>
(scope) => buildMobileInviteJoinRecovery(ref, scope),
),
],
child: AgeSignalPushBootstrap(child: child),
);