<commit_before>require 'timeout'
require 'net/ssh'
require 'net/sftp'
require 'nissh/response'
require 'nissh/events'

module Nissh
  class Session

    include Nissh::Events

    class CommandExecutionFailed < StandardError; end

    class << self
      attr_accessor :logger
      include Nissh::Events
    end

    attr_reader :session
    attr_accessor :sudo_password
    attr_writer :logger

    def initialize(*args, &block)
      block.call(self) if block_given?
      _emit :connect do
        if args.first.is_a?(Net::SSH::Connection::Session)
          @session = args.first
        else
          @session = Net::SSH.start(*args)
        end
      end
    end

    def close
      _emit :close do
        @session.close rescue nil
      end
    end

    def execute!(commands, options = {})
      unless commands.is_a?(Array)
        commands = [commands]
      end

      if options[:sudo]
        commands = commands.map do |command|
          "sudo --stdin #{command}"
        end
      end

      command = commands.join(' && ')
      log :info, "\e[44;37m=> #{command}\e[0m"

      emit :before, :execute, command, options
      self.class.emit :before, :execute, command, options

      response = Nissh::Response.new
      response.command = command
      channel = @session.open_channel do |channel|
        channel.exec(command) do |_, success|
          raise CommandExecutionFailed, "Command \"#{command}\" was unable to execute" unless success
          channel.on_data do |_,data|
            response.stdout += data
            log :debug, data.gsub(/[\r]/, ''), :tab => 4
          end

          channel.on_extended_data do |_,_,data|
            response.stderr += data.gsub(/\r/, '')
            log :warn, data, :tab => 4
            if data =~ /^\[sudo\] password for/
              password = options[:sudo].is_a?(String) ? options[:sudo] : self.sudo_password
              channel.send_data "#{password}\n"
            end
          end

          channel.on_request("exit-status") do |_,data|
            response.exit_code = data.read_long
            log :info, "\e[43;37m=> Exit status: #{response.exit_code}\e[0m"
          end

          channel.on_request("exit-signal") do |_, data|
            response.exit_signal = data.read_long
          end
        end
      end
      channel.wait<commit_msg>support for stdin<commit_after>require 'timeout'
require 'net/ssh'
require 'net/sftp'
require 'nissh/response'
require 'nissh/events'

module Nissh
  class Session

    include Nissh::Events

    class CommandExecutionFailed < StandardError; end

    class << self
      attr_accessor :logger
      include Nissh::Events
    end

    attr_reader :session
    attr_accessor :sudo_password
    attr_writer :logger

    def initialize(*args, &block)
      block.call(self) if block_given?
      _emit :connect do
        if args.first.is_a?(Net::SSH::Connection::Session)
          @session = args.first
        else
          @session = Net::SSH.start(*args)
        end
      end
    end

    def close
      _emit :close do
        @session.close rescue nil
      end
    end

    def execute!(commands, options = {})
      unless commands.is_a?(Array)
        commands = [commands]
      end

      if options[:sudo]
        commands = commands.map do |command|
          "sudo --stdin #{command}"
        end
      end

      command = commands.join(' && ')
      log :info, "\e[44;37m=> #{command}\e[0m"

      emit :before, :execute, command, options
      self.class.emit :before, :execute, command, options

      response = Nissh::Response.new
      response.command = command
      channel = @session.open_channel do |channel|
        channel.exec(command) do |_, success|
          raise CommandExecutionFailed, "Command \"#{command}\" was unable to execute" unless success

          if options[:stdin]
            channel.send_data(options[:stdin])
          end
          channel.eof!

          channel.on_data do |_,data|
            response.stdout += data
            log :debug, data.gsub(/[\r]/, ''), :tab => 4
          end

          channel.on_extended_data do |_,_,data|
            response.stderr += data.gsub(/\r/, '')
            log :warn, data, :tab => 4
            if data =~ /^\[sudo\] password for/
              password = options[:sudo].is_a?(String) ? options[:sudo] : self.sudo_password
              channel.send_data "#{password}\n"
            end
          end

          channel.on_request("exit-status") do |_,data|
            response.exit_code = data.read_long
            log :info, "\e[43;37m=> Exit status: #{response.exit_code}\e[0m"
          end

          channel.on_request("exit-signal") do |_, data|
            response.exit_signal = data.read_long
          end
        end
      end
      channel.wait
{'fraction_non_alphanumeric': 0.1152323400708481, 'fraction_numerical': 0.005001041883725776, 'mean_word_length': 2.0112923462986196, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 11, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '768793', 'n_tokens_mistral': 1441, 'n_tokens_neox': 1378, 'n_words': 352}