Last active
March 30, 2026 17:23
-
-
Save yeoweizheng/49c48fc7ff7e8c0f5165ca290e2162d4 to your computer and use it in GitHub Desktop.
OpenCode Profile Switcher
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/bin/sh | |
| # Switch between different opencode auth profiles under ~/.local/share/opencode/auth.json | |
| # Profiles are saved as auth-<profile_name>.json | |
| # Current profile saved in current_profile.txt | |
| die() { | |
| printf '%s\n' "$1" >&2 | |
| exit 1 | |
| } | |
| info() { | |
| printf '%s\n' "$1" | |
| } | |
| get_paths() { | |
| BASE_DIR=${OPENCODE_PROFILE_BASE_DIR:-"$HOME/.local/share/opencode"} | |
| AUTH_FILE="$BASE_DIR/auth.json" | |
| CURRENT_PROFILE_FILE="$BASE_DIR/current_profile.txt" | |
| PROFILES= | |
| PROFILE_COUNT=0 | |
| CURRENT_RECORDED_PROFILE= | |
| VALID_CURRENT_PROFILE= | |
| CURRENT_STATUS= | |
| } | |
| ensure_base_dir() { | |
| if [ ! -d "$BASE_DIR" ]; then | |
| mkdir -p "$BASE_DIR" || die "Failed to create base directory: $BASE_DIR" | |
| fi | |
| } | |
| load_profiles() { | |
| PROFILES= | |
| PROFILE_COUNT=0 | |
| for path in "$BASE_DIR"/auth-*.json; do | |
| [ -f "$path" ] || continue | |
| name=$(basename "$path") | |
| name=${name#auth-} | |
| name=${name%.json} | |
| if [ -z "$PROFILES" ]; then | |
| PROFILES=$name | |
| else | |
| PROFILES=$(printf '%s\n%s\n' "$PROFILES" "$name") | |
| fi | |
| done | |
| if [ -n "$PROFILES" ]; then | |
| PROFILES=$(printf '%s\n' "$PROFILES" | sort) | |
| set -- $PROFILES | |
| PROFILE_COUNT=$# | |
| fi | |
| } | |
| get_current_status() { | |
| CURRENT_RECORDED_PROFILE= | |
| VALID_CURRENT_PROFILE= | |
| if [ -f "$CURRENT_PROFILE_FILE" ]; then | |
| IFS= read -r CURRENT_RECORDED_PROFILE < "$CURRENT_PROFILE_FILE" || CURRENT_RECORDED_PROFILE= | |
| if [ -f "$BASE_DIR/auth-$CURRENT_RECORDED_PROFILE.json" ]; then | |
| VALID_CURRENT_PROFILE=$CURRENT_RECORDED_PROFILE | |
| CURRENT_STATUS=$CURRENT_RECORDED_PROFILE | |
| else | |
| CURRENT_STATUS="$CURRENT_RECORDED_PROFILE (missing profile file)" | |
| fi | |
| elif [ -f "$AUTH_FILE" ]; then | |
| CURRENT_STATUS='unknown profile' | |
| else | |
| CURRENT_STATUS='unset' | |
| fi | |
| } | |
| print_header() { | |
| printf '=== OpenCode Profile Utility ===\n' | |
| printf 'Current profile: %s\n\n' "$CURRENT_STATUS" | |
| } | |
| prompt_main_action() { | |
| while :; do | |
| printf '1) Switch to a profile\n' | |
| printf '2) Save current auth.json to a profile\n' | |
| printf 'q) Quit\n' | |
| printf 'Choose an action [1-2, q]: ' | |
| IFS= read -r action || action=q | |
| case $action in | |
| 1|2|q|quit) | |
| MAIN_ACTION=$action | |
| return 0 | |
| ;; | |
| *) | |
| info 'Invalid input. Please enter 1, 2, or q.' | |
| printf '\n' | |
| ;; | |
| esac | |
| done | |
| } | |
| print_profile_list() { | |
| if [ "$PROFILE_COUNT" -eq 0 ]; then | |
| return 0 | |
| fi | |
| index=1 | |
| set -- $PROFILES | |
| for profile_name do | |
| printf '%s) %s\n' "$index" "$profile_name" | |
| index=$((index + 1)) | |
| done | |
| } | |
| prompt_profile_selection() { | |
| if [ "$PROFILE_COUNT" -eq 0 ]; then | |
| info 'No saved profiles found.' | |
| return 1 | |
| fi | |
| print_profile_list | |
| while :; do | |
| printf 'Select a profile number [1-%s, q]: ' "$PROFILE_COUNT" | |
| IFS= read -r selection || return 1 | |
| case $selection in | |
| q|quit) | |
| return 1 | |
| ;; | |
| ''|*[!0-9]*) | |
| info 'Invalid input. Please enter a profile number or q.' | |
| ;; | |
| *) | |
| if [ "$selection" -lt 1 ] || [ "$selection" -gt "$PROFILE_COUNT" ]; then | |
| info "Invalid input. Please enter a number from 1 to $PROFILE_COUNT." | |
| else | |
| set -- $PROFILES | |
| index=1 | |
| for profile_name do | |
| if [ "$index" -eq "$selection" ]; then | |
| switch_to_profile "$profile_name" | |
| fi | |
| index=$((index + 1)) | |
| done | |
| fi | |
| ;; | |
| esac | |
| done | |
| } | |
| validate_profile_name() { | |
| profile_name=$1 | |
| case $profile_name in | |
| '') | |
| info 'Profile name cannot be empty.' | |
| return 1 | |
| ;; | |
| *[!A-Za-z0-9_-]*) | |
| info 'Profile name may contain only letters, numbers, underscores, and hyphens.' | |
| return 1 | |
| ;; | |
| *) | |
| return 0 | |
| ;; | |
| esac | |
| } | |
| confirm_overwrite() { | |
| profile_name=$1 | |
| printf "Profile '%s' already exists. Overwrite? [y/N]: " "$profile_name" | |
| IFS= read -r answer || return 1 | |
| case $answer in | |
| y|yes) | |
| return 0 | |
| ;; | |
| q|quit) | |
| return 1 | |
| ;; | |
| *) | |
| return 1 | |
| ;; | |
| esac | |
| } | |
| prompt_save_profile_name() { | |
| while :; do | |
| if [ -n "$VALID_CURRENT_PROFILE" ]; then | |
| printf 'Profile name [%s]: ' "$VALID_CURRENT_PROFILE" | |
| else | |
| printf 'Profile name: ' | |
| fi | |
| IFS= read -r profile_name || return 1 | |
| case $profile_name in | |
| q|quit) | |
| return 1 | |
| ;; | |
| esac | |
| if [ -z "$profile_name" ] && [ -n "$VALID_CURRENT_PROFILE" ]; then | |
| profile_name=$VALID_CURRENT_PROFILE | |
| fi | |
| if ! validate_profile_name "$profile_name"; then | |
| continue | |
| fi | |
| target_file="$BASE_DIR/auth-$profile_name.json" | |
| if [ -f "$target_file" ] && ! confirm_overwrite "$profile_name"; then | |
| continue | |
| fi | |
| save_current_to_profile "$profile_name" | |
| done | |
| } | |
| write_current_profile() { | |
| printf '%s\n' "$1" > "$CURRENT_PROFILE_FILE" || die 'Failed to update current_profile.txt' | |
| } | |
| switch_to_profile() { | |
| profile_name=$1 | |
| source_file="$BASE_DIR/auth-$profile_name.json" | |
| [ -f "$source_file" ] || die "Profile file not found: $source_file" | |
| cp "$source_file" "$AUTH_FILE" || die 'Failed to update auth.json' | |
| write_current_profile "$profile_name" | |
| info "Switched to profile '$profile_name'." | |
| exit 0 | |
| } | |
| save_current_to_profile() { | |
| profile_name=$1 | |
| target_file="$BASE_DIR/auth-$profile_name.json" | |
| [ -f "$AUTH_FILE" ] || die 'No auth.json found. Cannot save a profile.' | |
| cp "$AUTH_FILE" "$target_file" || die "Failed to save profile '$profile_name'" | |
| write_current_profile "$profile_name" | |
| info "Saved current auth to profile '$profile_name'." | |
| exit 0 | |
| } | |
| main() { | |
| get_paths | |
| ensure_base_dir | |
| while :; do | |
| load_profiles | |
| get_current_status | |
| print_header | |
| prompt_main_action | |
| case $MAIN_ACTION in | |
| 1) | |
| if prompt_profile_selection; then | |
| : | |
| fi | |
| ;; | |
| 2) | |
| if [ ! -f "$AUTH_FILE" ]; then | |
| info 'No auth.json found. Nothing to save.' | |
| else | |
| prompt_save_profile_name || : | |
| fi | |
| ;; | |
| q|quit) | |
| exit 0 | |
| ;; | |
| esac | |
| printf '\n' | |
| done | |
| } | |
| main "$@" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment