start method

Future<String> start({
  1. Duration timeout = const Duration(minutes: 5),
  2. List<int> ports = const [1455, 1457],
})

Starts listening and returns the callback redirect URL.

ports lists loopback ports to try in order — the first successful bind serves the callback. Defaults to the Codex-registered pair [1455, 1457] (auth.openai.com rejects off-list redirect hosts); tests pass const [0] (ephemeral) to stay hermetic where the registered pair is transiently occupied (PR #1375: EADDRINUSE on both ports flaked main twice).

Implementation

Future<String> start({
  Duration timeout = const Duration(minutes: 5),
  List<int> ports = const [1455, 1457],
}) async {
  if (ports.isEmpty) {
    throw ArgumentError.value(ports, 'ports', 'must not be empty');
  }
  await close();
  _result = Completer<ChatGptOAuthCallback?>();
  Object? lastError;
  for (final port in ports) {
    try {
      _server = await HttpServer.bind(InternetAddress.loopbackIPv4, port);
      break;
    } on Object catch (error) {
      lastError = error;
    }
  }
  if (_server == null) {
    throw StateError(
      'Could not bind ChatGPT OAuth callback port: $lastError',
    );
  }
  _timer = Timer(timeout, () => _complete(null));
  _server!.listen(_handle, onDone: () => _complete(null));
  return callbackUrl!;
}