Files

209 lines
6.7 KiB
Bash
Raw Permalink Normal View History

#!/usr/bin/env bash
# NetVM native interactive muse CLI wrapper
# Enforces account/node selection, verifies/refreshes authentication cookies,
# and executes commands in the dedicated network namespace.
set -euo pipefail
SCRIPT_SRC="${BASH_SOURCE[0]}"
while [ -h "$SCRIPT_SRC" ]; do
DIR="$(cd -P "$(dirname "$SCRIPT_SRC")" && pwd)"
SCRIPT_SRC="$(readlink "$SCRIPT_SRC")"
[[ $SCRIPT_SRC != /* ]] && SCRIPT_SRC="$DIR/$SCRIPT_SRC"
done
NETVM_BIN="$(cd -P "$(dirname "$SCRIPT_SRC")" && pwd)"
VALID_ACCOUNTS=("muse" "pip" "646" "opm" "def" "dev")
show_usage() {
echo "Usage: muse <account> <command> [arguments...]"
echo " muse -a <account> <command> [arguments...]"
echo " muse <global-command> [arguments...]"
echo ""
echo "Available accounts:"
for acct in "${VALID_ACCOUNTS[@]}"; do
echo " • $acct"
done
echo ""
echo "Global lookups & tools:"
echo " tui Interactive full-screen Muse TUI & Box fleet console"
echo " tmux [args...] Manage shared Muse tmux sessions (new, send, capture, ls, kill, prune)"
echo " status Fleet overview & node vitality"
echo " threads List registered threads and sidechats across fleet"
echo " unread View unread counts across all agents"
echo " lookup [subcommand] Unified lookup (fleet, threads, unread, approvals, key)"
echo " passkey (or key) View passkey location (VM-only), PIN, & agent approval protocol"
echo ""
echo "Per-account commands:"
echo " tui Launch interactive TUI for this account"
echo " chat [--thread <id>] Launch interactive conversational shell / REPL"
echo " status Check account status, sessions, and unread"
echo " threads List active threads and sidechats for account"
echo " history --thread <id> View message history"
echo " send --thread <id> msg Send message to an agent"
echo " unread View unread counts for account"
echo ""
echo "Authentication & Cookie Management:"
echo " Cookies are isolated per-node in ~/.config/muse-cli/<account>/"
echo " If expired, the CLI attempts automated CDP cookie extraction."
echo ""
}
# Direct top-level global actions that do not require an account
if [[ $# -gt 0 ]]; then
case "$1" in
tui)
shift
exec python3 "$NETVM_BIN/muse-tui.py" --mode muse "$@"
;;
tmux)
shift
exec python3 "$NETVM_BIN/muse-tmux.py" "$@"
;;
passkey|key)
shift
exec python3 "$NETVM_BIN/super-cli.py" passkey "$@"
;;
lookup|lookups)
shift
exec python3 "$NETVM_BIN/super-cli.py" lookup "$@"
;;
fleet)
shift
exec python3 "$NETVM_BIN/super-cli.py" fleet "$@"
;;
threads)
shift
exec python3 "$NETVM_BIN/super-cli.py" thread list "$@"
;;
unread)
shift
exec python3 "$NETVM_BIN/super-cli.py" lookup unread "$@"
;;
status)
shift
exec python3 "$NETVM_BIN/super-cli.py" fleet status "$@"
;;
esac
fi
ACCOUNT=""
POSITIONAL=()
# If first argument does not start with '-', treat it as the account candidate
if [[ $# -gt 0 && "$1" != -* ]]; then
ACCOUNT="$1"
shift
fi
while [[ $# -gt 0 ]]; do
case "$1" in
-a|--account)
if [[ -z "${2:-}" ]]; then
echo "Error: --account requires an argument." >&2
exit 1
fi
ACCOUNT="$2"
shift 2
;;
--account=*)
ACCOUNT="${1#*=}"
shift
;;
-h|--help)
if [[ -z "$ACCOUNT" && ${#POSITIONAL[@]} -eq 0 ]]; then
show_usage
exit 0
fi
POSITIONAL+=("$1")
shift
;;
*)
POSITIONAL+=("$1")
shift
;;
esac
done
if [[ -z "$ACCOUNT" ]]; then
echo "Error: No account specified. Specify account as first argument or with -a/--account." >&2
echo ""
show_usage
exit 1
fi
VALID=0
for acct in "${VALID_ACCOUNTS[@]}"; do
if [[ "$ACCOUNT" == "$acct" ]]; then
VALID=1
break
fi
done
if [[ $VALID -eq 0 ]]; then
echo "Error: Invalid account '$ACCOUNT'." >&2
echo ""
show_usage
exit 1
fi
if [[ ${#POSITIONAL[@]} -eq 0 ]]; then
POSITIONAL=("status")
fi
# If subcommand is 'tui', launch interactive Muse TUI
if [[ "${POSITIONAL[0]}" == "tui" ]]; then
shift_args=("${POSITIONAL[@]:1}")
exec python3 "$NETVM_BIN/muse-tui.py" --mode muse --account "$ACCOUNT" "${shift_args[@]}"
fi
# If subcommand is 'chat', launch interactive chat REPL
if [[ "${POSITIONAL[0]}" == "chat" ]]; then
shift_args=("${POSITIONAL[@]:1}")
exec python3 "$NETVM_BIN/muse-chat-repl.py" "$ACCOUNT" "${shift_args[@]}"
fi
# If subcommand is 'tmux', dispatch to shared muse-tmux manager
if [[ "${POSITIONAL[0]}" == "tmux" ]]; then
shift_args=("${POSITIONAL[@]:1}")
exec python3 "$NETVM_BIN/muse-tmux.py" "${shift_args[@]}"
fi
# Execute command; if auth fails and auto-cdp fails, offer interactive OTP sign-in if running interactively
set +e
"$NETVM_BIN/muse-cli-node" "$ACCOUNT" "${POSITIONAL[@]}"
RC=$?
set -e
if [[ $RC -ne 0 && -t 0 ]]; then
# Check if failure is authentication related
echo ""
echo "Notice: muse-cli command failed with exit code $RC."
echo "Would you like to initiate an interactive login for account '$ACCOUNT'? [y/N] "
read -r response
if [[ "$response" =~ ^([yY][eE][sS]|[yY])$ ]]; then
# Determine email for account if available
EMAIL=$(grep -E "^\|[[:space:]]*$ACCOUNT[[:space:]]*\|" "$NETVM_BIN/../ACCOUNTS.md" | awk -F '|' '{print $7}' | tr -d ' ' || true)
if [[ -z "$EMAIL" || "$EMAIL" == "-" ]]; then
echo -n "Enter email for account '$ACCOUNT': "
read -r EMAIL
fi
echo "Starting sign-in for $EMAIL..."
set +e
"$NETVM_BIN/muse-signin.py" --email "$EMAIL"
SIGNIN_RC=$?
set -e
if [[ $SIGNIN_RC -eq 2 ]]; then
echo -n "Enter the OTP code received: "
read -r OTP_CODE
"$NETVM_BIN/muse-signin.py" --email "$EMAIL" --otp "$OTP_CODE"
echo "Extracting cookies for $ACCOUNT..."
"$NETVM_BIN/refresh-node-cookies.py" "$ACCOUNT"
echo "Retrying command..."
exec "$NETVM_BIN/muse-cli-node" "$ACCOUNT" "${POSITIONAL[@]}"
fi
fi
fi
exit $RC