mirror of
https://github.com/cheikh2shift/godex.git
synced 2026-10-19 01:29:53 -05:00
324 lines
9.9 KiB
Bash
Executable File
324 lines
9.9 KiB
Bash
Executable File
#!/bin/sh
|
|
|
|
set -e
|
|
|
|
REPO="cheikh2shift/godex"
|
|
INSTALL_DIR="${INSTALL_DIR:-$HOME/.local/bin}"
|
|
BINARY_NAME="godex"
|
|
|
|
# Detect version from API
|
|
get_version() {
|
|
# Try API first, fall back to parsing HTML
|
|
curl -sL "https://api.github.com/repos/${REPO}/releases/latest" 2>/dev/null | grep '"tag_name"' | sed 's/.*"v\([^"]*\)".*/\1/'
|
|
}
|
|
|
|
# VERSION can be passed as arg, env var, or auto-detected
|
|
if [ -n "$1" ]; then
|
|
LATEST_TAG="$1"
|
|
elif [ -n "$VERSION" ]; then
|
|
LATEST_TAG="$VERSION"
|
|
else
|
|
LATEST_TAG=$(get_version)
|
|
if [ -z "$LATEST_TAG" ]; then
|
|
# Fallback: parse release page
|
|
LATEST_TAG=$(curl -sL "https://github.com/${REPO}/releases" 2>/dev/null | grep -o 'releases/tag/[^"]*' | head -1 | sed 's/.*tag\///')
|
|
fi
|
|
if [ -z "$LATEST_TAG" ]; then
|
|
echo "Failed to get latest release version"
|
|
exit 1
|
|
fi
|
|
fi
|
|
|
|
LATEST_TAG="${LATEST_TAG#v}"
|
|
|
|
echo "LATEST_TAG=$LATEST_TAG"
|
|
|
|
# Detect OS and architecture
|
|
OS="$(uname -s | tr '[:upper:]' '[:lower:]')"
|
|
ARCH="$(uname -m)"
|
|
echo "Detected OS=$OS ARCH=$ARCH"
|
|
|
|
case "$ARCH" in
|
|
x86_64)
|
|
ARCH="amd64"
|
|
;;
|
|
aarch64|arm64)
|
|
ARCH="arm64"
|
|
;;
|
|
*)
|
|
echo "Unsupported architecture: $ARCH"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# Map OS names
|
|
case "$OS" in
|
|
darwin)
|
|
OS="darwin"
|
|
;;
|
|
linux)
|
|
OS="linux"
|
|
;;
|
|
*)
|
|
echo "Unsupported OS: $OS"
|
|
exit 1
|
|
;;
|
|
esac
|
|
|
|
# If running in CI or explicit override, use the target
|
|
if [ -n "$TARGET_OS" ]; then
|
|
OS="$TARGET_OS"
|
|
fi
|
|
if [ -n "$TARGET_ARCH" ]; then
|
|
ARCH="$TARGET_ARCH"
|
|
fi
|
|
|
|
FILENAME="${BINARY_NAME}-${OS}-${ARCH}"
|
|
SHA_FILENAME="${FILENAME}.sha256"
|
|
if [ "$OS" = "windows" ]; then
|
|
FILENAME="${FILENAME}.exe"
|
|
SHA_FILENAME="${FILENAME}.sha256"
|
|
fi
|
|
|
|
|
|
URL="https://github.com/${REPO}/releases/download/v${LATEST_TAG}/${FILENAME}"
|
|
SHA_URL="https://github.com/${REPO}/releases/download/v${LATEST_TAG}/${SHA_FILENAME}"
|
|
|
|
echo "Using version v${LATEST_TAG}"
|
|
|
|
echo "Downloading ${FILENAME}..."
|
|
curl -sSL -o "${FILENAME}" "$URL"
|
|
|
|
echo "Downloading SHA256 checksum..."
|
|
curl -sSL -o "${SHA_FILENAME}" "$SHA_URL"
|
|
|
|
echo "Verifying checksum..."
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
|
sha256sum -c "${SHA_FILENAME}"
|
|
elif command -v shasum >/dev/null 2>&1; then
|
|
EXPECTED_HASH=$(cat "${SHA_FILENAME}" | cut -d' ' -f1)
|
|
ACTUAL_HASH=$(shasum -a 256 "${FILENAME}" | cut -d' ' -f1)
|
|
if [ "$EXPECTED_HASH" = "$ACTUAL_HASH" ]; then
|
|
echo "Checksum verified"
|
|
else
|
|
echo "Checksum mismatch!"
|
|
exit 1
|
|
fi
|
|
elif command -v openssl >/dev/null 2>&1; then
|
|
EXPECTED_HASH=$(cat "${SHA_FILENAME}" | cut -d' ' -f1)
|
|
ACTUAL_HASH=$(openssl dgst -sha256 "${FILENAME}" | sed 's/.* //')
|
|
if [ "$EXPECTED_HASH" = "$ACTUAL_HASH" ]; then
|
|
echo "Checksum verified"
|
|
else
|
|
echo "Checksum mismatch!"
|
|
exit 1
|
|
fi
|
|
else
|
|
echo "Warning: Neither sha256sum nor shasum nor openssl found. Skipping checksum verification."
|
|
echo "Consider installing coreutils (Linux) or using Homebrew (macOS) for security."
|
|
fi
|
|
|
|
# Clean up checksum file
|
|
rm -f "${SHA_FILENAME}"
|
|
|
|
# Rename to generic name for installation
|
|
mv "${FILENAME}" "${BINARY_NAME}"
|
|
chmod +x "${BINARY_NAME}"
|
|
|
|
# Create install directory if it doesn't exist
|
|
mkdir -p "$INSTALL_DIR"
|
|
|
|
# Move to install directory
|
|
if [ -w "$INSTALL_DIR" ]; then
|
|
mv "${BINARY_NAME}" "${INSTALL_DIR}/${BINARY_NAME}"
|
|
echo "Installed to ${INSTALL_DIR}/${BINARY_NAME}"
|
|
else
|
|
echo "Cannot write to ${INSTALL_DIR}, installed to current directory"
|
|
echo "Move it manually: mv ${BINARY_NAME} ${INSTALL_DIR}/"
|
|
fi
|
|
|
|
# Setup shell completion
|
|
GODEX_BASH_COMPLETION='#!/bin/bash
|
|
# shellcheck disable=SC2034
|
|
PROG="godex"
|
|
|
|
godex_get_config_path() {
|
|
local default_path="${HOME}/.godex/providers.yaml"
|
|
for ((i=1; i<cword; i++)); do
|
|
if [[ "${words[i]}" == "--config" ]] && [[ -n "${words[i+1]}" ]] && [[ "${words[i+1]}" != --* ]]; then
|
|
echo "${words[i+1]}"
|
|
return
|
|
fi
|
|
done
|
|
echo "$default_path"
|
|
}
|
|
|
|
godex_get_providers() {
|
|
local config_path=$(godex_get_config_path)
|
|
if [[ -f "$config_path" ]]; then
|
|
grep -E "^ - name:" "$config_path" | sed "s/.*- name://" | tr -d " "
|
|
fi
|
|
}
|
|
|
|
godex_get_default_provider() {
|
|
local config_path=$(godex_get_config_path)
|
|
if [[ -f "$config_path" ]]; then
|
|
grep -E "^ - name:" "$config_path" | head -1 | sed "s/.*- name://" | tr -d " "
|
|
fi
|
|
}
|
|
|
|
godex_get_models() {
|
|
local config_path=$(godex_get_config_path)
|
|
local provider_name="${1:-}"
|
|
local query="${2:-}"
|
|
if [[ -f "$config_path" ]]; then
|
|
if [[ -z "$provider_name" ]]; then
|
|
provider_name=$(godex_get_default_provider)
|
|
fi
|
|
godex --completion models "$config_path" "$provider_name" "$query"
|
|
fi
|
|
}
|
|
|
|
godex_get_mcp_servers() {
|
|
local config_path=$(godex_get_config_path)
|
|
local provider_name="${1:-}"
|
|
if [[ -z "$provider_name" ]]; then
|
|
provider_name=$(godex_get_default_provider)
|
|
fi
|
|
if [[ -f "$config_path" ]]; then
|
|
godex --completion mcp-servers "$config_path" "$provider_name"
|
|
fi
|
|
}
|
|
|
|
godex_get_flags_with_desc() {
|
|
echo "--config:provider configuration YAML"
|
|
echo "--provider:provider name to use"
|
|
echo "--model:override provider model"
|
|
echo "--hive:enable hive mode with a shared secret"
|
|
echo "--wizard:launch provider configuration wizard"
|
|
echo "--prompt:run a single prompt non-interactively"
|
|
echo "--auto-confirm:auto-run suggested commands"
|
|
echo "--version:print version information"
|
|
echo "--debug:enable debug mode"
|
|
echo "--completion:generate shell completion"
|
|
echo "--llama-server:external llama.cpp server URL"
|
|
echo "mcp:manage MCP servers (subcommand)"
|
|
}
|
|
|
|
_cgodex_completion() {
|
|
local cur prev words cword
|
|
_init_completion || return
|
|
|
|
if [[ "${words[1]}" == "mcp" ]]; then
|
|
if [[ $cword -eq 2 ]]; then
|
|
COMPREPLY=($(compgen -W "add remove" -- "${cur}"))
|
|
return 0
|
|
fi
|
|
case "$prev" in
|
|
--name)
|
|
local provider_name=""
|
|
local i
|
|
for ((i=1; i<cword; i++)); do
|
|
if [[ "${words[i]}" == "--provider" ]] && [[ -n "${words[i+1]}" ]] && [[ "${words[i+1]}" != --* ]]; then
|
|
provider_name="${words[i+1]}"
|
|
break
|
|
fi
|
|
done
|
|
if [[ "${words[2]}" == "remove" ]]; then
|
|
local servers=$(godex_get_mcp_servers "$provider_name")
|
|
COMPREPLY=($(compgen -W "$servers" -- "$cur"))
|
|
else
|
|
COMPREPLY=($(compgen -W "filesystem bash webscraper" -- "$cur"))
|
|
fi
|
|
return 0
|
|
;;
|
|
--provider)
|
|
local providers=$(godex_get_providers)
|
|
if [[ -n "$providers" ]]; then
|
|
COMPREPLY=($(compgen -W "$providers" -- "$cur"))
|
|
fi
|
|
return 0
|
|
;;
|
|
--config)
|
|
COMPREPLY=($(compgen -d -S/ -- "$cur"))
|
|
COMPREPLY+=($(compgen -f -X '!*.yaml' -X '!*.yml' -- "$cur"))
|
|
return 0
|
|
;;
|
|
--allowed-path)
|
|
COMPREPLY=($(compgen -d -S/ -- "$cur"))
|
|
return 0
|
|
;;
|
|
esac
|
|
if [[ "$cur" == -* ]]; then
|
|
local mcp_flags="--provider --name --command --args --env --transport --allowed-path --allowed-url"
|
|
COMPREPLY=($(compgen -W "$mcp_flags" -- "$cur"))
|
|
return 0
|
|
fi
|
|
fi
|
|
|
|
case "$prev" in
|
|
--provider)
|
|
local providers=$(godex_get_providers)
|
|
if [[ -n "$providers" ]]; then
|
|
COMPREPLY=($(compgen -W "$providers" -- "$cur"))
|
|
fi
|
|
return 0
|
|
;;
|
|
--config)
|
|
COMPREPLY=($(compgen -d -S/ -- "$cur"))
|
|
COMPREPLY+=($(compgen -f -X '!*.yaml' -X '!*.yml' -- "$cur"))
|
|
return 0
|
|
;;
|
|
--model)
|
|
local provider_name=""
|
|
local i
|
|
for ((i=1; i<cword; i++)); do
|
|
if [[ "${words[i]}" == "--provider" ]] && [[ -n "${words[i+1]}" ]] && [[ "${words[i+1]}" != --* ]]; then
|
|
provider_name="${words[i+1]}"
|
|
break
|
|
fi
|
|
done
|
|
if [[ -z "$provider_name" ]]; then
|
|
provider_name=$(godex_get_default_provider)
|
|
fi
|
|
while IFS= read -r model; do
|
|
COMPREPLY+=("$model")
|
|
done < <(godex --completion models "$(godex_get_config_path)" "$provider_name" "${cur}")
|
|
return 0
|
|
;;
|
|
esac
|
|
|
|
if [[ "$cur" == -* ]]; then
|
|
local flags=$(godex_get_flags_with_desc | cut -d: -f1)
|
|
COMPREPLY=($(compgen -W "$flags" -- "$cur"))
|
|
else
|
|
local providers=$(godex_get_providers)
|
|
if [[ -n "$providers" ]]; then
|
|
COMPREPLY=($(compgen -W "$providers mcp" -- "$cur"))
|
|
else
|
|
COMPREPLY=($(compgen -W "mcp" -- "$cur"))
|
|
fi
|
|
fi
|
|
}
|
|
|
|
complete -F _cgodex_completion godex
|
|
'
|
|
|
|
# Install bash completion
|
|
if [ -d "/etc/bash_completion.d" ] && [ -w "/etc/bash_completion.d" ]; then
|
|
echo "$GODEX_BASH_COMPLETION" > "/etc/bash_completion.d/godex"
|
|
echo "Bash completion installed to /etc/bash_completion.d/godex"
|
|
elif [ -n "$BASH_COMPLETION_USER_DIR" ] && [ -d "$BASH_COMPLETION_USER_DIR" ]; then
|
|
echo "$GODEX_BASH_COMPLETION" > "${BASH_COMPLETION_USER_DIR}/godex"
|
|
echo "Bash completion installed to ${BASH_COMPLETION_USER_DIR}/godex"
|
|
elif [ -d "$HOME/.bash_completion.d" ]; then
|
|
echo "$GODEX_BASH_COMPLETION" > "$HOME/.bash_completion.d/godex"
|
|
echo "Bash completion installed to $HOME/.bash_completion.d/godex"
|
|
else
|
|
mkdir -p "$HOME/.bash_completion.d"
|
|
echo "$GODEX_BASH_COMPLETION" > "$HOME/.bash_completion.d/godex"
|
|
echo "Bash completion installed to $HOME/.bash_completion.d/godex"
|
|
fi
|
|
|
|
echo "Installation complete!"
|