~kmaasrud/shatgpt

33f4e9a70734553b418db9f6ca07f8e1cd873b90 — Knut Magnus Aasrud 8 months ago c3b7f04
formatting
1 files changed, 135 insertions(+), 137 deletions(-)

M shatgpt
M shatgpt => shatgpt +135 -137
@@ 1,64 1,62 @@
#!/usr/bin/env sh

err() {
    printf 'error: %s\n' "$1" >&2
	printf 'error: %s\n' "$1" >&2
}

has() {
    # check if a command exists
    # $1 - cmd to check
    cmd=$(command -v "$1") 2>/dev/null || return 1
    [ -x "$cmd" ] || return 1
	# check if a command exists
	# $1 - cmd to check
	cmd=$(command -v "$1") 2>/dev/null || return 1
	[ -x "$cmd" ] || return 1
}

editor() {
    # get the user's editor
    printf '%s' "${EDITOR:-nano}"
	# get the user's editor
	printf '%s' "${EDITOR:-nano}"
}

shell() {
    # get the user's editor
    shell=$(basename "$SHELL")
    printf '%s' "${shell:-sh}"
	# get the user's editor
	shell=$(basename "$SHELL")
	printf '%s' "${shell:-sh}"
}

os() {
    # get the user's os
    printf '%s' "$(uname -or)"
	# get the user's os
	printf '%s' "$(uname -or)"
}

hostname() {
    # get the user's hostname
    printf '%s' "$(uname -n)"
	# get the user's hostname
	printf '%s' "$(uname -n)"
}

cwd() {
    # get the current working directory
    printf '%s' "${PWD:-$(pwd)}"
	# get the current working directory
	printf '%s' "${PWD:-$(pwd)}"
}

clear_line() {
    # clear the current line and place cursor at the beginning
    printf '\r\033[K' >&2
	# clear the current line and place cursor at the beginning
	printf '\r\033[K' >&2
}

spinner() {
    # display a spinner animation
    # $1 - message to display
    spinner_chars="⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏"
    while :
    do
        for char in $spinner_chars
        do
            clear_line
            printf '%s %s' "$char" "$1" >&2
            sleep 0.05
        done
    done
	# display a spinner animation
	# $1 - message to display
	spinner_chars="⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏"
	while :; do
		for char in $spinner_chars; do
			clear_line
			printf '%s %s' "$char" "$1" >&2
			sleep 0.05
		done
	done
}

