runOpenRouterOAuthCliFlow function

Future<OpenRouterOAuthKey?> runOpenRouterOAuthCliFlow({
  1. required void onStatus(
    1. String
    ),
  2. Future<bool> openBrowserFn(
    1. String url
    ) = openBrowser,
  3. Future<OpenRouterOAuthKey> exchangeFn({
    1. required String code,
    2. required String codeVerifier,
    3. String? label,
    }) = _defaultExchange,
  4. String keyLabel = openRouterDefaultKeyLabel,
})

Runs the full automatic OAuth flow for the CLI: starts a localhost server, opens the browser, waits for the callback, and exchanges the code.

onStatus receives human-readable status lines ("open this URL", "waiting", etc.). openBrowserFn and exchangeFn are injectable for tests.

Implementation

Future<OpenRouterOAuthKey?> runOpenRouterOAuthCliFlow({
  required void Function(String) onStatus,
  Future<bool> Function(String) openBrowserFn = openBrowser,
  Future<OpenRouterOAuthKey> Function({
        required String code,
        required String codeVerifier,
        String? label,
      })
      exchangeFn =
      _defaultExchange,
  String keyLabel = openRouterDefaultKeyLabel,
}) async {
  final verifier = generateOpenRouterCodeVerifier();
  final challenge = generateOpenRouterCodeChallenge(verifier);
  final server = OpenRouterOAuthLocalCallbackServer();

  final callbackUrl = await server.start();
  onStatus('listening for OAuth callback on $callbackUrl');

  final authUrl = buildOpenRouterAuthUrl(
    codeChallenge: challenge,
    callbackUrl: callbackUrl,
    keyLabel: keyLabel,
  );

  final opened = await openBrowserFn(authUrl.toString());
  if (opened) {
    onStatus('browser opened; complete authorization on the OpenRouter page');
  } else {
    onStatus('could not open browser automatically');
    onStatus('open this URL manually: $authUrl');
  }

  final code = await server.waitForCode();
  if (code == null || code.isEmpty) {
    onStatus('no authorization code received (timeout or cancelled)');
    return null;
  }
  onStatus('authorization code received, exchanging for API key...');

  try {
    final key = await exchangeFn(
      code: code,
      codeVerifier: verifier,
      label: keyLabel,
    );
    onStatus('OpenRouter authorized');
    return key;
  } on Exception catch (e) {
    onStatus('authorization failed: $e');
    return null;
  }
}