runOpenRouterOAuthCliFlow function
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;
}
}