mkprompt_shell() {
    prompt="Provide only commands or scripts for the shell \`$(shell)\`.\n\
	prompt="Provide only commands or scripts for the shell \`$(shell)\`.\n\
Do not give any explanations or descriptions.\n\
Provide the command exactly as it should be ran, so the user can copy and paste it.\n\
Ensure the output is a valid for the user's shell.\n\


@@ 66,159 64,159 @@ Provide the most logical solution if the prompt is unclear.\n\
Try to make a one-line solution, but it is not required.\n\
The user is using the operating system $(os).\n\
The user is currently in the directory \`$(cwd)\` on a host named \`$(hostname)\`.\n"
    if [ -n "$2" ]; then
        prompt="$prompt""The user piped this in from stdin: $2\n"
    fi
    prompt="$prompt""The user request is: $1"
    printf '%s' "$prompt"
	if [ -n "$2" ]; then
		prompt="$prompt""The user piped this in from stdin: $2\n"
	fi
	prompt="$prompt""The user request is: $1"
	printf '%s' "$prompt"
}

mkprompt_default() {
    prompt="You are a shell script called shatgpt, assisting the user with programming and system administration.\n\
	prompt="You are a shell script called shatgpt, assisting the user with programming and system administration.\n\
The user is using the operating system $(os).\n\
The user is currently in the directory \`$(cwd)\` on a host named \`$(hostname)\`.\n"
    if [ -n "$2" ]; then
        prompt="$prompt""The user piped this in from stdin: $2\n"
    fi
    prompt="$prompt""The user request is: $1"
    printf '%s' "$prompt"
	if [ -n "$2" ]; then
		prompt="$prompt""The user piped this in from stdin: $2\n"
	fi
	prompt="$prompt""The user request is: $1"
	printf '%s' "$prompt"
}

mkbody() {
    # make a body to send to the openai chat api
    # $1 - prompt
    printf '%s' "{\"model\": \"gpt-3.5-turbo\", \"stream\": true, \"messages\": [{\"role\": \"user\", \"content\": \""
    printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
    printf '%s' "\"}]}"
	# make a body to send to the openai chat api
	# $1 - prompt
	printf '%s' "{\"model\": \"gpt-3.5-turbo\", \"stream\": true, \"messages\": [{\"role\": \"user\", \"content\": \""
	printf '%s' "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
	printf '%s' "\"}]}"
}

get_field() {
    # get a field from a json string
    # $1 - json string
    # $2 - field to get
    case $1 in
      *"$2"*);;
      *) return;;
    esac

    tmp="${1#*\"$2\":\"}"
    while [ -n "$tmp" ]; do
      rest="${tmp#?}"
      c="${tmp%"$rest"}"
      tmp="$rest"
      case $c in
        '"')
          break
        ;;
        '\')
          printf '%s' "$c"
          rest="${tmp#?}"
          c="${tmp%"$rest"}"
          tmp="$rest"
        ;;
        *);;
      esac
      printf '%s' "$c"
    done
	# get a field from a json string
	# $1 - json string
	# $2 - field to get
	case $1 in
	*"$2"*) ;;
	*) return ;;
	esac

	tmp="${1#*\"$2\":\"}"
	while [ -n "$tmp" ]; do
		rest="${tmp#?}"
		c="${tmp%"$rest"}"
		tmp="$rest"
		case $c in
		'"')
			break
			;;
		'\')
			printf '%s' "$c"
			rest="${tmp#?}"
			c="${tmp%"$rest"}"
			tmp="$rest"
			;;
		*) ;;
		esac
		printf '%s' "$c"
	done
}

process_event() {
    # process each streaming event
    # $1 - event
    delta=$(get_field "$1" content)
    if [ -n "$delta" ]; then
	printf '%b' "$delta"
    fi
	# process each streaming event
	# $1 - event
	delta=$(get_field "$1" content)
	if [ -n "$delta" ]; then
		printf '%b' "$delta"
	fi
}

stream_events() {
    # do http request from a url
    # $1 - url
    # $2 - data
    spinner "Establishing connection..." &
    spinner_pid=$!

    if has curl; then
	curl "$1" -d "$2" -s -N -H "Content-Type: application/json" -H "Authorization: Bearer $OPENAI_API_KEY" | while IFS= read -r event; do
	    if kill $spinner_pid 2>/dev/null; then
		clear_line
	    fi
            process_event "$event"
        done
        printf '\n'
    else
        cleanup
        err "curl not found"
        exit 1
    fi
	# do http request from a url
	# $1 - url
	# $2 - data
	spinner "Establishing connection..." &
	spinner_pid=$!

	if has curl; then
		curl "$1" -d "$2" -s -N -H "Content-Type: application/json" -H "Authorization: Bearer $OPENAI_API_KEY" | while IFS= read -r event; do
			if kill $spinner_pid 2>/dev/null; then
				clear_line
			fi
			process_event "$event"
		done
		printf '\n'
	else
		cleanup
		err "curl not found"
		exit 1
	fi
}

cleanup() {
    kill $spinner_pid 2>/dev/null
    clear_line
	kill $spinner_pid 2>/dev/null
	clear_line
}

term() {
    cleanup
    exit 0
	cleanup
	exit 0
}

trap term INT TERM

show_help() {
  printf '%s\n' "usage: $(basename $0) [-hes] prompt"
  printf '%s\n' "options:"
  printf '%s\n' "  -h, --help     show this help message and exit."
  printf '%s\n' "  -e, --edit     open an editor to enter the prompt interactively."
  printf '%s\n' "  -s, --shell    output only shell commands to the terminal."
	printf '%s\n' "usage: $(basename $0) [-hes] prompt"
	printf '%s\n' "options:"
	printf '%s\n' "  -h, --help     show this help message and exit."
	printf '%s\n' "  -e, --edit     open an editor to enter the prompt interactively."
	printf '%s\n' "  -s, --shell    output only shell commands to the terminal."
}

parse_args() {
  while [ $# -gt 0 ]; do
    case $1 in
      -h|--help)
        show_help
        term
      ;;
      -e|--edit)
        editor_mode=true
        shift
      ;;
      -s|--shell)
        shell_mode=true
        shift
      ;;
      *)
        break
      ;;
    esac
  done

  prompt="$*"
	while [ $# -gt 0 ]; do
		case $1 in
		-h | --help)
			show_help
			term
			;;
		-e | --edit)
			editor_mode=true
			shift
			;;
		-s | --shell)
			shell_mode=true
			shift
			;;
		*)
			break
			;;
		esac
	done

	prompt="$*"
}

editor_mode=false
shell_mode=false

if [ -t 0 ]; then
  stdin=""
	stdin=""
else
  stdin="$(cat)"
	stdin="$(cat)"
fi

parse_args $*

if [ $editor_mode = true ]; then
  tmp=$(mktemp)
  printf '%s' "$prompt" > $tmp
  $(editor) "$tmp"
  prompt="$(cat $tmp)"
	tmp=$(mktemp)
	printf '%s' "$prompt" >$tmp
	$(editor) "$tmp"
	prompt="$(cat $tmp)"
fi

if [ $shell_mode = true ]; then
  prompt="$(mkprompt_shell "$prompt" "$stdin")"
	prompt="$(mkprompt_shell "$prompt" "$stdin")"
else
  prompt="$(mkprompt_default "$prompt" "$stdin")"
	prompt="$(mkprompt_default "$prompt" "$stdin")"
fi

request=$(mkbody "$prompt")