docs: apply fleet-template (16-artifact scaffold)
Some checks are pending
CI / lint (push) Waiting to run
CI / test (push) Waiting to run
CI / security (push) Waiting to run
CI / pre-commit (push) Waiting to run
CI / license (push) Waiting to run
CI / ai-review (push) Waiting to run

Adds missing standard artifacts:
- README.md (if missing)
- AGENTS.md (AI agent contract)
- PLAN.md (current sprint)
- STATUS.md (where we are)
- DEVELOPMENT.md (dev workflow)
- DEPLOYMENT.md (deploy procedure)
- TESTING.md (test strategy)
- DECISIONS.md (ADR index + templates)
- .github/CODEOWNERS
- .github/workflows/ci.yml

Preserves all existing artifacts.

Refs: RugMunchMedia/fleet-template
This commit is contained in:
Crypto Rug Munch 2026-07-02 02:07:06 +07:00
commit e13bd4d774
203 changed files with 31140 additions and 0 deletions

650
cli/walletpress Executable file
View file

@ -0,0 +1,650 @@
#!/usr/bin/env bash
# WalletPress CLI — Self-Hosted Wallet Management Platform
# Usage: walletpress <command> [options]
set -euo pipefail
VERSION="1.0.0-beta"
CONFIG_DIR="${XDG_CONFIG_HOME:-$HOME/.config}/walletpress"
CONFIG_FILE="$CONFIG_DIR/config.json"
DATA_DIR="${XDG_DATA_HOME:-$HOME/.local/share}/walletpress"
GATES_FILE="$DATA_DIR/gates.json"
LOG_FILE="$DATA_DIR/walletpress.log"
RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; BLUE='\033[0;34m'; CYAN='\033[0;36m'; BOLD='\033[1m'; NC='\033[0m'
mkdir -p "$CONFIG_DIR" "$DATA_DIR"
touch "$GATES_FILE" "$LOG_FILE"
log() { echo "[$(date +%H:%M:%S)] $*" >> "$LOG_FILE"; }
info() { echo -e "${BLUE}::${NC} $*"; }
ok() { echo -e "${GREEN}✓${NC} $*"; }
warn() { echo -e "${YELLOW}⚠${NC} $*"; }
err() { echo -e "${RED}✗${NC} $*" >&2; }
hr() { printf '%*s\n' "${COLUMNS:-80}" '' | tr ' ' '─'; }
die() { err "$1"; exit 1; }
# ─── Config ──────────────────────────────────────────────────────────
load_config() {
if [[ -f "$CONFIG_FILE" ]]; then
API_URL=$(jq -r '.api_url // ""' "$CONFIG_FILE")
API_KEY=$(jq -r '.api_key // ""' "$CONFIG_FILE")
MERCHANT_WALLET=$(jq -r '.merchant_wallet // ""' "$CONFIG_FILE")
MERCHANT_CHAIN=$(jq -r '.merchant_chain // "solana"' "$CONFIG_FILE")
DEFAULT_CHAIN=$(jq -r '.default_chain // "solana"' "$CONFIG_FILE")
else API_URL=""; API_KEY=""; MERCHANT_WALLET=""; MERCHANT_CHAIN="solana"; DEFAULT_CHAIN="solana"
fi
}
save_config() {
jq -n --arg api_url "$API_URL" --arg api_key "$API_KEY" --arg merchant_wallet "$MERCHANT_WALLET" --arg merchant_chain "$MERCHANT_CHAIN" --arg default_chain "$DEFAULT_CHAIN" \
'{api_url: $api_url, api_key: $api_key, merchant_wallet: $merchant_wallet, merchant_chain: $merchant_chain, default_chain: $default_chain}' > "$CONFIG_FILE"
ok "Configuration saved"
}
# ─── API ─────────────────────────────────────────────────────────────
CV="/api/v1/chain-vault"
WA="/api/v1/wallet"
api_call() {
local method="$1" path="$2" body="${3:-}"
if [[ -z "$API_URL" ]]; then die "API not configured. Run 'walletpress setup'"; fi
local url="${API_URL}${path}" headers=()
headers+=(-H "Content-Type: application/json")
[[ -n "$API_KEY" ]] && headers+=(-H "Authorization: Bearer $API_KEY" -H "X-API-Key: $API_KEY")
local cmd=("curl" "-sf")
[[ -n "$body" ]] && cmd+=(-X "$method" -d "$body") || cmd+=(-X "$method")
cmd+=( "${headers[@]}" "$url" )
"${cmd[@]}" 2>/dev/null || return 1
}
api_get() { api_call GET "$1" ""; }
api_post() { api_call POST "$1" "$2"; }
api_del() { api_call DELETE "$1" ""; }
# ─── Gates ───────────────────────────────────────────────────────────
load_gates() {
if [[ -f "$GATES_FILE" ]] && [[ -s "$GATES_FILE" ]]; then GATES=$(cat "$GATES_FILE"); else GATES='[]'; fi
}
save_gates() { echo "$GATES" > "$GATES_FILE"; }
# ══════════════════════════════════════════════════════════════════════
# COMMANDS
# ══════════════════════════════════════════════════════════════════════
cmd_setup() {
hr; echo -e " ${BOLD}WalletPress Setup Wizard${NC}"; hr
echo -e "\n${CYAN}[1/5] API Connection${NC}"
read -rp " Backend API URL [${API_URL:-}]: " input; API_URL="${input:-$API_URL}"
read -rsp " API Key: " input; echo; API_KEY="${input:-$API_KEY}"
if [[ -n "$API_URL" && -n "$API_KEY" ]]; then
info "Testing connection..."
if health=$(api_get "/health" 2>/dev/null); then ok "Connected: $(echo "$health" | jq -r '.status // "ok"')"; else warn "API unreachable"; fi
fi
echo -e "\n${CYAN}[2/5] Merchant Wallet${NC}"
read -rp " Wallet address [${MERCHANT_WALLET:-}]: " input; MERCHANT_WALLET="${input:-$MERCHANT_WALLET}"
read -rp " Chain [${MERCHANT_CHAIN}]: " input; MERCHANT_CHAIN="${input:-$MERCHANT_CHAIN}"
echo -e "\n${CYAN}[3/5] Default Chain${NC}"
read -rp " Default chain [${DEFAULT_CHAIN}]: " input; DEFAULT_CHAIN="${input:-$DEFAULT_CHAIN}"
save_config
echo -e "\n${CYAN}[4/5] Generate First Wallet${NC}"
read -rp " Generate a wallet now? (y/N): " gen
if [[ "$gen" =~ ^[yY] ]]; then cmd_vault generate; fi
echo -e "\n${CYAN}[5/5] Create Token Gate${NC}"
read -rp " Create a token gate? (y/N): " gate
if [[ "$gate" =~ ^[yY] ]]; then
read -rp " Gate name: " title; read -rp " Token address: " token; read -rp " Chain [$DEFAULT_CHAIN]: " chain
chain="${chain:-$DEFAULT_CHAIN}"; read -rp " Min amount [1]: " min; min="${min:-1}"
load_gates; GATES=$(echo "$GATES" | jq --arg t "$title" --arg k "$token" --arg c "$chain" --argjson m "$min" '. += [{title: $t, token: $k, chain: $c, min_amount: $m, created_at: now|strflocaltime("%Y-%m-%dT%H:%M:%S")}]'); save_gates
ok "Gate '$title' created"; fi
hr; ok "Setup complete!"
info "Run 'walletpress --help' to see all commands"
}
cmd_dashboard() {
load_config; hr
echo -e " ${BOLD}WalletPress${NC} v${VERSION}"; hr
echo -e " URL: ${API_URL:-(not set)}"
echo -e " Merchant: ${MERCHANT_WALLET:0:16}... [${MERCHANT_CHAIN}]"
echo -e " Default: ${DEFAULT_CHAIN}"
if [[ -n "$API_URL" ]]; then
health=$(api_get "/health" 2>/dev/null || echo '{"status":"unreachable"}')
echo -e " API: $(echo "$health" | jq -r '.status')"
stats=$(api_get "${CV}/stats" 2>/dev/null || echo '{}')
echo -e " Vault: $(echo "$stats" | jq -r '.total_wallets // "?"') wallets"
chains=$(api_get "${CV}/chains" 2>/dev/null || echo '[]')
echo -e " Chains: $(echo "$chains" | jq 'length') supported"
fi
load_gates; local gc=$(echo "$GATES" | jq 'length')
echo -e " Gates: $gc"
hr
}
# ─── VAULT ───────────────────────────────────────────────────────────
cmd_vault() {
load_config
case "${1:-list}" in
list|ls)
local vault
vault=$(api_get "${CV}/vault" 2>/dev/null || die "Failed to fetch vault")
local count=$(echo "$vault" | jq 'length - 1')
echo -e "${BOLD}Vault (${count} wallets)${NC}"
echo "$vault" | jq -r 'to_entries | .[] | select(.key != "_meta") | " \(.key[0:8]).. \(.value.chain // "?") \(.value.address // .value.public_key // "")[0:24]... \(.value.label // "")"'
;;
get|view)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
api_get "${CV}/vault/${id}" | jq '.' || warn "Wallet not found"
;;
generate|gen)
local chain="${2:-$DEFAULT_CHAIN}" label="${3:-}"
[[ -z "$chain" ]] && read -rp "Chain [$DEFAULT_CHAIN]: " chain; chain="${chain:-$DEFAULT_CHAIN}"
[[ -z "$label" ]] && read -rp "Label: " label
result=$(api_post "${CV}/generate" "{\"chain\":\"$chain\",\"label\":\"$label\"}" || true)
if [[ -n "$result" ]]; then ok "Wallet generated"; echo "$result" | jq '{chain, address, label}'; else die "Generation failed"; fi
;;
batch)
local chains="${2:-solana,ethereum,base,polygon,bsc}" prefix="${3:-batch}"
info "Generating wallets for: $chains"
result=$(api_post "${CV}/generate/batch" "{\"chains\":\"$chains\",\"label_prefix\":\"$prefix\"}" || true)
if [[ -n "$result" ]]; then ok "Batch complete"; echo "$result" | jq '.'; else die "Batch failed"; fi
;;
all)
local prefix="${2:-full}"
info "Generating wallet for ALL supported chains..."
result=$(api_post "${CV}/generate/batch" "{\"label_prefix\":\"$prefix\"}" || true)
if [[ -n "$result" ]]; then ok "All chains generated"; echo "$result" | jq 'keys | .[]'; else die "Failed"; fi
;;
import)
local chain="${2:-}" key="${3:-}" label="${4:-}"
[[ -z "$chain" ]] && read -rp "Chain: " chain
[[ -z "$key" ]] && read -rsp "Private key: " key; echo
[[ -z "$label" ]] && read -rp "Label: " label
result=$(api_post "${CV}/import" "{\"chain\":\"$chain\",\"private_key\":\"$key\",\"label\":\"$label\"}" || true)
if [[ -n "$result" ]]; then ok "Wallet imported"; echo "$result" | jq '.'; else die "Import failed"; fi
;;
stats)
api_get "${CV}/stats" | jq '.' || warn "Failed to fetch stats"
;;
tree)
api_get "${CV}/tree" | jq '.' || warn "Failed"
;;
health-score)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
api_get "${CV}/health-score/${id}" | jq '.' || warn "Failed"
;;
rotate)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
result=$(api_post "${CV}/rotate" "{\"wallet_id\":\"$id\"}" || true)
if [[ -n "$result" ]]; then ok "Wallet rotated"; echo "$result" | jq '{chain, new_address}'; else die "Rotation failed"; fi
;;
export)
local format="${2:-json}"
result=$(api_post "${CV}/export" "{\"format\":\"$format\"}" || true)
if [[ -n "$result" ]]; then echo "$result" | jq '.'; else die "Export failed"; fi
;;
filter)
local field="${2:-chain}" value="${3:-solana}"
api_post "${CV}/bulk/filter" "{\"filters\":{\"$field\":\"$value\"}}" | jq '.' || warn "Filter failed"
;;
delete)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
result=$(api_post "${CV}/bulk/delete" "{\"ids\":[\"$id\"]}" || true)
if [[ -n "$result" ]]; then ok "Wallet deleted"; else die "Delete failed"; fi
;;
paper)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Wallet ID: " id
api_get "${CV}/paper-wallet/${id}" | jq '.' || warn "Failed"
;;
*)
echo "Usage: walletpress vault <list|get|generate|batch|all|import|stats|tree|health-score|rotate|export|filter|delete|paper>"
esac
}
# ─── MNEMONIC ────────────────────────────────────────────────────────
cmd_mnemonic() {
load_config
local phrase="${1:-}" passphrase="${2:-}"
if [[ -z "$phrase" ]]; then
read -rp "Mnemonic phrase (12/24 words): " phrase
read -rp "Passphrase (optional): " passphrase
fi
info "Deriving addresses for all chains..."
result=$(api_post "${CV}/from-mnemonic" "{\"mnemonic\":\"$phrase\",\"passphrase\":\"$passphrase\"}" || true)
if [[ -z "$result" ]]; then die "Conversion failed"; fi
echo "$result" | jq -r '.wallets // .result // . | to_entries | .[] | " \(.key): \(.value.address // .value.public_key // "?")"'
ok "Done — $(echo "$result" | jq '.wallets // .result // . | length') addresses derived"
}
# ─── RECOVERY ────────────────────────────────────────────────────────
cmd_recover() {
load_config
local partial="${1:-}" chain="${2:-$DEFAULT_CHAIN}"
if [[ -z "$partial" ]]; then
read -rp "Partial mnemonic (use ? for unknown words): " partial
read -rp "Chain [$chain]: " input; chain="${input:-$chain}"
fi
info "Attempting recovery on $chain..."
info "This may take time for large partial phrases."
# wallet_killer.py has the recovery engine — invoke via backend
warn "Recovery endpoint not yet exposed in REST API. Use backend directly: python3 -c 'from wallet_killer import recover; print(recover(...))'"
}
# ─── SWEEP ───────────────────────────────────────────────────────────
cmd_sweep() {
load_config
case "${1:-}" in
rotate)
local id="${2:-}" to="${3:-}"
[[ -z "$id" ]] && read -rp "Wallet ID to rotate: " id
[[ -z "$to" ]] && read -rp "Destination address: " to
result=$(api_post "${CV}/rotate-sweep" "{\"wallet_id\":\"$id\",\"to_address\":\"$to\"}" || true)
if [[ -n "$result" ]]; then ok "Rotated and swept"; echo "$result" | jq '.'; else die "Failed"; fi
;;
dust|sweep)
local id="${2:-}" to="${3:-}"
[[ -z "$id" ]] && read -rp "Wallet ID to sweep: " id
[[ -z "$to" ]] && read -rp "Destination address: " to
result=$(api_post "${CV}/sweep" "{\"from_wallet_id\":\"$id\",\"to_address\":\"$to\"}" || true)
if [[ -n "$result" ]]; then ok "Swept"; echo "$result" | jq '.'; else die "Sweep failed"; fi
;;
temporal)
local chain="${2:-$DEFAULT_CHAIN}" ttl="${3:-3600}" label="${4:-temporal}"
result=$(api_post "${CV}/temporal/create" "{\"chain\":\"$chain\",\"ttl_seconds\":$ttl,\"label\":\"$label\"}" || true)
if [[ -n "$result" ]]; then ok "Temporal wallet created (TTL: ${ttl}s)"; echo "$result" | jq '{address, expires_at}'; else die "Failed"; fi
;;
temporal-list)
api_get "${CV}/temporal/list" | jq '.' || warn "None"
;;
temporal-release)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Temporal ID: " id
result=$(api_post "${CV}/temporal/release" "{\"temporal_id\":\"$id\"}" || true)
if [[ -n "$result" ]]; then ok "Released"; else die "Failed"; fi
;;
distribute)
echo "Distribute funds across wallets (advanced). Use backend API directly for now."
;;
funding)
local chain="${2:-$DEFAULT_CHAIN}" id="${3:-}" amount="${4:-}"
[[ -z "$id" ]] && read -rp "Wallet ID: " id
[[ -z "$amount" ]] && read -rp "Amount: " amount
result=$(api_post "${CV}/funding-bundle" "{\"chain\":\"$chain\",\"wallet_id\":\"$id\",\"amount\":$amount}" || true)
if [[ -n "$result" ]]; then ok "Funding bundle created"; echo "$result" | jq '.'; else die "Failed"; fi
;;
deployer)
local chain="${2:-$DEFAULT_CHAIN}" id="${3:-}"
[[ -z "$id" ]] && read -rp "Wallet ID: " id
result=$(api_post "${CV}/deployer-setup" "{\"chain\":\"$chain\",\"wallet_id\":\"$id\"}" || true)
if [[ -n "$result" ]]; then ok "Deployer setup done"; echo "$result" | jq '.'; else die "Failed"; fi
;;
*)
echo "Usage: walletpress sweep <rotate|dust|temporal|temporal-list|temporal-release|funding|deployer>"
esac
}
# ─── ESCROW ──────────────────────────────────────────────────────────
cmd_escrow() {
load_config
case "${1:-create}" in
create)
local addr amount platform conditions
read -rp "Deposit address: " addr
read -rp "Amount (USD): " amount
read -rp "Platform [solana]: " platform; platform="${platform:-solana}"
read -rp "Conditions (JSON): " conditions
result=$(api_post "${CV}/escrow" "{\"deposit_address\":\"$addr\",\"amount\":$amount,\"platform\":\"$platform\",\"conditions\":$conditions}" || true)
if [[ -n "$result" ]]; then ok "Escrow created"; echo "$result" | jq '.'; else die "Failed"; fi
;;
release)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Escrow ID: " id
result=$(api_post "${CV}/escrow/release" "{\"escrow_id\":\"$id\"}" || true)
if [[ -n "$result" ]]; then ok "Released"; echo "$result" | jq '.'; else die "Failed"; fi
;;
*) echo "Usage: walletpress escrow <create|release>"
esac
}
# ─── WALLET ANALYSIS ────────────────────────────────────────────────
cmd_wallet() {
load_config
local sub="${1:-balance}" addr="${2:-}" chain="${3:-$DEFAULT_CHAIN}"
[[ -z "$addr" ]] && read -rp "Address: " addr
[[ "$sub" != "verify" ]] && [[ -z "$chain" || "$chain" == "$DEFAULT_CHAIN" ]] && read -rp "Chain [$DEFAULT_CHAIN]: " input && chain="${input:-$chain}"
case "$sub" in
balance)
api_get "${WA}/${addr}/balance?chain=${chain}" | jq '.' || warn "Failed"
;;
analyze)
api_get "${WA}/${addr}/analyze?chain=${chain}" | jq '.' || warn "Failed"
;;
scan)
local tier="${3:-free}"
api_post "${WA}/scan" "{\"address\":\"$addr\",\"chain\":\"$chain\",\"tier\":\"$tier\"}" | jq '.' || warn "Failed"
;;
txs|transactions)
local limit="${4:-20}"
api_get "${WA}/${addr}/transactions?chain=${chain}&limit=${limit}" | jq '.' || warn "Failed"
;;
verify)
local msg sig
read -rp "Message: " msg; read -rp "Signature: " sig
api_post "${WA}/verify" "{\"address\":\"$addr\",\"message\":\"$msg\",\"signature\":\"$sig\",\"chain\":\"$chain\"}" | jq '.' || warn "Failed"
;;
*) echo "Usage: walletpress wallet <balance|analyze|scan|txs|verify> [address] [chain]"
esac
}
# ─── GATES ───────────────────────────────────────────────────────────
cmd_gate() {
load_config; load_gates
case "${1:-list}" in
add)
local title token chain min
read -rp "Gate name: " title; read -rp "Token address: " token
read -rp "Chain [$DEFAULT_CHAIN]: " chain; chain="${chain:-$DEFAULT_CHAIN}"
read -rp "Min amount [1]: " min; min="${min:-1}"
GATES=$(echo "$GATES" | jq --arg t "$title" --arg k "$token" --arg c "$chain" --argjson m "$min" '. += [{title: $t, token: $k, chain: $c, min_amount: $m, created_at: now|strflocaltime("%Y-%m-%dT%H:%M:%S")}]')
save_gates; local id=$(echo "$GATES" | jq 'length - 1')
ok "Gate '$title' created — shortcode: [wallet_gate id=\"$id\"]"
;;
list)
local c=$(echo "$GATES" | jq 'length'); [[ "$c" -eq 0 ]] && { info "No gates"; return; }
hr; echo -e "${BOLD}Token Gates (${c})${NC}"; hr
echo "$GATES" | jq -r 'to_entries | .[] | " \(.key). \(.value.title)\n token: \(.value.token[0:32])... chain: \(.value.chain) min: \(.value.min_amount)\n code: [wallet_gate id=\"\(.key)\"]\n"'
hr
;;
delete)
local id="${2:-}"; [[ -z "$id" ]] && cmd_gate list >&2 && read -rp "Gate ID to delete: " id
GATES=$(echo "$GATES" | jq "del(.[$id])"); save_gates; ok "Deleted"
;;
check)
local addr="${2:-}" contract="${3:-}" chain="${4:-$DEFAULT_CHAIN}"
[[ -z "$addr" ]] && read -rp "Wallet address: " addr
[[ -z "$contract" ]] && read -rp "Contract address: " contract
result=$(api_get "${WA}/${addr}/balance?chain=${chain}" 2>/dev/null || echo '{}')
local held=$(echo "$result" | jq -r --arg c "$contract" '[.tokens // []] | map(select(.address == $c or .mint == $c)) | .[0].amount // 0')
if [[ "$(echo "$held > 0" | bc -l 2>/dev/null)" == "1" ]]; then ok "Wallet holds $held of $contract"; else warn "No holdings found"; fi
;;
*) echo "Usage: walletpress gate <add|list|delete|check>"
esac
}
# ─── PAYMENTS ─────────────────────────────────────────────────────────
cmd_payment() {
load_config
case "${1:-list}" in
create)
local from to amount token chain
read -rp "From address: " from; read -rp "To [${MERCHANT_WALLET:-}]: " to; to="${to:-$MERCHANT_WALLET}"
read -rp "Amount: " amount; read -rp "Token [SOL]: " token; token="${token:-SOL}"
read -rp "Chain [$DEFAULT_CHAIN]: " chain; chain="${chain:-$DEFAULT_CHAIN}"
result=$(api_post "${CV}/tx/build" "{\"chain\":\"$chain\",\"from\":\"$from\",\"to\":\"$to\",\"amount\":$amount,\"token\":\"$token\"}" || true)
if [[ -n "$result" ]]; then ok "Payment built"; echo "$result" | jq '.'; else die "Build failed"; fi
;;
broadcast)
local tx="${2:-}" chain="${3:-$DEFAULT_CHAIN}"
[[ -z "$tx" ]] && read -rp "Signed TX (hex): " tx
result=$(api_post "${CV}/tx/broadcast" "{\"chain\":\"$chain\",\"signed_tx\":\"$tx\"}" || true)
if [[ -n "$result" ]]; then ok "Broadcasted"; echo "$result" | jq '{tx_hash, status}'; else die "Failed"; fi
;;
list)
echo "On-chain payments: use 'walletpress wallet txs <address>' to view"
;;
*) echo "Usage: walletpress payment <create|broadcast|list>"
esac
}
# ─── API KEYS ────────────────────────────────────────────────────────
cmd_key() {
load_config
case "${1:-generate}" in
generate|create)
local label="${2:-}" scopes="${3:-vault.read,generate}"
[[ -z "$label" ]] && read -rp "Key label: " label
result=$(api_post "${CV}/api-keys" "{\"label\":\"$label\",\"scopes\":[\"${scopes//,/\"\,\"}\"]}" || true)
if [[ -n "$result" ]]; then
hr; echo -e "${BOLD}New API Key${NC}"; hr
echo -e " ${CYAN}$(echo "$result" | jq -r '.api_key // "generated"')${NC}"; hr
echo " Save this now — it won't be shown again."
else die "Key generation failed"; fi
;;
list)
api_get "${CV}/api-keys" | jq '.' || info "No keys"
;;
revoke)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Key ID: " id
result=$(api_post "${CV}/api-keys/revoke" "{\"key_id\":\"$id\"}" || true)
if [[ -n "$result" ]]; then ok "Revoked"; else die "Failed"; fi
;;
local)
local key="wp_$(openssl rand -hex 24)"
hr; echo -e "${BOLD}Local API Key${NC}"; hr
echo -e " ${CYAN}$key${NC}"; hr
echo " Add to backend .env: WALLETPRESS_API_KEY=$key"
echo " Add to WordPress settings as your API key"
;;
*) echo "Usage: walletpress key <generate|list|revoke|local>"
esac
}
# ─── WEBHOOKS ────────────────────────────────────────────────────────
cmd_webhook() {
load_config
case "${1:-list}" in
create)
local url events secret
read -rp "Webhook URL: " url
read -rp "Events (comma-sep) [wallet.generated]: " events; events="${events:-wallet.generated}"
read -rp "Secret: " secret
result=$(api_post "${CV}/webhooks" "{\"url\":\"$url\",\"events\":[\"${events//,/\"\,\"}\"],\"secret\":\"$secret\"}" || true)
if [[ -n "$result" ]]; then ok "Webhook created"; else die "Failed"; fi
;;
list)
api_get "${CV}/webhooks" | jq '.' || info "No webhooks"
;;
delete)
local id="${2:-}"; [[ -z "$id" ]] && read -rp "Webhook ID: " id
api_del "${CV}/webhooks/${id}" && ok "Deleted" || die "Failed"
;;
*) echo "Usage: walletpress webhook <create|list|delete>"
esac
}
# ─── BALANCES ────────────────────────────────────────────────────────
cmd_balances() {
load_config
case "${1:-list}" in
list)
api_get "${CV}/balances" | jq '.' || warn "Failed"
;;
snapshot)
api_post "${CV}/balances/snapshot" "{}" | jq '.' || warn "Failed"
;;
history)
local days="${2:-30}"
api_get "${CV}/balances/history?days=${days}" | jq '.' || warn "Failed"
;;
*) echo "Usage: walletpress balances <list|snapshot|history>"
esac
}
# ─── CHAINS ──────────────────────────────────────────────────────────
cmd_chain() {
load_config
case "${1:-list}" in
list)
api_get "${CV}/chains" | jq -r 'to_entries | .[] | " \(.key): \(.value.name // .value)"' || warn "Failed"
;;
rpc)
api_get "${CV}/rpc-chains" | jq '.' || warn "Failed"
;;
validate)
local chain="${2:-}" addr="${3:-}"
[[ -z "$chain" ]] && read -rp "Chain: " chain
[[ -z "$addr" ]] && read -rp "Address: " addr
api_get "${CV}/validate/${chain}/${addr}" | jq '.' || warn "Failed"
;;
link-proof)
local src="${2:-}" sc="${3:-}" tc="${4:-}"
[[ -z "$src" ]] && read -rp "Source address: " src
[[ -z "$sc" ]] && read -rp "Source chain: " sc
[[ -z "$tc" ]] && read -rp "Target chain: " tc
api_post "${CV}/link/proof" "{\"source_address\":\"$src\",\"source_chain\":\"$sc\",\"target_chain\":\"$tc\"}" | jq '.' || warn "Failed"
;;
link-verify)
local addr="${2:-}" chain="${3:-}" proof="${4:-}"
[[ -z "$addr" ]] && read -rp "Address: " addr
[[ -z "$chain" ]] && read -rp "Chain: " chain
[[ -z "$proof" ]] && read -rp "Proof: " proof
api_post "${CV}/link/verify" "{\"address\":\"$addr\",\"chain\":\"$chain\",\"proof\":\"$proof\"}" | jq '.' || warn "Failed"
;;
*) echo "Usage: walletpress chain <list|rpc|validate|link-proof|link-verify>"
esac
}
# ─── AUDIT ───────────────────────────────────────────────────────────
cmd_audit() {
load_config
local limit="${1:-50}"
api_get "${CV}/audit-trail?limit=${limit}" | jq '.' || warn "No audit entries"
}
# ─── CLUSTER ─────────────────────────────────────────────────────────
cmd_cluster() {
load_config
local chain="${1:-$DEFAULT_CHAIN}" count="${2:-10}"
info "Generating cluster-aware wallets on $chain..."
result=$(api_post "${CV}/cluster" "{\"chain\":\"$chain\",\"count\":$count}" || true)
if [[ -n "$result" ]]; then ok "Cluster generated"; echo "$result" | jq '.'; else die "Failed"; fi
}
# ─── HEALTH ──────────────────────────────────────────────────────────
cmd_health() {
load_config; hr
echo -e "${BOLD}WalletPress Health Check${NC}"; hr
echo -n " Config: "; [[ -f "$CONFIG_FILE" ]] && ok "found" || warn "not configured"
echo -n " API: "
if [[ -n "$API_URL" ]]; then
local h; h=$(api_get "/health" 2>/dev/null || true)
[[ -n "$h" ]] && ok "$(echo "$h" | jq -r '.status')" || warn "unreachable"
else warn "not configured"; fi
echo -n " Vault: "
local v; v=$(api_get "${CV}/stats" 2>/dev/null || true)
[[ -n "$v" ]] && ok "$(echo "$v" | jq -r '.total_wallets // 0') wallets" || warn "unknown"
echo -n " Chains: "
local c; c=$(api_get "${CV}/chains" 2>/dev/null || true)
[[ -n "$c" ]] && ok "$(echo "$c" | jq 'length') supported" || warn "unknown"
echo -n " Merchant: "
[[ -n "$MERCHANT_WALLET" ]] && ok "${MERCHANT_WALLET:0:8}... [$MERCHANT_CHAIN]" || warn "not set"
load_gates; echo -n " Gates: "; ok "$(echo "$GATES" | jq 'length') gate(s)"
hr
}
# ─── SERVE ───────────────────────────────────────────────────────────
cmd_serve() {
load_config; local port="${1:-8580}" host="${2:-127.0.0.1}"
command -v python3 &>/dev/null || die "python3 required"
command -v walletpress-backend &>/dev/null && {
info "Starting WalletPress backend on $host:$port"
walletpress-backend serve --host "$host" --port "$port"
return
}
command -v uvicorn &>/dev/null && {
info "Starting WalletPress API at http://$host:$port"
cd "$(dirname "$0")/../backend" 2>/dev/null && uvicorn main:app --host "$host" --port "$port" --reload
return
}
info "Admin UI at http://$host:$port — Ctrl+C to stop"; hr
cd "$DATA_DIR" && python3 -m http.server "$port" --bind "$host"
}
# ══════════════════════════════════════════════════════════════════════
# MAIN
# ══════════════════════════════════════════════════════════════════════
load_config
case "${1:-help}" in
help|-h|--help) cat <<'HELP'
WalletPress CLI — self-hosted multi-chain wallet generator
Usage: walletpress <command> [options]
Commands:
setup Run setup wizard
dash Show dashboard
vault List/browse vault wallets
mnemonic Convert mnemonic to addresses
recover Attempt wallet recovery
sweep Sweep & rotate wallets
escrow Create escrow wallets
wallet Wallet operations
gate Token gate management
payment Payment records
serve Start admin UI
help Show this help
HELP
;;
setup|wizard) shift; cmd_setup "$@" ;;
dash|dashboard|st) cmd_dashboard ;;
vault|wallet-vault) shift; cmd_vault "$@" ;;
mnemonic|convert) shift; cmd_mnemonic "$@" ;;
recover|recovery) shift; cmd_recover "$@" ;;
sweep) shift; cmd_sweep "$@" ;;
escrow) shift; cmd_escrow "$@" ;;
wallet|address) shift; cmd_wallet "$@" ;;
gate|gates) shift; cmd_gate "$@" ;;
pay|payment) shift; cmd_payment "$@" ;;
key|keys|apikey) shift; cmd_key "$@" ;;
webhook|hooks) shift; cmd_webhook "$@" ;;
balance|balances) shift; cmd_balances "$@" ;;
chain|chains) shift; cmd_chain "$@" ;;
audit|logs) shift; cmd_audit "$@" ;;
cluster) shift; cmd_cluster "$@" ;;
health|status) cmd_health ;;
serve|ui) shift; cmd_serve "$@" ;;
version|-v|--version) echo "WalletPress CLI v${VERSION}" ;;
help|-h|--help|"")
hr
echo -e "${BOLD}WalletPress CLI v${VERSION}${NC}"; hr
echo -e " ${CYAN}setup${NC} Interactive setup wizard (5 steps)"
echo -e " ${CYAN}dashboard${NC} System overview"
echo -e " ${CYAN}vault${NC} Wallet vault: list, get, generate, batch, all, import,"; echo " stats, tree, health-score, rotate, export, filter, delete, paper"
echo -e " ${CYAN}mnemonic${NC} BIP39 mnemonic → 55 chain addresses"
echo -e " ${CYAN}recover${NC} Wallet recovery from partial mnemonics"
echo -e " ${CYAN}sweep${NC} Sweep: rotate, dust, temporal, funding, deployer"
echo -e " ${CYAN}escrow${NC} Pay-to-release escrow: create, release"
echo -e " ${CYAN}wallet${NC} Wallet analysis: balance, analyze, scan, txs, verify"
echo -e " ${CYAN}gate${NC} Token gates: add, list, delete, check"
echo -e " ${CYAN}payment${NC} Crypto payments: create, broadcast"
echo -e " ${CYAN}key${NC} API keys: generate, list, revoke, local"
echo -e " ${CYAN}webhook${NC} Webhooks: create, list, delete"
echo -e " ${CYAN}balances${NC} Balance tracking: list, snapshot, history"
echo -e " ${CYAN}chain${NC} Chain info: list, rpc, validate, link-proof, link-verify"
echo -e " ${CYAN}audit${NC} Audit trail viewer"
echo -e " ${CYAN}cluster${NC} Cluster-aware wallet generation"
echo -e " ${CYAN}health${NC} System health check"
echo -e " ${CYAN}serve${NC} Start admin HTTP UI [port] [host]"
hr
echo " Config: $CONFIG_FILE | Data: $DATA_DIR | Log: $LOG_FILE"
hr
;;
*) die "Unknown: $1. Run 'walletpress help'"
esac