diff --git a/README.md b/README.md index 578ba4f..d4916f2 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ veth IPs aren't routable off the host and Warp forwards no inbound traffic. - `bin/accounts-health.py` — per-account CDP session probe (runs inside the netns). - `bin/accounts-health.sh` — aggregates account vitality from ACCOUNTS.md, signs + POSTs to the board health ingest (systemd timer, every 15 min). +- `bin/muse -a [args]` — interactive terminal entrypoint for muse-cli; enforces account selection, auto-refreshes CDP cookies, and provides interactive email/OTP prompt fallback. - `bin/muse-cli-node [args]` — runs muse-cli inside node's netns with dedicated Cloudflare WARP egress & auto-refreshing cookies. - `bin/refresh-node-cookies.py ` — extracts fresh cookies from running Chromium CDP in netns into `~/.config/muse-cli//cookies.txt`. - `bin/muse_hybrid.py` — programmatic hybrid bridge combining fast gateway calls with CDP fallbacks. diff --git a/bin/muse b/bin/muse new file mode 100755 index 0000000..13bde8b --- /dev/null +++ b/bin/muse @@ -0,0 +1,137 @@ +#!/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 [arguments...]" + echo " muse -a [arguments...]" + echo "" + echo "Required:" + echo " -a, --account Target NetVM account / node identity" + echo "" + echo "Available accounts:" + for acct in "${VALID_ACCOUNTS[@]}"; do + echo " • $acct" + done + echo "" + echo "Common commands:" + echo " status Check agent status, sessions, and unread" + echo " threads List active threads and sidechats" + echo " history --thread View message history" + echo " send --thread msg Send message to an agent" + echo " unread View unread counts" + echo "" + echo "Authentication & Cookie Management:" + echo " Cookies are isolated per-node in ~/.config/muse-cli//" + echo " If expired, the CLI attempts automated CDP cookie extraction." + echo "" +} + +ACCOUNT="" +POSITIONAL=() + +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. Please specify an account with -a or --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 + +# 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 diff --git a/docs/AGENT-TOOLING.md b/docs/AGENT-TOOLING.md index 85fb418..02e4ad4 100644 --- a/docs/AGENT-TOOLING.md +++ b/docs/AGENT-TOOLING.md @@ -1,5 +1,7 @@ # Agent Tooling & Subagent Delegation Guide +> **Box is the main surface.** All operator work goes through Box (box.muse-dev.online). The web UI, `box` CLI, and agents share the same API endpoints. No UI-only powers. + Welcome, operator. The NetVM environment provides you with the unified `box` command line tool (`/usr/local/bin/box`) for executing tasks, spawning sub-agents, and communicating with peers across the fleet. --- @@ -44,24 +46,25 @@ box dm send --agent 646 --to pip --target 646-pip "Hey Pip, start-page onboardin --- -## 3. Direct Gateway Tooling (`box muse`) +## 3. Direct Gateway Tooling (`muse` & `box muse`) -You can directly interact with the headless Muse gateway inside your isolated network namespace: +You can directly interact with the headless Muse gateway inside your isolated network namespace using either `muse` or `box muse`: ```bash -# List all your active threads +# Using native muse wrapper (interactive prompt & account enforcement) +muse -a status +muse -a threads +muse -a history --thread --limit 10 +muse -a send --thread "" + +# If invoked without -a/--account, it displays valid accounts and usage instructions: +muse + +# Alternatively via box CLI: box muse threads - -# Read message history in a thread box muse history --thread --limit 10 - -# Check unread messages box muse unread - -# Start a new thread / session box muse session-start --title "" - -# Fast fire-and-forget message send box muse <self> send --thread <thread_uuid> "<message>" ```