2026-04-15 08:19:42 +00:00
|
|
|
|
#!/usr/bin/env bash
|
|
|
|
|
|
set -euo pipefail
|
|
|
|
|
|
|
|
|
|
|
|
BINARY_NAME="email-mcp"
|
|
|
|
|
|
DEFAULT_PROFILE="default"
|
|
|
|
|
|
PROFILE_ENV="EMAIL_MCP_PROFILE"
|
2026-05-11 10:21:55 +00:00
|
|
|
|
RELEASE_BASE_URL="https://forge.lclr.dev"
|
2026-04-15 08:38:58 +00:00
|
|
|
|
RELEASE_REPOSITORY="AI/email-mcp"
|
|
|
|
|
|
INSTALLED_BINARY_PATH=""
|
2026-04-15 08:59:23 +00:00
|
|
|
|
PREFILL_SERVER_NAME=""
|
|
|
|
|
|
PREFILL_PROFILE_VALUE=""
|
|
|
|
|
|
PREFILL_COMMAND_PATH=""
|
2026-04-15 08:35:41 +00:00
|
|
|
|
|
|
|
|
|
|
if [ -t 2 ] && [ -z "${NO_COLOR:-}" ]; then
|
|
|
|
|
|
C_RESET="$(printf '\033[0m')"
|
|
|
|
|
|
C_BOLD="$(printf '\033[1m')"
|
|
|
|
|
|
C_DIM="$(printf '\033[2m')"
|
|
|
|
|
|
C_RED="$(printf '\033[31m')"
|
|
|
|
|
|
C_GREEN="$(printf '\033[32m')"
|
|
|
|
|
|
C_YELLOW="$(printf '\033[33m')"
|
|
|
|
|
|
C_BLUE="$(printf '\033[34m')"
|
|
|
|
|
|
C_MAGENTA="$(printf '\033[35m')"
|
|
|
|
|
|
C_CYAN="$(printf '\033[36m')"
|
|
|
|
|
|
else
|
|
|
|
|
|
C_RESET=""
|
|
|
|
|
|
C_BOLD=""
|
|
|
|
|
|
C_DIM=""
|
|
|
|
|
|
C_RED=""
|
|
|
|
|
|
C_GREEN=""
|
|
|
|
|
|
C_YELLOW=""
|
|
|
|
|
|
C_BLUE=""
|
|
|
|
|
|
C_MAGENTA=""
|
|
|
|
|
|
C_CYAN=""
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
ui_line() {
|
|
|
|
|
|
printf "%b%s%b\n" "$C_DIM" "------------------------------------------------------------" "$C_RESET" >&2
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ui_title() {
|
|
|
|
|
|
printf "\n%b%s%b\n" "$C_BOLD$C_CYAN" "$1" "$C_RESET" >&2
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ui_info() {
|
|
|
|
|
|
printf "%b[info]%b %s\n" "$C_BLUE" "$C_RESET" "$1" >&2
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ui_success() {
|
|
|
|
|
|
printf "%b[ok]%b %s\n" "$C_GREEN" "$C_RESET" "$1" >&2
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ui_warn() {
|
|
|
|
|
|
printf "%b[warn]%b %s\n" "$C_YELLOW" "$C_RESET" "$1" >&2
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ui_error() {
|
|
|
|
|
|
printf "%b[error]%b %s\n" "$C_RED" "$C_RESET" "$1" >&2
|
|
|
|
|
|
}
|
2026-04-15 08:19:42 +00:00
|
|
|
|
|
|
|
|
|
|
prompt() {
|
|
|
|
|
|
local label="$1"
|
|
|
|
|
|
local default_value="$2"
|
|
|
|
|
|
local answer=""
|
|
|
|
|
|
|
|
|
|
|
|
if [ -n "$default_value" ]; then
|
2026-04-15 08:35:41 +00:00
|
|
|
|
printf "%b%s%b [%s]: " "$C_BOLD" "$label" "$C_RESET" "$default_value" >&2
|
2026-04-15 08:19:42 +00:00
|
|
|
|
else
|
2026-04-15 08:35:41 +00:00
|
|
|
|
printf "%b%s%b: " "$C_BOLD" "$label" "$C_RESET" >&2
|
2026-04-15 08:19:42 +00:00
|
|
|
|
fi
|
|
|
|
|
|
|
2026-04-15 08:35:41 +00:00
|
|
|
|
if [ -t 2 ] && [ -r /dev/tty ]; then
|
|
|
|
|
|
if ! IFS= read -r answer < /dev/tty 2>/dev/null; then
|
|
|
|
|
|
IFS= read -r answer || answer=""
|
|
|
|
|
|
fi
|
2026-04-15 08:19:42 +00:00
|
|
|
|
else
|
|
|
|
|
|
IFS= read -r answer || answer=""
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if [ -z "$answer" ]; then
|
|
|
|
|
|
printf "%s" "$default_value"
|
|
|
|
|
|
return
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
printf "%s" "$answer"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 08:43:00 +00:00
|
|
|
|
tty_prompt_available() {
|
|
|
|
|
|
[ -t 2 ] && [ -r /dev/tty ] && [ -w /dev/tty ]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
menu_select() {
|
|
|
|
|
|
local title="$1"
|
|
|
|
|
|
shift
|
|
|
|
|
|
local options=("$@")
|
|
|
|
|
|
local count="${#options[@]}"
|
|
|
|
|
|
local index=0
|
|
|
|
|
|
local key=""
|
|
|
|
|
|
local i=0
|
|
|
|
|
|
local rows=$((count + 3))
|
|
|
|
|
|
local rendered=0
|
|
|
|
|
|
|
|
|
|
|
|
if [ "$count" -eq 0 ]; then
|
|
|
|
|
|
return 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
if ! tty_prompt_available; then
|
|
|
|
|
|
ui_title "$title"
|
|
|
|
|
|
i=0
|
|
|
|
|
|
while [ "$i" -lt "$count" ]; do
|
|
|
|
|
|
printf " %d) %s\n" "$((i + 1))" "${options[$i]}" >&2
|
|
|
|
|
|
i=$((i + 1))
|
|
|
|
|
|
done
|
|
|
|
|
|
|
|
|
|
|
|
while true; do
|
|
|
|
|
|
local raw_choice
|
|
|
|
|
|
raw_choice="$(prompt "Choix" "1")"
|
|
|
|
|
|
case "$raw_choice" in
|
|
|
|
|
|
''|*[!0-9]*)
|
|
|
|
|
|
ui_warn "Choix invalide: $raw_choice"
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
if [ "$raw_choice" -ge 1 ] && [ "$raw_choice" -le "$count" ]; then
|
|
|
|
|
|
printf "%s" "${options[$((raw_choice - 1))]}"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
fi
|
|
|
|
|
|
ui_warn "Choix invalide: $raw_choice"
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
done
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
while true; do
|
|
|
|
|
|
if [ "$rendered" -eq 1 ]; then
|
|
|
|
|
|
printf "\033[%dA\033[J" "$rows" >&2 2>/dev/null || true
|
|
|
|
|
|
fi
|
|
|
|
|
|
ui_title "$title"
|
|
|
|
|
|
i=0
|
|
|
|
|
|
while [ "$i" -lt "$count" ]; do
|
|
|
|
|
|
if [ "$i" -eq "$index" ]; then
|
|
|
|
|
|
printf " %b› %s%b\n" "$C_BOLD$C_CYAN" "${options[$i]}" "$C_RESET" >&2
|
|
|
|
|
|
else
|
|
|
|
|
|
printf " %s\n" "${options[$i]}" >&2
|
|
|
|
|
|
fi
|
|
|
|
|
|
i=$((i + 1))
|
|
|
|
|
|
done
|
|
|
|
|
|
printf "%bUtilise ↑/↓ puis Entrée.%b\n" "$C_DIM" "$C_RESET" >&2
|
|
|
|
|
|
rendered=1
|
|
|
|
|
|
|
|
|
|
|
|
if ! IFS= read -rsn1 key < /dev/tty; then
|
|
|
|
|
|
continue
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
case "$key" in
|
|
|
|
|
|
"")
|
|
|
|
|
|
printf "%s" "${options[$index]}"
|
|
|
|
|
|
return 0
|
|
|
|
|
|
;;
|
|
|
|
|
|
$'\x1b')
|
|
|
|
|
|
if IFS= read -rsn2 key < /dev/tty; then
|
|
|
|
|
|
case "$key" in
|
|
|
|
|
|
"[A")
|
|
|
|
|
|
if [ "$index" -eq 0 ]; then
|
|
|
|
|
|
index=$((count - 1))
|
|
|
|
|
|
else
|
|
|
|
|
|
index=$((index - 1))
|
|
|
|
|
|
fi
|
|
|
|
|
|
;;
|
|
|
|
|
|
"[B")
|
|
|
|
|
|
if [ "$index" -eq $((count - 1)) ]; then
|
|
|
|
|
|
index=0
|
|
|
|
|
|
else
|
|
|
|
|
|
index=$((index + 1))
|
|
|
|
|
|
fi
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
fi
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
done
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 08:35:41 +00:00
|
|
|
|
sanitize_server_name() {
|
|
|
|
|
|
local raw="$1"
|
|
|
|
|
|
local sanitized
|
|
|
|
|
|
sanitized="$(printf "%s" "$raw" | tr '[:upper:]' '[:lower:]' | sed 's/[^a-z0-9_-]/-/g; s/--*/-/g; s/^-*//; s/-*$//')"
|
|
|
|
|
|
if [ -z "$sanitized" ]; then
|
|
|
|
|
|
sanitized="$BINARY_NAME"
|
|
|
|
|
|
fi
|
|
|
|
|
|
printf "%s" "$sanitized"
|
2026-04-15 08:19:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 08:35:41 +00:00
|
|
|
|
toml_escape() {
|
|
|
|
|
|
printf "%s" "$1" | sed 's/\\/\\\\/g; s/"/\\"/g'
|
2026-04-15 08:19:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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() {
|
2026-04-15 08:38:58 +00:00
|
|
|
|
if [ -n "$INSTALLED_BINARY_PATH" ] && [ -x "$INSTALLED_BINARY_PATH" ]; then
|
|
|
|
|
|
printf "%s\n" "$INSTALLED_BINARY_PATH"
|
|
|
|
|
|
return
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-04-15 08:19:42 +00:00
|
|
|
|
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"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 08:35:41 +00:00
|
|
|
|
ensure_cli() {
|
|
|
|
|
|
local cli_name="$1"
|
|
|
|
|
|
if command -v "$cli_name" >/dev/null 2>&1; then
|
2026-04-15 08:19:42 +00:00
|
|
|
|
return
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-04-15 08:35:41 +00:00
|
|
|
|
ui_error "Commande introuvable: $cli_name"
|
|
|
|
|
|
exit 1
|
2026-04-15 08:19:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 08:38:58 +00:00
|
|
|
|
detect_os() {
|
|
|
|
|
|
case "$(uname -s | tr '[:upper:]' '[:lower:]')" in
|
|
|
|
|
|
linux) printf "linux" ;;
|
|
|
|
|
|
darwin) printf "darwin" ;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
ui_error "OS non supporte: $(uname -s)"
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
detect_arch() {
|
|
|
|
|
|
case "$(uname -m)" in
|
|
|
|
|
|
x86_64|amd64) printf "amd64" ;;
|
|
|
|
|
|
aarch64|arm64) printf "arm64" ;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
ui_error "Architecture non supportee: $(uname -m)"
|
|
|
|
|
|
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
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
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
|
|
|
|
|
|
ui_error "Checksum invalide pour $binary_path"
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
return
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
ui_warn "Aucun utilitaire checksum trouve (sha256sum/shasum), verification ignoree."
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
download_latest_release_binary() {
|
|
|
|
|
|
if ! command -v curl >/dev/null 2>&1; then
|
|
|
|
|
|
ui_error "curl est requis pour telecharger la release."
|
|
|
|
|
|
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"
|
|
|
|
|
|
|
|
|
|
|
|
ui_info "Recuperation de la derniere release..."
|
|
|
|
|
|
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
|
|
|
|
|
|
ui_error "Asset introuvable dans la derniere release: $asset_name"
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
local checksum_url
|
|
|
|
|
|
checksum_url="$(extract_asset_url "$release_json" "$checksum_name" || true)"
|
|
|
|
|
|
|
|
|
|
|
|
local tmp_dir
|
|
|
|
|
|
tmp_dir="$(mktemp -d)"
|
2026-04-15 08:47:34 +00:00
|
|
|
|
trap 'if [ -n "${tmp_dir:-}" ] && [ -d "${tmp_dir:-}" ]; then rm -rf "${tmp_dir}"; fi' EXIT INT TERM
|
2026-04-15 08:38:58 +00:00
|
|
|
|
|
|
|
|
|
|
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 "Repertoire d'installation" "$HOME/.local/bin")"
|
|
|
|
|
|
mkdir -p "$target_dir"
|
|
|
|
|
|
install -m 0755 "$tmp_dir/$asset_name" "$target_dir/$BINARY_NAME"
|
|
|
|
|
|
INSTALLED_BINARY_PATH="$target_dir/$BINARY_NAME"
|
|
|
|
|
|
|
|
|
|
|
|
ui_success "Binaire installe: $INSTALLED_BINARY_PATH"
|
|
|
|
|
|
ui_info "Ajoute ce dossier au PATH si necessaire."
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 08:19:42 +00:00
|
|
|
|
install_binary() {
|
|
|
|
|
|
if command -v "$BINARY_NAME" >/dev/null 2>&1; then
|
2026-04-15 08:38:58 +00:00
|
|
|
|
INSTALLED_BINARY_PATH="$(command -v "$BINARY_NAME")"
|
|
|
|
|
|
ui_success "Binaire detecte: $INSTALLED_BINARY_PATH"
|
2026-04-15 08:19:42 +00:00
|
|
|
|
local reinstall
|
2026-04-15 08:38:58 +00:00
|
|
|
|
reinstall="$(prompt "Forcer une reinstall depuis la derniere release ? (y/N)" "N")"
|
2026-04-15 08:19:42 +00:00
|
|
|
|
case "$reinstall" in
|
|
|
|
|
|
y|Y|yes|YES)
|
2026-04-15 08:38:58 +00:00
|
|
|
|
download_latest_release_binary
|
|
|
|
|
|
return
|
2026-04-15 08:19:42 +00:00
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
return
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-04-15 08:38:58 +00:00
|
|
|
|
download_latest_release_binary
|
2026-04-15 08:19:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
run_setup_wizard() {
|
|
|
|
|
|
install_binary
|
|
|
|
|
|
|
|
|
|
|
|
local profile
|
2026-04-15 08:35:41 +00:00
|
|
|
|
profile="$(prompt "Profil a configurer (${PROFILE_ENV})" "$DEFAULT_PROFILE")"
|
2026-04-15 08:19:42 +00:00
|
|
|
|
local binary_path
|
|
|
|
|
|
binary_path="$(resolve_binary_path)"
|
|
|
|
|
|
|
2026-04-15 08:38:58 +00:00
|
|
|
|
if [ ! -x "$binary_path" ]; then
|
|
|
|
|
|
ui_error "Binaire introuvable/executable: $binary_path"
|
|
|
|
|
|
exit 1
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-05-11 10:33:43 +00:00
|
|
|
|
ui_info "Déverrouillage Bitwarden avant le setup..."
|
|
|
|
|
|
if [ -t 2 ] && [ -r /dev/tty ] && [ -w /dev/tty ]; then
|
|
|
|
|
|
env "${PROFILE_ENV}=${profile}" "$binary_path" login < /dev/tty > /dev/tty
|
|
|
|
|
|
else
|
|
|
|
|
|
env "${PROFILE_ENV}=${profile}" "$binary_path" login
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
2026-04-15 08:35:41 +00:00
|
|
|
|
ui_info "Lancement de $BINARY_NAME setup"
|
2026-04-15 08:59:23 +00:00
|
|
|
|
if [ -t 2 ] && [ -r /dev/tty ] && [ -w /dev/tty ]; then
|
2026-04-15 08:19:42 +00:00
|
|
|
|
env "${PROFILE_ENV}=${profile}" "$binary_path" setup < /dev/tty > /dev/tty
|
|
|
|
|
|
else
|
|
|
|
|
|
env "${PROFILE_ENV}=${profile}" "$binary_path" setup
|
|
|
|
|
|
fi
|
2026-04-15 08:35:41 +00:00
|
|
|
|
ui_success "Setup termine pour le profil \"$profile\"."
|
2026-04-15 08:59:23 +00:00
|
|
|
|
|
|
|
|
|
|
PREFILL_SERVER_NAME="$(sanitize_server_name "$BINARY_NAME")"
|
|
|
|
|
|
PREFILL_PROFILE_VALUE="$profile"
|
|
|
|
|
|
PREFILL_COMMAND_PATH="$binary_path"
|
2026-04-15 08:19:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 08:35:41 +00:00
|
|
|
|
collect_server_inputs() {
|
|
|
|
|
|
local default_name
|
2026-04-15 08:59:23 +00:00
|
|
|
|
default_name="$(sanitize_server_name "${PREFILL_SERVER_NAME:-$BINARY_NAME}")"
|
2026-04-15 08:35:41 +00:00
|
|
|
|
SERVER_NAME="$(prompt "Nom du serveur MCP" "$default_name")"
|
|
|
|
|
|
SERVER_NAME="$(sanitize_server_name "$SERVER_NAME")"
|
|
|
|
|
|
|
2026-04-15 08:59:23 +00:00
|
|
|
|
PROFILE_VALUE="$(prompt "Valeur de ${PROFILE_ENV}" "${PREFILL_PROFILE_VALUE:-$DEFAULT_PROFILE}")"
|
2026-04-15 08:35:41 +00:00
|
|
|
|
|
2026-04-15 08:19:42 +00:00
|
|
|
|
local default_command
|
2026-04-15 08:59:23 +00:00
|
|
|
|
if [ -n "${PREFILL_COMMAND_PATH:-}" ]; then
|
|
|
|
|
|
default_command="$PREFILL_COMMAND_PATH"
|
|
|
|
|
|
else
|
|
|
|
|
|
default_command="$(resolve_binary_path)"
|
|
|
|
|
|
fi
|
2026-04-15 08:35:41 +00:00
|
|
|
|
COMMAND_PATH="$(prompt "Chemin du binaire serveur MCP" "$default_command")"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
choose_scope() {
|
|
|
|
|
|
local selected
|
2026-04-15 08:43:00 +00:00
|
|
|
|
selected="$(menu_select "Scope de configuration" "global (user)" "project (projet courant)")"
|
|
|
|
|
|
case "$selected" in
|
|
|
|
|
|
"global (user)")
|
|
|
|
|
|
printf "global"
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
printf "project"
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
2026-04-15 08:35:41 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
apply_claude_mcp() {
|
|
|
|
|
|
ensure_cli "claude"
|
|
|
|
|
|
collect_server_inputs
|
|
|
|
|
|
|
|
|
|
|
|
local scope_choice
|
|
|
|
|
|
scope_choice="$(choose_scope)"
|
|
|
|
|
|
local claude_scope
|
|
|
|
|
|
if [ "$scope_choice" = "global" ]; then
|
|
|
|
|
|
claude_scope="user"
|
|
|
|
|
|
else
|
|
|
|
|
|
claude_scope="project"
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
ui_info "Application de la configuration Claude ($claude_scope)..."
|
|
|
|
|
|
claude mcp remove --scope "$claude_scope" "$SERVER_NAME" >/dev/null 2>&1 || true
|
|
|
|
|
|
claude mcp add \
|
|
|
|
|
|
--transport stdio \
|
|
|
|
|
|
--scope "$claude_scope" \
|
2026-04-15 08:45:28 +00:00
|
|
|
|
"$SERVER_NAME" \
|
|
|
|
|
|
--env "${PROFILE_ENV}=${PROFILE_VALUE}" \
|
|
|
|
|
|
-- "$COMMAND_PATH" mcp
|
2026-04-15 08:35:41 +00:00
|
|
|
|
|
|
|
|
|
|
ui_success "Serveur \"$SERVER_NAME\" configure dans Claude ($claude_scope)."
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
rewrite_codex_project_config() {
|
|
|
|
|
|
local project_dir="$1"
|
|
|
|
|
|
local config_file="$project_dir/.codex/config.toml"
|
|
|
|
|
|
local section_prefix="[mcp_servers.${SERVER_NAME}"
|
|
|
|
|
|
|
|
|
|
|
|
mkdir -p "$project_dir/.codex"
|
|
|
|
|
|
touch "$config_file"
|
|
|
|
|
|
|
|
|
|
|
|
local tmp_file
|
|
|
|
|
|
tmp_file="$(mktemp)"
|
|
|
|
|
|
|
|
|
|
|
|
awk -v prefix="$section_prefix" '
|
|
|
|
|
|
function is_target(line, p, next_char) {
|
|
|
|
|
|
if (index(line, p) != 1) {
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
|
|
|
|
|
next_char = substr(line, length(p) + 1, 1)
|
|
|
|
|
|
return next_char == "]" || next_char == "."
|
|
|
|
|
|
}
|
|
|
|
|
|
/^\[.*\]$/ {
|
|
|
|
|
|
if (is_target($0, prefix)) {
|
|
|
|
|
|
skip = 1
|
|
|
|
|
|
next
|
|
|
|
|
|
}
|
|
|
|
|
|
if (skip == 1) {
|
|
|
|
|
|
skip = 0
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
{
|
|
|
|
|
|
if (skip != 1) {
|
|
|
|
|
|
print $0
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
' "$config_file" > "$tmp_file"
|
|
|
|
|
|
|
|
|
|
|
|
mv "$tmp_file" "$config_file"
|
|
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
|
printf "\n[mcp_servers.%s]\n" "$SERVER_NAME"
|
|
|
|
|
|
printf "command = \"%s\"\n" "$(toml_escape "$COMMAND_PATH")"
|
|
|
|
|
|
printf "args = [\"mcp\"]\n\n"
|
|
|
|
|
|
printf "[mcp_servers.%s.env]\n" "$SERVER_NAME"
|
|
|
|
|
|
printf "%s = \"%s\"\n" "$PROFILE_ENV" "$(toml_escape "$PROFILE_VALUE")"
|
|
|
|
|
|
} >> "$config_file"
|
|
|
|
|
|
}
|
2026-04-15 08:19:42 +00:00
|
|
|
|
|
2026-04-15 08:35:41 +00:00
|
|
|
|
apply_codex_mcp() {
|
|
|
|
|
|
ensure_cli "codex"
|
|
|
|
|
|
collect_server_inputs
|
|
|
|
|
|
|
|
|
|
|
|
local scope_choice
|
|
|
|
|
|
scope_choice="$(choose_scope)"
|
|
|
|
|
|
|
|
|
|
|
|
if [ "$scope_choice" = "global" ]; then
|
|
|
|
|
|
ui_info "Application via codex mcp add (scope global)..."
|
|
|
|
|
|
codex mcp remove "$SERVER_NAME" >/dev/null 2>&1 || true
|
|
|
|
|
|
codex mcp add \
|
|
|
|
|
|
"$SERVER_NAME" \
|
|
|
|
|
|
--env "${PROFILE_ENV}=${PROFILE_VALUE}" \
|
|
|
|
|
|
-- "$COMMAND_PATH" mcp
|
|
|
|
|
|
ui_success "Serveur \"$SERVER_NAME\" configure dans le scope global Codex."
|
|
|
|
|
|
return
|
|
|
|
|
|
fi
|
|
|
|
|
|
|
|
|
|
|
|
local default_project_dir
|
|
|
|
|
|
default_project_dir="$(pwd)"
|
|
|
|
|
|
local project_dir
|
|
|
|
|
|
project_dir="$(prompt "Dossier projet cible pour .codex/config.toml" "$default_project_dir")"
|
|
|
|
|
|
rewrite_codex_project_config "$project_dir"
|
|
|
|
|
|
ui_success "Configuration projet ecrite dans $project_dir/.codex/config.toml"
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
print_mcp_json() {
|
|
|
|
|
|
collect_server_inputs
|
2026-04-15 08:19:42 +00:00
|
|
|
|
cat <<JSON
|
|
|
|
|
|
{
|
|
|
|
|
|
"mcpServers": {
|
2026-04-15 08:35:41 +00:00
|
|
|
|
"${SERVER_NAME}": {
|
|
|
|
|
|
"command": "${COMMAND_PATH}",
|
2026-04-15 08:19:42 +00:00
|
|
|
|
"args": ["mcp"],
|
|
|
|
|
|
"env": {
|
2026-04-15 08:35:41 +00:00
|
|
|
|
"${PROFILE_ENV}": "${PROFILE_VALUE}"
|
2026-04-15 08:19:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
JSON
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
print_header() {
|
2026-04-15 08:35:41 +00:00
|
|
|
|
ui_line
|
|
|
|
|
|
printf "%bMCP Install Wizard%b for %b%s%b\n" "$C_BOLD$C_MAGENTA" "$C_RESET" "$C_BOLD" "$BINARY_NAME" "$C_RESET" >&2
|
2026-04-15 08:38:58 +00:00
|
|
|
|
printf "%bRelease repo:%b %s\n" "$C_DIM" "$C_RESET" "$RELEASE_REPOSITORY" >&2
|
2026-04-15 08:35:41 +00:00
|
|
|
|
ui_line
|
2026-04-15 08:43:00 +00:00
|
|
|
|
printf "%bSelectionne une action dans le menu interactif.%b\n" "$C_DIM" "$C_RESET" >&2
|
2026-04-15 08:19:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 08:59:23 +00:00
|
|
|
|
post_setup_configure_mcp() {
|
|
|
|
|
|
ui_title "Configuration MCP apres setup"
|
|
|
|
|
|
local next_action
|
|
|
|
|
|
next_action="$(menu_select \
|
|
|
|
|
|
"Configurer le MCP maintenant ?" \
|
|
|
|
|
|
"Configurer Claude Code (apply direct)" \
|
|
|
|
|
|
"Configurer Codex (apply direct)" \
|
|
|
|
|
|
"Generer JSON MCP manuel" \
|
|
|
|
|
|
"Terminer sans config MCP")"
|
|
|
|
|
|
printf "\n" >&2
|
|
|
|
|
|
|
|
|
|
|
|
case "$next_action" in
|
|
|
|
|
|
"Configurer Claude Code (apply direct)")
|
|
|
|
|
|
apply_claude_mcp
|
|
|
|
|
|
;;
|
|
|
|
|
|
"Configurer Codex (apply direct)")
|
|
|
|
|
|
apply_codex_mcp
|
|
|
|
|
|
;;
|
|
|
|
|
|
"Generer JSON MCP manuel")
|
|
|
|
|
|
ui_info "JSON MCP genere sur stdout."
|
|
|
|
|
|
print_mcp_json
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
ui_info "Setup termine sans configuration MCP additionnelle."
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-04-15 08:19:42 +00:00
|
|
|
|
main() {
|
2026-04-15 08:43:00 +00:00
|
|
|
|
print_header
|
|
|
|
|
|
|
|
|
|
|
|
local action
|
|
|
|
|
|
action="$(menu_select \
|
|
|
|
|
|
"Choisis une action" \
|
|
|
|
|
|
"Installer/mettre a jour le binaire + setup" \
|
|
|
|
|
|
"Configurer Claude Code (apply direct)" \
|
|
|
|
|
|
"Configurer Codex (apply direct)" \
|
|
|
|
|
|
"Generer JSON MCP manuel" \
|
|
|
|
|
|
"Quitter")"
|
|
|
|
|
|
printf "\n" >&2
|
|
|
|
|
|
|
|
|
|
|
|
case "$action" in
|
|
|
|
|
|
"Installer/mettre a jour le binaire + setup")
|
|
|
|
|
|
run_setup_wizard
|
2026-04-15 08:59:23 +00:00
|
|
|
|
post_setup_configure_mcp
|
2026-04-15 08:43:00 +00:00
|
|
|
|
;;
|
|
|
|
|
|
"Configurer Claude Code (apply direct)")
|
|
|
|
|
|
apply_claude_mcp
|
|
|
|
|
|
;;
|
|
|
|
|
|
"Configurer Codex (apply direct)")
|
|
|
|
|
|
apply_codex_mcp
|
|
|
|
|
|
;;
|
|
|
|
|
|
"Generer JSON MCP manuel")
|
|
|
|
|
|
ui_info "JSON MCP genere sur stdout."
|
|
|
|
|
|
print_mcp_json
|
|
|
|
|
|
;;
|
|
|
|
|
|
*)
|
|
|
|
|
|
ui_warn "Annule."
|
|
|
|
|
|
;;
|
|
|
|
|
|
esac
|
2026-04-15 08:19:42 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
main "$@"
|