Group
Extension

Langertha-Knarr/lib/Langertha/Knarr/CLI.pm

package Langertha::Knarr::CLI;
# ABSTRACT: CLI entry point for Knarr LLM Proxy
use Moo;
use MooX::Cmd;
use MooX::Options protect_argv => 0;


our $VERSION = '1.103';

option config => (
  is      => 'ro',
  format  => 's',
  short   => 'c',
  doc     => 'Config file path (default: ./knarr.yaml)',
  default => sub { './knarr.yaml' },
);


option verbose => (
  is      => 'ro',
  short   => 'v',
  doc     => 'Enable verbose logging (or set KNARR_DEBUG=1)',
  default => sub { $ENV{KNARR_DEBUG} ? 1 : 0 },
  negativable => 1,
);


# MooX::Options maps a dashed long option (--log-file) to its underscore
# attribute (log_file), but its argv rewrite takes the token after a
# boolean flag as that flag's value and passes it through unmapped — so
# `--from-env --log-file X` reached Getopt::Long as `--log-file` and died
# with "Unknown option". Map every long option name up front; arguments
# not starting with `--` and everything after a bare `--` are left alone.
around new_with_cmd => sub {
  my ( $orig, $class, @args ) = @_;
  local @ARGV = $class->normalize_argv(@ARGV);
  return $class->$orig(@args);
};

sub normalize_argv {
  my ( $class, @argv ) = @_;
  my @out;
  while (@argv) {
    my $arg = shift @argv;
    if ( $arg eq '--' ) {
      push @out, $arg, @argv;
      last;
    }
    if ( my ( $neg, $name, $value ) = $arg =~ /\A--(no-)?([A-Za-z][A-Za-z0-9_-]*)(=.*)?\z/s ) {
      $name =~ tr/-/_/;
      $arg = '--'.( $neg // '' ).$name.( $value // '' );
    }
    push @out, $arg;
  }
  return @out;
}


sub execute {
  my ($self) = @_;
  print _banner();
  print "\n";
  print "USAGE\n";
  print "  knarr <command> [options]\n\n";
  print "COMMANDS\n";
  print "  start       Start the proxy server\n";
  print "  init        Scan environment and generate configuration\n";
  print "  models      List configured models and their backends\n";
  print "  check       Validate configuration file\n\n";
  print "GLOBAL OPTIONS\n";
  print "  -c, --config <path>   Config file (default: ./knarr.yaml)\n";
  print "  -v, --verbose         Enable verbose logging\n\n";
  print "QUICK START (Docker)\n";
  print "  docker run -e OPENAI_API_KEY=sk-... -p 8080:8080 raudssus/langertha-knarr\n\n";
  print "QUICK START (Local)\n";
  print "  knarr init > knarr.yaml\n";
  print "  knarr start\n\n";
  print "EXAMPLES\n";
  print "  knarr start                              # Start with ./knarr.yaml\n";
  print "  knarr start -c production.yaml -p 9090   # Custom config and port\n";
  print "  knarr start --from-env                   # Auto-detect from ENV\n";
  print "  knarr start --from-env -p 8080 -p 11434  # ENV config, custom ports\n";
  print "  knarr init > knarr.yaml                   # Generate config\n";
  print "  knarr models                              # List configured models\n";
  print "  knarr check                               # Validate config\n\n";
  print "ENVIRONMENT\n";
  print "  OPENAI_API_KEY        OpenAI API key\n";
  print "  ANTHROPIC_API_KEY     Anthropic API key\n";
  print "  LANGFUSE_PUBLIC_KEY   Langfuse public key (enables tracing)\n";
  print "  LANGFUSE_SECRET_KEY   Langfuse secret key\n";
  print "  LANGFUSE_URL          Langfuse URL (default: https://cloud.langfuse.com)\n";
  print "  KNARR_API_KEY         Proxy authentication key (optional)\n\n";
  print "Version $VERSION | https://github.com/Getty/langertha-knarr\n";
}

sub _banner {
  return <<'BANNER';
         .  *  .
        . _/|_ .          KNARR
     .  /|    |\ .        Langertha LLM Proxy
   ~~~~~|______|~~~~~
   ~~ ~~~~~~~~~~~~~ ~~    Cargo transport for your LLM calls
   ~~~~~~~~~~~~~~~~~~~~
BANNER
}

1;

__END__

=pod

=encoding UTF-8

=head1 NAME

Langertha::Knarr::CLI - CLI entry point for Knarr LLM Proxy

=head1 VERSION

version 1.103

=head1 DESCRIPTION

MooX::Cmd entry point for the C<knarr> CLI. Dispatches to subcommand classes
under C<Langertha::Knarr::CLI::Cmd::*>. When invoked without a subcommand,
prints a banner and usage summary.

For full CLI documentation see L<knarr> and L<Langertha::Knarr>.

=head1 SEE ALSO

=over

=item * L<knarr> — CLI synopsis and option reference

=item * L<Langertha::Knarr> — Full documentation

=item * L<Langertha::Knarr::CLI::Cmd::Start> — C<knarr start>

=item * L<Langertha::Knarr::CLI::Cmd::Init> — C<knarr init>

=item * L<Langertha::Knarr::CLI::Cmd::Models> — C<knarr models>

=item * L<Langertha::Knarr::CLI::Cmd::Check> — C<knarr check>

=back

=head2 --config

Path to the YAML configuration file. Short form: C<-c>. Defaults to
C<./knarr.yaml>. Accepted before the subcommand (C<knarr -c prod.yaml
start>) and after C<start>, C<check> and C<models> (C<knarr start -c
prod.yaml>, see L<Langertha::Knarr::CLI::Role::GlobalOptions>); a value
after the subcommand wins.

=head2 --verbose

Enable verbose logging to stderr. Short form: C<-v>. Also enabled by
C<KNARR_DEBUG=1>. Accepted before the subcommand and after C<start>,
C<check> and C<models>, like L</--config>.

=head2 normalize_argv

    my @argv = Langertha::Knarr::CLI->normalize_argv(@ARGV);

Rewrites long option names from their dashed to their underscore spelling
(C<--log-file=x> becomes C<--log_file=x>, C<--no-verbose> keeps its C<no->
prefix). Arguments that do not start with C<--> (short options and option
values) and everything after a bare C<--> are left untouched.
C<new_with_cmd> applies it to C<@ARGV> before parsing, so every documented
dashed option works in any position.

=head1 SUPPORT

=head2 Issues

Please report bugs and feature requests on GitHub at
L<https://github.com/Getty/langertha-knarr/issues>.

=head2 IRC

Join C<#langertha> on C<irc.perl.org> or message Getty directly.

=head1 CONTRIBUTING

Contributions are welcome! Please fork the repository and submit a pull request.

=head1 AUTHOR

Torsten Raudssus <torsten@raudssus.de> L<https://raudssus.de/>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2026 by Torsten Raudssus.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut


Powered by Groonga
Maintained by Kenichi Ishigaki <ishigaki@cpan.org>. If you find anything, submit it on GitHub.