MAESTRO: add TTY detection, --provider/--api-key flags, and curl | bash support to install.sh
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
+121
-15
@@ -3,7 +3,13 @@ set -euo pipefail
|
|||||||
|
|
||||||
# claude-mem OpenClaw Plugin Installer
|
# claude-mem OpenClaw Plugin Installer
|
||||||
# Installs the claude-mem persistent memory plugin for OpenClaw gateways.
|
# Installs the claude-mem persistent memory plugin for OpenClaw gateways.
|
||||||
# Usage: bash install.sh [--non-interactive]
|
#
|
||||||
|
# Usage:
|
||||||
|
# curl -fsSL https://raw.githubusercontent.com/thedotmack/claude-mem/main/openclaw/install.sh | bash
|
||||||
|
# # Or with options:
|
||||||
|
# curl -fsSL https://raw.githubusercontent.com/thedotmack/claude-mem/main/openclaw/install.sh | bash -s -- --provider=gemini --api-key=YOUR_KEY
|
||||||
|
# # Direct execution:
|
||||||
|
# bash install.sh [--non-interactive] [--provider=claude|gemini|openrouter] [--api-key=KEY]
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Constants
|
# Constants
|
||||||
@@ -11,7 +17,68 @@ set -euo pipefail
|
|||||||
|
|
||||||
readonly MIN_BUN_VERSION="1.1.14"
|
readonly MIN_BUN_VERSION="1.1.14"
|
||||||
readonly INSTALLER_VERSION="1.0.0"
|
readonly INSTALLER_VERSION="1.0.0"
|
||||||
readonly NON_INTERACTIVE="${1:-}"
|
|
||||||
|
###############################################################################
|
||||||
|
# Argument parsing
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
NON_INTERACTIVE=""
|
||||||
|
CLI_PROVIDER=""
|
||||||
|
CLI_API_KEY=""
|
||||||
|
|
||||||
|
while [[ $# -gt 0 ]]; do
|
||||||
|
case "$1" in
|
||||||
|
--non-interactive)
|
||||||
|
NON_INTERACTIVE="true"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--provider=*)
|
||||||
|
CLI_PROVIDER="${1#--provider=}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--provider)
|
||||||
|
CLI_PROVIDER="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
--api-key=*)
|
||||||
|
CLI_API_KEY="${1#--api-key=}"
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
--api-key)
|
||||||
|
CLI_API_KEY="${2:-}"
|
||||||
|
shift 2
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
shift
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# TTY detection — ensure interactive prompts work under curl | bash
|
||||||
|
# When piped, stdin reads from curl's output, not the terminal.
|
||||||
|
# We open /dev/tty on fd 3 and read interactive input from there.
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
TTY_FD=0
|
||||||
|
|
||||||
|
setup_tty() {
|
||||||
|
if [[ -t 0 ]]; then
|
||||||
|
# stdin IS a terminal — use it directly
|
||||||
|
TTY_FD=0
|
||||||
|
elif [[ -e /dev/tty ]]; then
|
||||||
|
# stdin is piped (curl | bash) but /dev/tty is available
|
||||||
|
exec 3</dev/tty
|
||||||
|
TTY_FD=3
|
||||||
|
else
|
||||||
|
# No terminal available at all
|
||||||
|
if [[ "$NON_INTERACTIVE" != "true" ]]; then
|
||||||
|
echo "Error: No terminal available for interactive prompts." >&2
|
||||||
|
echo "Use --non-interactive or run directly: bash install.sh" >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Color utilities — auto-detect terminal color support
|
# Color utilities — auto-detect terminal color support
|
||||||
@@ -43,17 +110,20 @@ warn() { echo -e "${COLOR_YELLOW}⚠${COLOR_RESET} $*"; }
|
|||||||
error() { echo -e "${COLOR_RED}✗${COLOR_RESET} $*" >&2; }
|
error() { echo -e "${COLOR_RED}✗${COLOR_RESET} $*" >&2; }
|
||||||
|
|
||||||
prompt_user() {
|
prompt_user() {
|
||||||
if [[ "$NON_INTERACTIVE" == "--non-interactive" ]]; then
|
if [[ "$NON_INTERACTIVE" == "true" ]]; then
|
||||||
error "Cannot prompt in non-interactive mode: $*"
|
error "Cannot prompt in non-interactive mode: $*"
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
if [[ ! -t 0 ]]; then
|
|
||||||
error "Cannot prompt when stdin is not a terminal: $*"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
echo -en "${COLOR_CYAN}?${COLOR_RESET} $* "
|
echo -en "${COLOR_CYAN}?${COLOR_RESET} $* "
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Read a line from the terminal (works even when stdin is piped from curl)
|
||||||
|
# Callers always pass -r via $@; shellcheck can't see through the delegation
|
||||||
|
read_tty() {
|
||||||
|
# shellcheck disable=SC2162
|
||||||
|
read "$@" <&"$TTY_FD"
|
||||||
|
}
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Banner
|
# Banner
|
||||||
###############################################################################
|
###############################################################################
|
||||||
@@ -500,7 +570,42 @@ setup_ai_provider() {
|
|||||||
info "AI Provider Configuration"
|
info "AI Provider Configuration"
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [[ "$NON_INTERACTIVE" == "--non-interactive" ]] || [[ ! -t 0 ]]; then
|
# Handle --provider flag (pre-selected via CLI)
|
||||||
|
if [[ -n "$CLI_PROVIDER" ]]; then
|
||||||
|
case "$CLI_PROVIDER" in
|
||||||
|
claude)
|
||||||
|
AI_PROVIDER="claude"
|
||||||
|
success "Selected via --provider: Claude Max Plan (CLI authentication)"
|
||||||
|
;;
|
||||||
|
gemini)
|
||||||
|
AI_PROVIDER="gemini"
|
||||||
|
AI_PROVIDER_API_KEY="${CLI_API_KEY}"
|
||||||
|
if [[ -n "$AI_PROVIDER_API_KEY" ]]; then
|
||||||
|
success "Selected via --provider: Gemini (API key set via --api-key)"
|
||||||
|
else
|
||||||
|
warn "Selected via --provider: Gemini (no API key — add later in ~/.claude-mem/settings.json)"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
openrouter)
|
||||||
|
AI_PROVIDER="openrouter"
|
||||||
|
AI_PROVIDER_API_KEY="${CLI_API_KEY}"
|
||||||
|
if [[ -n "$AI_PROVIDER_API_KEY" ]]; then
|
||||||
|
success "Selected via --provider: OpenRouter (API key set via --api-key)"
|
||||||
|
else
|
||||||
|
warn "Selected via --provider: OpenRouter (no API key — add later in ~/.claude-mem/settings.json)"
|
||||||
|
fi
|
||||||
|
;;
|
||||||
|
*)
|
||||||
|
error "Unknown provider: ${CLI_PROVIDER}"
|
||||||
|
error "Valid providers: claude, gemini, openrouter"
|
||||||
|
exit 1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
return 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Handle non-interactive mode (no --provider flag)
|
||||||
|
if [[ "$NON_INTERACTIVE" == "true" ]]; then
|
||||||
info "Non-interactive mode: defaulting to Claude Max Plan (no API key needed)"
|
info "Non-interactive mode: defaulting to Claude Max Plan (no API key needed)"
|
||||||
AI_PROVIDER="claude"
|
AI_PROVIDER="claude"
|
||||||
return 0
|
return 0
|
||||||
@@ -521,7 +626,7 @@ setup_ai_provider() {
|
|||||||
local choice
|
local choice
|
||||||
while true; do
|
while true; do
|
||||||
prompt_user "Enter choice [1/2/3] (default: 1):"
|
prompt_user "Enter choice [1/2/3] (default: 1):"
|
||||||
read -r choice
|
read_tty -r choice
|
||||||
choice="${choice:-1}"
|
choice="${choice:-1}"
|
||||||
|
|
||||||
case "$choice" in
|
case "$choice" in
|
||||||
@@ -534,7 +639,7 @@ setup_ai_provider() {
|
|||||||
AI_PROVIDER="gemini"
|
AI_PROVIDER="gemini"
|
||||||
echo ""
|
echo ""
|
||||||
prompt_user "Enter your Gemini API key (from https://ai.google.dev):"
|
prompt_user "Enter your Gemini API key (from https://ai.google.dev):"
|
||||||
read -rs AI_PROVIDER_API_KEY
|
read_tty -rs AI_PROVIDER_API_KEY
|
||||||
echo ""
|
echo ""
|
||||||
if [[ -z "$AI_PROVIDER_API_KEY" ]]; then
|
if [[ -z "$AI_PROVIDER_API_KEY" ]]; then
|
||||||
warn "No API key provided — you can add it later in ~/.claude-mem/settings.json"
|
warn "No API key provided — you can add it later in ~/.claude-mem/settings.json"
|
||||||
@@ -547,7 +652,7 @@ setup_ai_provider() {
|
|||||||
AI_PROVIDER="openrouter"
|
AI_PROVIDER="openrouter"
|
||||||
echo ""
|
echo ""
|
||||||
prompt_user "Enter your OpenRouter API key (from https://openrouter.ai):"
|
prompt_user "Enter your OpenRouter API key (from https://openrouter.ai):"
|
||||||
read -rs AI_PROVIDER_API_KEY
|
read_tty -rs AI_PROVIDER_API_KEY
|
||||||
echo ""
|
echo ""
|
||||||
if [[ -z "$AI_PROVIDER_API_KEY" ]]; then
|
if [[ -z "$AI_PROVIDER_API_KEY" ]]; then
|
||||||
warn "No API key provided — you can add it later in ~/.claude-mem/settings.json"
|
warn "No API key provided — you can add it later in ~/.claude-mem/settings.json"
|
||||||
@@ -822,7 +927,7 @@ setup_observation_feed() {
|
|||||||
echo " you'll see it in your chat."
|
echo " you'll see it in your chat."
|
||||||
echo ""
|
echo ""
|
||||||
|
|
||||||
if [[ "$NON_INTERACTIVE" == "--non-interactive" ]] || [[ ! -t 0 ]]; then
|
if [[ "$NON_INTERACTIVE" == "true" ]]; then
|
||||||
info "Non-interactive mode: skipping observation feed setup"
|
info "Non-interactive mode: skipping observation feed setup"
|
||||||
info "Configure later in ~/.openclaw/openclaw.json under"
|
info "Configure later in ~/.openclaw/openclaw.json under"
|
||||||
info " plugins.entries.claude-mem.config.observationFeed"
|
info " plugins.entries.claude-mem.config.observationFeed"
|
||||||
@@ -831,7 +936,7 @@ setup_observation_feed() {
|
|||||||
|
|
||||||
prompt_user "Would you like to set up real-time observation streaming to a messaging channel? (y/n)"
|
prompt_user "Would you like to set up real-time observation streaming to a messaging channel? (y/n)"
|
||||||
local answer
|
local answer
|
||||||
read -r answer
|
read_tty -r answer
|
||||||
answer="${answer:-n}"
|
answer="${answer:-n}"
|
||||||
|
|
||||||
if [[ "${answer,,}" != "y" && "${answer,,}" != "yes" ]]; then
|
if [[ "${answer,,}" != "y" && "${answer,,}" != "yes" ]]; then
|
||||||
@@ -857,7 +962,7 @@ setup_observation_feed() {
|
|||||||
local channel_choice
|
local channel_choice
|
||||||
while true; do
|
while true; do
|
||||||
prompt_user "Enter choice [1-6]:"
|
prompt_user "Enter choice [1-6]:"
|
||||||
read -r channel_choice
|
read_tty -r channel_choice
|
||||||
|
|
||||||
case "$channel_choice" in
|
case "$channel_choice" in
|
||||||
1)
|
1)
|
||||||
@@ -917,7 +1022,7 @@ setup_observation_feed() {
|
|||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
prompt_user "Enter your ${FEED_CHANNEL} target ID:"
|
prompt_user "Enter your ${FEED_CHANNEL} target ID:"
|
||||||
read -r FEED_TARGET_ID
|
read_tty -r FEED_TARGET_ID
|
||||||
|
|
||||||
if [[ -z "$FEED_TARGET_ID" ]]; then
|
if [[ -z "$FEED_TARGET_ID" ]]; then
|
||||||
warn "No target ID provided — skipping observation feed setup."
|
warn "No target ID provided — skipping observation feed setup."
|
||||||
@@ -1098,6 +1203,7 @@ print_completion_summary() {
|
|||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
|
setup_tty
|
||||||
print_banner
|
print_banner
|
||||||
detect_platform
|
detect_platform
|
||||||
|
|
||||||
|
|||||||
@@ -1324,6 +1324,275 @@ assert_contains "$(grep -A2 'userinfobot' "$INSTALL_SCRIPT" 2>/dev/null || echo
|
|||||||
assert_contains "$(grep -A2 'Developer Mode' "$INSTALL_SCRIPT" 2>/dev/null || echo '')" "Developer Mode" "Discord instructions include Developer Mode"
|
assert_contains "$(grep -A2 'Developer Mode' "$INSTALL_SCRIPT" 2>/dev/null || echo '')" "Developer Mode" "Discord instructions include Developer Mode"
|
||||||
assert_contains "$(grep -A2 'C01ABC2DEFG' "$INSTALL_SCRIPT" 2>/dev/null || echo '')" "C01ABC2DEFG" "Slack instructions include sample channel ID"
|
assert_contains "$(grep -A2 'C01ABC2DEFG' "$INSTALL_SCRIPT" 2>/dev/null || echo '')" "C01ABC2DEFG" "Slack instructions include sample channel ID"
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Test: TTY detection — setup_tty() and read_tty() exist
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== TTY detection ==="
|
||||||
|
|
||||||
|
for fn in setup_tty read_tty; do
|
||||||
|
if declare -f "$fn" &>/dev/null; then
|
||||||
|
test_pass "Function ${fn}() is defined"
|
||||||
|
else
|
||||||
|
test_fail "Function ${fn}() should be defined"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
|
||||||
|
# Verify TTY_FD is initialized (defaults to 0)
|
||||||
|
if declare -p TTY_FD &>/dev/null; then
|
||||||
|
test_pass "TTY_FD variable is defined"
|
||||||
|
else
|
||||||
|
test_fail "TTY_FD variable should be defined"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Verify setup_tty is called from main()
|
||||||
|
if grep -q 'setup_tty' "$INSTALL_SCRIPT"; then
|
||||||
|
test_pass "main() calls setup_tty"
|
||||||
|
else
|
||||||
|
test_fail "main() should call setup_tty"
|
||||||
|
fi
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Test: Argument parsing — --provider flag
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Argument parsing — --provider flag ==="
|
||||||
|
|
||||||
|
test_provider_flag_claude() {
|
||||||
|
local result
|
||||||
|
result="$(bash -c '
|
||||||
|
set -euo pipefail
|
||||||
|
TERM=dumb
|
||||||
|
tmp=$(mktemp)
|
||||||
|
sed "$ d" "'"${INSTALL_SCRIPT}"'" > "$tmp"
|
||||||
|
echo "main() { :; }" >> "$tmp"
|
||||||
|
set -- "--provider=claude"
|
||||||
|
source "$tmp"
|
||||||
|
rm -f "$tmp"
|
||||||
|
setup_ai_provider >/dev/null 2>&1
|
||||||
|
echo "$AI_PROVIDER"
|
||||||
|
' 2>/dev/null)" || true
|
||||||
|
|
||||||
|
assert_eq "claude" "$result" "--provider=claude sets AI_PROVIDER to claude"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_provider_flag_claude
|
||||||
|
|
||||||
|
test_provider_flag_gemini_with_api_key() {
|
||||||
|
local result
|
||||||
|
result="$(bash -c '
|
||||||
|
set -euo pipefail
|
||||||
|
TERM=dumb
|
||||||
|
tmp=$(mktemp)
|
||||||
|
sed "$ d" "'"${INSTALL_SCRIPT}"'" > "$tmp"
|
||||||
|
echo "main() { :; }" >> "$tmp"
|
||||||
|
set -- "--provider=gemini" "--api-key=test-key-123"
|
||||||
|
source "$tmp"
|
||||||
|
rm -f "$tmp"
|
||||||
|
setup_ai_provider >/dev/null 2>&1
|
||||||
|
echo "PROVIDER=$AI_PROVIDER"
|
||||||
|
echo "KEY=$AI_PROVIDER_API_KEY"
|
||||||
|
' 2>/dev/null)" || true
|
||||||
|
|
||||||
|
assert_contains "$result" "PROVIDER=gemini" "--provider=gemini sets AI_PROVIDER to gemini"
|
||||||
|
assert_contains "$result" "KEY=test-key-123" "--api-key=test-key-123 sets API key"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_provider_flag_gemini_with_api_key
|
||||||
|
|
||||||
|
test_provider_flag_openrouter() {
|
||||||
|
local result
|
||||||
|
result="$(bash -c '
|
||||||
|
set -euo pipefail
|
||||||
|
TERM=dumb
|
||||||
|
tmp=$(mktemp)
|
||||||
|
sed "$ d" "'"${INSTALL_SCRIPT}"'" > "$tmp"
|
||||||
|
echo "main() { :; }" >> "$tmp"
|
||||||
|
set -- "--provider=openrouter" "--api-key=sk-or-test"
|
||||||
|
source "$tmp"
|
||||||
|
rm -f "$tmp"
|
||||||
|
setup_ai_provider >/dev/null 2>&1
|
||||||
|
echo "PROVIDER=$AI_PROVIDER"
|
||||||
|
echo "KEY=$AI_PROVIDER_API_KEY"
|
||||||
|
' 2>/dev/null)" || true
|
||||||
|
|
||||||
|
assert_contains "$result" "PROVIDER=openrouter" "--provider=openrouter sets AI_PROVIDER"
|
||||||
|
assert_contains "$result" "KEY=sk-or-test" "--api-key sets API key for openrouter"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_provider_flag_openrouter
|
||||||
|
|
||||||
|
test_provider_flag_invalid() {
|
||||||
|
local exit_code=0
|
||||||
|
bash -c '
|
||||||
|
set -euo pipefail
|
||||||
|
TERM=dumb
|
||||||
|
tmp=$(mktemp)
|
||||||
|
sed "$ d" "'"${INSTALL_SCRIPT}"'" > "$tmp"
|
||||||
|
echo "main() { :; }" >> "$tmp"
|
||||||
|
set -- "--provider=invalid"
|
||||||
|
source "$tmp"
|
||||||
|
rm -f "$tmp"
|
||||||
|
setup_ai_provider
|
||||||
|
' >/dev/null 2>&1 || exit_code=$?
|
||||||
|
|
||||||
|
if [[ "$exit_code" -ne 0 ]]; then
|
||||||
|
test_pass "--provider=invalid exits with error"
|
||||||
|
else
|
||||||
|
test_fail "--provider=invalid should exit with error"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
test_provider_flag_invalid
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Test: Argument parsing — --non-interactive flag (new format)
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== Argument parsing — --non-interactive ==="
|
||||||
|
|
||||||
|
test_non_interactive_flag() {
|
||||||
|
local result
|
||||||
|
result="$(bash -c '
|
||||||
|
set -euo pipefail
|
||||||
|
TERM=dumb
|
||||||
|
tmp=$(mktemp)
|
||||||
|
sed "$ d" "'"${INSTALL_SCRIPT}"'" > "$tmp"
|
||||||
|
echo "main() { :; }" >> "$tmp"
|
||||||
|
set -- "--non-interactive"
|
||||||
|
source "$tmp"
|
||||||
|
rm -f "$tmp"
|
||||||
|
echo "NON_INTERACTIVE=$NON_INTERACTIVE"
|
||||||
|
' 2>/dev/null)" || true
|
||||||
|
|
||||||
|
assert_contains "$result" "NON_INTERACTIVE=true" "--non-interactive sets NON_INTERACTIVE=true"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_non_interactive_flag
|
||||||
|
|
||||||
|
test_non_interactive_with_provider() {
|
||||||
|
local result
|
||||||
|
result="$(bash -c '
|
||||||
|
set -euo pipefail
|
||||||
|
TERM=dumb
|
||||||
|
tmp=$(mktemp)
|
||||||
|
sed "$ d" "'"${INSTALL_SCRIPT}"'" > "$tmp"
|
||||||
|
echo "main() { :; }" >> "$tmp"
|
||||||
|
set -- "--non-interactive" "--provider=gemini" "--api-key=my-key"
|
||||||
|
source "$tmp"
|
||||||
|
rm -f "$tmp"
|
||||||
|
setup_ai_provider >/dev/null 2>&1
|
||||||
|
echo "PROVIDER=$AI_PROVIDER"
|
||||||
|
echo "KEY=$AI_PROVIDER_API_KEY"
|
||||||
|
echo "NON_INTERACTIVE=$NON_INTERACTIVE"
|
||||||
|
' 2>/dev/null)" || true
|
||||||
|
|
||||||
|
assert_contains "$result" "PROVIDER=gemini" "--non-interactive + --provider: provider set correctly"
|
||||||
|
assert_contains "$result" "KEY=my-key" "--non-interactive + --api-key: key set correctly"
|
||||||
|
assert_contains "$result" "NON_INTERACTIVE=true" "--non-interactive flag parsed alongside --provider"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_non_interactive_with_provider
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Test: --non-interactive mode completes without hanging
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== --non-interactive full flow ==="
|
||||||
|
|
||||||
|
test_non_interactive_completes() {
|
||||||
|
# Run the full setup_ai_provider + setup_observation_feed in non-interactive mode
|
||||||
|
# This should complete without any prompts or hangs
|
||||||
|
local result
|
||||||
|
result="$(bash -c '
|
||||||
|
set -euo pipefail
|
||||||
|
TERM=dumb
|
||||||
|
tmp=$(mktemp)
|
||||||
|
sed "$ d" "'"${INSTALL_SCRIPT}"'" > "$tmp"
|
||||||
|
echo "main() { :; }" >> "$tmp"
|
||||||
|
set -- "--non-interactive"
|
||||||
|
source "$tmp"
|
||||||
|
rm -f "$tmp"
|
||||||
|
setup_ai_provider 2>/dev/null
|
||||||
|
setup_observation_feed 2>/dev/null
|
||||||
|
echo "AI=$AI_PROVIDER"
|
||||||
|
echo "FEED=$FEED_CONFIGURED"
|
||||||
|
' 2>/dev/null)" || true
|
||||||
|
|
||||||
|
assert_contains "$result" "AI=claude" "--non-interactive: AI provider defaults to claude"
|
||||||
|
assert_contains "$result" "FEED=false" "--non-interactive: observation feed skipped"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_non_interactive_completes
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Test: Script structure — curl | bash usage comment
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== curl | bash usage comment ==="
|
||||||
|
|
||||||
|
if grep -q 'curl -fsSL.*raw.githubusercontent.com.*install.sh | bash' "$INSTALL_SCRIPT"; then
|
||||||
|
test_pass "install.sh contains curl | bash usage comment"
|
||||||
|
else
|
||||||
|
test_fail "install.sh should contain curl | bash usage comment"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if grep -q 'bash -s -- --provider=' "$INSTALL_SCRIPT"; then
|
||||||
|
test_pass "install.sh documents --provider flag in usage comment"
|
||||||
|
else
|
||||||
|
test_fail "install.sh should document --provider flag in usage comment"
|
||||||
|
fi
|
||||||
|
|
||||||
|
###############################################################################
|
||||||
|
# Test: write_settings with --provider flag end-to-end
|
||||||
|
###############################################################################
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== write_settings with --provider flag ==="
|
||||||
|
|
||||||
|
test_write_settings_via_provider_flag() {
|
||||||
|
local fake_home
|
||||||
|
fake_home="$(mktemp -d)"
|
||||||
|
|
||||||
|
local result
|
||||||
|
result="$(bash -c '
|
||||||
|
set -euo pipefail
|
||||||
|
TERM=dumb
|
||||||
|
export HOME="'"$fake_home"'"
|
||||||
|
tmp=$(mktemp)
|
||||||
|
sed "$ d" "'"${INSTALL_SCRIPT}"'" > "$tmp"
|
||||||
|
echo "main() { :; }" >> "$tmp"
|
||||||
|
set -- "--provider=gemini" "--api-key=test-end-to-end-key"
|
||||||
|
source "$tmp"
|
||||||
|
rm -f "$tmp"
|
||||||
|
setup_ai_provider >/dev/null 2>&1
|
||||||
|
write_settings >/dev/null 2>&1
|
||||||
|
echo "DONE"
|
||||||
|
' 2>/dev/null)" || true
|
||||||
|
|
||||||
|
if [[ "$result" == *"DONE"* ]]; then
|
||||||
|
local settings_file="${fake_home}/.claude-mem/settings.json"
|
||||||
|
local provider
|
||||||
|
provider="$(node -e "const s = JSON.parse(require('fs').readFileSync('${settings_file}','utf8')); console.log(s.CLAUDE_MEM_PROVIDER);")"
|
||||||
|
assert_eq "gemini" "$provider" "--provider flag: settings.json has provider=gemini"
|
||||||
|
|
||||||
|
local api_key
|
||||||
|
api_key="$(node -e "const s = JSON.parse(require('fs').readFileSync('${settings_file}','utf8')); console.log(s.CLAUDE_MEM_GEMINI_API_KEY);")"
|
||||||
|
assert_eq "test-end-to-end-key" "$api_key" "--provider flag: settings.json has correct API key"
|
||||||
|
else
|
||||||
|
test_fail "--provider flag: write_settings failed"
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "$fake_home"
|
||||||
|
}
|
||||||
|
|
||||||
|
test_write_settings_via_provider_flag
|
||||||
|
|
||||||
###############################################################################
|
###############################################################################
|
||||||
# Summary
|
# Summary
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|||||||
Reference in New Issue
Block a user