App-karr
view release on metacpan or search on metacpan
lib/App/karr/Foundation/Runner.pm view on Meta::CPAN
my $started = time;
my $output = '';
my $timed_out = 0;
my $sel = IO::Select->new($reader);
while (1) {
my $wait;
if ( $max_runtime > 0 ) {
$wait = $max_runtime - ( time - $started );
if ( $wait <= 0 ) { $timed_out = 1; last }
}
# undef $wait => block indefinitely (max_runtime: 0 disables the timeout).
my @ready = $sel->can_read($wait);
unless (@ready) {
# Spurious wakeup (signal) or deadline. Only the deadline ends the loop.
next unless $max_runtime > 0;
if ( time - $started >= $max_runtime ) { $timed_out = 1; last }
next;
}
my $chunk;
my $n = sysread( $reader, $chunk, 65536 );
last if !defined $n; # read error
last if $n == 0; # EOF â the command closed its output
print {$log_fh} $chunk;
print $chunk if $stream_terms;
$output .= $chunk;
}
my $exit_code;
if ($timed_out) {
my $elapsed = time - $started;
$self->foundation->_append_log( $repo, "TIMEOUT after ${elapsed}s â sending SIGTERM to $pid" );
kill 'TERM', $pid;
sleep 2;
kill 'KILL', $pid;
waitpid( $pid, 0 );
$exit_code = -1;
} else {
waitpid( $pid, 0 );
$exit_code = $? >> 8;
}
close $reader;
close $log_fh;
my $elapsed = time - $started;
$self->foundation->_append_log( $repo, "END elapsed=${elapsed}s exit=$exit_code" );
return ( $exit_code, $output );
}
# ---------------------------------------------------------------------------
# Common-error detection
# ---------------------------------------------------------------------------
sub _error_patterns {
my ( $self, $karr ) = @_;
my @default = (
'rate limit', 'rate-limit', 'usage limit', 'quota exceeded', 'quota',
'overloaded', 'too many requests', '429', '529',
'unauthorized', 'forbidden', 'authentication', 'invalid api key',
'credentials', '401', '403',
'connection refused', 'connection reset', 'network', 'timed out',
'service unavailable', '503', '500 internal',
);
return [ @default, @{ $karr->{error_patterns} // [] } ];
}
sub _match_error {
my ( $self, $text, $patterns ) = @_;
return undef unless defined $text && length $text;
for my $p ( @$patterns ) {
return $p if $text =~ /\Q$p\E/i;
}
return undef;
}
1;
__END__
=pod
=encoding UTF-8
=head1 NAME
App::karr::Foundation::Runner - karr-foundation command execution â fork/pipe/select tee + error classification
=head1 VERSION
version 0.400
=head1 DESCRIPTION
L<App::karr::Foundation::Runner> runs a single agent command for
L<App::karr::Foundation>. It forks the command under C</bin/sh -c>, reads its
combined stdout/stderr over a native pipe, and tees each chunk to the
persistent C<.karr.log>, the terminal (when streaming), and an in-memory buffer
used for error scanning, enforcing the per-run C<max_runtime> timeout. It also
classifies observable common errors (rate limit, auth, network, 5xx, ...). A
weak back-reference to the owning foundation supplies shared options and helpers
(C<dry_run>, C<_stream_to_terminal>, C<_prompt_for>, C<_append_log>,
C<_say_verbose>).
=head1 SUPPORT
=head2 Issues
Please report bugs and feature requests on GitHub at
L<https://github.com/Getty/karr/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
( run in 0.553 second using v1.01-cache-2.11-cpan-6aa56a78535 )