283 lines
6.1 KiB
Bash
283 lines
6.1 KiB
Bash
|
|
#!/usr/bin/env bash
|
||
|
|
set -euo pipefail
|
||
|
|
|
||
|
|
BINARY_NAME="email-mcp"
|
||
|
|
DEFAULT_PROFILE="default"
|
||
|
|
PROFILE_ENV="EMAIL_MCP_PROFILE"
|
||
|
|
RELEASE_BASE_URL="https://gitea.lclr.dev"
|
||
|
|
RELEASE_REPOSITORY="AI/email-mcp"
|
||
|
|
|
||
|
|
prompt() {
|
||
|
|
local label="$1"
|
||
|
|
local default_value="$2"
|
||
|
|
local answer=""
|
||
|
|
|
||
|
|
if [ -n "$default_value" ]; then
|
||
|
|
printf "%s [%s]: " "$label" "$default_value"
|
||
|
|
else
|
||
|
|
printf "%s: " "$label"
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ -r /dev/tty ]; then
|
||
|
|
IFS= read -r answer < /dev/tty || answer=""
|
||
|
|
else
|
||
|
|
IFS= read -r answer || answer=""
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ -z "$answer" ]; then
|
||
|
|
printf "%s" "$default_value"
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
|
||
|
|
printf "%s" "$answer"
|
||
|
|
}
|
||
|
|
|
||
|
|
detect_os() {
|
||
|
|
case "$(uname -s | tr '[:upper:]' '[:lower:]')" in
|
||
|
|
linux) printf "linux" ;;
|
||
|
|
darwin) printf "darwin" ;;
|
||
|
|
*)
|
||
|
|
printf "OS non supporté: %s\n" "$(uname -s)" >&2
|
||
|
|
exit 1
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
}
|
||
|
|
|
||
|
|
detect_arch() {
|
||
|
|
case "$(uname -m)" in
|
||
|
|
x86_64|amd64) printf "amd64" ;;
|
||
|
|
aarch64|arm64) printf "arm64" ;;
|
||
|
|
*)
|
||
|
|
printf "Architecture non supportée: %s\n" "$(uname -m)" >&2
|
||
|
|
exit 1
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
}
|
||
|
|
|
||
|
|
release_api_url() {
|
||
|
|
printf "%s/api/v1/repos/%s/releases/latest\n" "${RELEASE_BASE_URL%/}" "$RELEASE_REPOSITORY"
|
||
|
|
}
|
||
|
|
|
||
|
|
extract_asset_url() {
|
||
|
|
local json="$1"
|
||
|
|
local asset_name="$2"
|
||
|
|
|
||
|
|
printf "%s\n" "$json" \
|
||
|
|
| grep -o '"browser_download_url":"[^"]*"' \
|
||
|
|
| sed 's/"browser_download_url":"//;s/"$//' \
|
||
|
|
| sed 's#\\/#/#g' \
|
||
|
|
| grep "/${asset_name}$" \
|
||
|
|
| head -n 1
|
||
|
|
}
|
||
|
|
|
||
|
|
go_bin_dir() {
|
||
|
|
local gobin
|
||
|
|
gobin="$(go env GOBIN 2>/dev/null || true)"
|
||
|
|
if [ -n "$gobin" ]; then
|
||
|
|
printf "%s\n" "$gobin"
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
|
||
|
|
go env GOPATH 2>/dev/null | awk '{print $1 "/bin"}'
|
||
|
|
}
|
||
|
|
|
||
|
|
resolve_binary_path() {
|
||
|
|
if command -v "$BINARY_NAME" >/dev/null 2>&1; then
|
||
|
|
command -v "$BINARY_NAME"
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
|
||
|
|
if command -v go >/dev/null 2>&1; then
|
||
|
|
local bin_dir
|
||
|
|
bin_dir="$(go_bin_dir)"
|
||
|
|
if [ -n "$bin_dir" ] && [ -x "$bin_dir/$BINARY_NAME" ]; then
|
||
|
|
printf "%s\n" "$bin_dir/$BINARY_NAME"
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
printf "%s\n" "$HOME/.local/bin/$BINARY_NAME"
|
||
|
|
}
|
||
|
|
|
||
|
|
verify_checksum() {
|
||
|
|
local binary_path="$1"
|
||
|
|
local checksum_path="$2"
|
||
|
|
|
||
|
|
if command -v sha256sum >/dev/null 2>&1; then
|
||
|
|
( cd "$(dirname "$binary_path")" && sha256sum -c "$(basename "$checksum_path")" )
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
|
||
|
|
if command -v shasum >/dev/null 2>&1; then
|
||
|
|
local expected
|
||
|
|
local actual
|
||
|
|
expected="$(awk '{print $1}' "$checksum_path")"
|
||
|
|
actual="$(shasum -a 256 "$binary_path" | awk '{print $1}')"
|
||
|
|
if [ "$expected" != "$actual" ]; then
|
||
|
|
printf "Checksum invalide pour %s\n" "$binary_path" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
|
||
|
|
printf "Aucun utilitaire de checksum trouvé (sha256sum/shasum), vérification ignorée.\n" >&2
|
||
|
|
}
|
||
|
|
|
||
|
|
download_latest_release_binary() {
|
||
|
|
if ! command -v curl >/dev/null 2>&1; then
|
||
|
|
printf "curl est requis pour télécharger la release.\n" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
local os_name
|
||
|
|
local arch_name
|
||
|
|
local ext=""
|
||
|
|
os_name="$(detect_os)"
|
||
|
|
arch_name="$(detect_arch)"
|
||
|
|
if [ "$os_name" = "windows" ]; then
|
||
|
|
ext=".exe"
|
||
|
|
fi
|
||
|
|
|
||
|
|
local asset_name
|
||
|
|
local checksum_name
|
||
|
|
asset_name="${BINARY_NAME}-${os_name}-${arch_name}${ext}"
|
||
|
|
checksum_name="${asset_name}.sha256"
|
||
|
|
|
||
|
|
printf "Récupération de la dernière release...\n"
|
||
|
|
local release_json
|
||
|
|
release_json="$(curl -fsSL "$(release_api_url)")"
|
||
|
|
|
||
|
|
local asset_url
|
||
|
|
asset_url="$(extract_asset_url "$release_json" "$asset_name")"
|
||
|
|
if [ -z "$asset_url" ]; then
|
||
|
|
printf "Asset introuvable dans la dernière release: %s\n" "$asset_name" >&2
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
|
||
|
|
local checksum_url
|
||
|
|
checksum_url="$(extract_asset_url "$release_json" "$checksum_name" || true)"
|
||
|
|
|
||
|
|
local tmp_dir
|
||
|
|
tmp_dir="$(mktemp -d)"
|
||
|
|
trap 'rm -rf "$tmp_dir"' EXIT INT TERM
|
||
|
|
|
||
|
|
curl -fsSL "$asset_url" -o "$tmp_dir/$asset_name"
|
||
|
|
chmod +x "$tmp_dir/$asset_name"
|
||
|
|
|
||
|
|
if [ -n "$checksum_url" ]; then
|
||
|
|
curl -fsSL "$checksum_url" -o "$tmp_dir/$checksum_name"
|
||
|
|
verify_checksum "$tmp_dir/$asset_name" "$tmp_dir/$checksum_name"
|
||
|
|
fi
|
||
|
|
|
||
|
|
local target_dir
|
||
|
|
target_dir="$(prompt "Répertoire d'installation" "$HOME/.local/bin")"
|
||
|
|
mkdir -p "$target_dir"
|
||
|
|
install -m 0755 "$tmp_dir/$asset_name" "$target_dir/$BINARY_NAME"
|
||
|
|
|
||
|
|
printf "Binaire installé: %s/%s\n" "$target_dir" "$BINARY_NAME"
|
||
|
|
printf "Ajoute ce dossier au PATH si nécessaire.\n"
|
||
|
|
}
|
||
|
|
|
||
|
|
install_binary() {
|
||
|
|
if command -v "$BINARY_NAME" >/dev/null 2>&1; then
|
||
|
|
printf "Binaire détecté: %s\n" "$(command -v "$BINARY_NAME")"
|
||
|
|
local reinstall
|
||
|
|
reinstall="$(prompt "Forcer une réinstallation depuis la dernière release ? (y/N)" "N")"
|
||
|
|
case "$reinstall" in
|
||
|
|
y|Y|yes|YES)
|
||
|
|
download_latest_release_binary
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
return
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
return
|
||
|
|
fi
|
||
|
|
|
||
|
|
download_latest_release_binary
|
||
|
|
}
|
||
|
|
|
||
|
|
run_setup_wizard() {
|
||
|
|
install_binary
|
||
|
|
|
||
|
|
local profile
|
||
|
|
profile="$(prompt "Profil à configurer (${PROFILE_ENV})" "$DEFAULT_PROFILE")"
|
||
|
|
local binary_path
|
||
|
|
binary_path="$(resolve_binary_path)"
|
||
|
|
|
||
|
|
printf "Lancement de %s setup...\n\n" "$BINARY_NAME"
|
||
|
|
if [ -r /dev/tty ] && [ -w /dev/tty ]; then
|
||
|
|
env "${PROFILE_ENV}=${profile}" "$binary_path" setup < /dev/tty > /dev/tty
|
||
|
|
else
|
||
|
|
env "${PROFILE_ENV}=${profile}" "$binary_path" setup
|
||
|
|
fi
|
||
|
|
}
|
||
|
|
|
||
|
|
print_mcp_json() {
|
||
|
|
local profile
|
||
|
|
profile="$(prompt "Profil à exposer dans la config MCP (${PROFILE_ENV})" "$DEFAULT_PROFILE")"
|
||
|
|
local default_command
|
||
|
|
default_command="$(resolve_binary_path)"
|
||
|
|
local command_path
|
||
|
|
command_path="$(prompt "Commande du serveur MCP" "$default_command")"
|
||
|
|
|
||
|
|
cat <<JSON
|
||
|
|
{
|
||
|
|
"mcpServers": {
|
||
|
|
"${BINARY_NAME}": {
|
||
|
|
"command": "${command_path}",
|
||
|
|
"args": ["mcp"],
|
||
|
|
"env": {
|
||
|
|
"${PROFILE_ENV}": "${profile}"
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
JSON
|
||
|
|
}
|
||
|
|
|
||
|
|
print_header() {
|
||
|
|
cat <<TXT
|
||
|
|
===========================================================
|
||
|
|
Installateur MCP pour ${BINARY_NAME}
|
||
|
|
===========================================================
|
||
|
|
Choisis une action :
|
||
|
|
1) Installer le binaire + lancer le setup
|
||
|
|
2) Générer un JSON de config MCP (Codex)
|
||
|
|
3) Générer un JSON de config MCP (Claude Desktop)
|
||
|
|
4) Générer un JSON de config MCP (autre client)
|
||
|
|
5) Quitter
|
||
|
|
TXT
|
||
|
|
}
|
||
|
|
|
||
|
|
main() {
|
||
|
|
while true; do
|
||
|
|
print_header
|
||
|
|
local choice
|
||
|
|
choice="$(prompt "Choix" "1")"
|
||
|
|
printf "\n"
|
||
|
|
|
||
|
|
case "$choice" in
|
||
|
|
1)
|
||
|
|
run_setup_wizard
|
||
|
|
printf "\nInstallation terminée.\n"
|
||
|
|
return
|
||
|
|
;;
|
||
|
|
2|3|4)
|
||
|
|
printf "Copie ce JSON dans la config MCP du client ciblé.\n\n"
|
||
|
|
print_mcp_json
|
||
|
|
return
|
||
|
|
;;
|
||
|
|
5)
|
||
|
|
printf "Annulé.\n"
|
||
|
|
return
|
||
|
|
;;
|
||
|
|
*)
|
||
|
|
printf "Choix invalide: %s\n\n" "$choice" >&2
|
||
|
|
;;
|
||
|
|
esac
|
||
|
|
done
|
||
|
|
}
|
||
|
|
|
||
|
|
main "$@"
|