@@ -572,83 +572,169 @@ cmd_start() {
572572 fi
573573}
574574
575- cmd_repair () {
575+ cmd_dry_run () {
576576 check_install
577577 cd " $INSTALL_DIR "
578- sr_load
579578
580- local service=" ${1:- } "
581- if [[ -z " $service " ]]; then
582- error " Usage: dream repair <service> (e.g. dream repair perplexica)"
579+ echo -e " ${BLUE} ━━━ Dream Update — Dry Run ━━━${NC} "
580+ echo -e " ${CYAN} Preview only. No changes will be applied.${NC} "
581+ echo " "
582+
583+ # ── version ──────────────────────────────────────────────────────────────
584+ local cur_ver=" 0.0.0"
585+ if [[ -f " $INSTALL_DIR /.env" ]]; then
586+ local _v
587+ _v=$( grep ' ^DREAM_VERSION=' " $INSTALL_DIR /.env" 2> /dev/null | cut -d= -f2 | tr -d ' [:space:]' )
588+ [[ -n " $_v " ]] && cur_ver=" $_v "
589+ fi
590+ if [[ " $cur_ver " == " 0.0.0" && -f " $INSTALL_DIR /.version" ]]; then
591+ local _vf
592+ _vf=$( jq -r ' .version // empty' " $INSTALL_DIR /.version" 2> /dev/null)
593+ [[ -n " $_vf " ]] && cur_ver=" $_vf "
583594 fi
584595
585- service=$( resolve_service " $service " )
586- local container=" ${SERVICE_CONTAINERS[$service]:- dream-$service } "
596+ # Try dashboard API first; fall back to direct GitHub query.
597+ local api_json=" "
598+ local dashboard_port=" ${DASHBOARD_PORT:- 3002} "
599+ local api_key=" "
600+ api_key=$( grep ' ^DASHBOARD_API_KEY=' " $INSTALL_DIR /.env" 2> /dev/null | cut -d= -f2 | tr -d ' [:space:]' )
601+ if [[ -n " $api_key " ]]; then
602+ api_json=$( curl -sf --max-time 5 \
603+ -H " X-API-Key: ${api_key} " \
604+ " http://localhost:${dashboard_port} /api/update/dry-run" 2> /dev/null || true)
605+ fi
587606
588- warn " This will destroy all data for $service and recreate it from scratch."
589- read -p " Continue? [y/N] " -n 1 -r
590- echo " "
591- if [[ ! $REPLY =~ ^[Yy]$ ]]; then
592- log " Repair cancelled."
593- return 0
607+ local latest_ver=" " changelog_url=" " update_status=" "
608+ if [[ -n " $api_json " ]] && echo " $api_json " | jq -e ' .current_version' > /dev/null 2>&1 ; then
609+ latest_ver=$( echo " $api_json " | jq -r ' .latest_version // empty' )
610+ changelog_url=$( echo " $api_json " | jq -r ' .changelog_url // empty' )
611+ local api_available
612+ api_available=$( echo " $api_json " | jq -r ' .update_available // false' )
613+ [[ " $api_available " == " true" ]] && update_status=" update available" || update_status=" up to date"
614+ else
615+ # Direct GitHub fallback
616+ local gh_resp
617+ gh_resp=$( curl -sf --max-time 8 \
618+ " https://api.github.com/repos/Light-Heart-Labs/DreamServer/releases/latest" 2> /dev/null || true)
619+ latest_ver=$( echo " $gh_resp " | jq -r ' .tag_name // empty' 2> /dev/null | sed ' s/^v//' )
620+ changelog_url=$( echo " $gh_resp " | jq -r ' .html_url // empty' 2> /dev/null)
621+ if [[ -n " $latest_ver " ]]; then
622+ if _semver_lt " $cur_ver " " $latest_ver " ; then
623+ update_status=" update available"
624+ else
625+ update_status=" up to date"
626+ fi
627+ fi
594628 fi
595629
630+ echo -e " ${CYAN} Version:${NC} "
631+ echo " Installed : v${cur_ver} "
632+ if [[ -n " $latest_ver " ]]; then
633+ echo " Available : v${latest_ver} "
634+ if [[ " $update_status " == " update available" ]]; then
635+ echo -e " Status : ${YELLOW}${update_status}${NC} "
636+ else
637+ echo -e " Status : ${GREEN}${update_status}${NC} "
638+ fi
639+ [[ -n " $changelog_url " ]] && echo " Changelog : ${changelog_url} "
640+ else
641+ echo " Available : (could not reach GitHub)"
642+ fi
643+ echo " "
644+
645+ # ── image tags ────────────────────────────────────────────────────────────
646+ echo -e " ${CYAN} Configured image tags (would be pulled):${NC} "
596647 local flags_str
597648 flags_str=$( get_compose_flags)
598649 local -a flags
599650 read -ra flags <<< " $flags_str"
600651
601- log " Repairing $service ..."
602-
603- # Stop and remove container
604- log " Stopping $service ..."
605- docker stop " $container " 2> /dev/null || warn " $container not running"
606- docker rm " $container " 2> /dev/null || warn " $container not found"
607-
608- # Find and remove service volumes
609- local volumes
610- volumes=$( docker compose " ${flags[@]} " config --volumes 2> /dev/null | grep -iE " ^${service} ([-_]|$)" || true)
611- local project
612- project=$( basename " $INSTALL_DIR " | tr ' [:upper:]' ' [:lower:]' | tr -cd ' [:alnum:]-' )
613- for vol in $volumes ; do
614- local full_vol=" ${project} _${vol} "
615- if docker volume inspect " $full_vol " > /dev/null 2>&1 ; then
616- log " Removing volume $full_vol ..."
617- docker volume rm " $full_vol " || warn " Could not remove volume $full_vol "
618- fi
652+ # Prefer API response; fall back to parsing compose files directly.
653+ # Use process substitution so flags like images_shown update in this shell (not a pipe subshell).
654+ local images_shown=false
655+ if [[ -n " $api_json " ]] && echo " $api_json " | jq -e ' .images | length > 0' > /dev/null 2>&1 ; then
656+ local _img_line _any_api=false
657+ while IFS= read -r _img_line || [[ -n " $_img_line " ]]; do
658+ [[ -z " $_img_line " ]] && continue
659+ echo " ${_img_line} "
660+ _any_api=true
661+ done < <( echo " $api_json " | jq -r ' .images[]' | sort -u)
662+ [[ " $_any_api " == " true" ]] && images_shown=true
663+ fi
664+ if [[ " $images_shown " == " false" ]]; then
665+ local _img_line _any_compose=false
666+ while IFS= read -r _img_line || [[ -n " $_img_line " ]]; do
667+ [[ -z " $_img_line " ]] && continue
668+ echo " ${_img_line} "
669+ _any_compose=true
670+ done < <( docker compose " ${flags[@]} " config 2> /dev/null \
671+ | grep -E ' ^\s+image:' | sed ' s/.*image:\s*//' | sort -u)
672+ [[ " $_any_compose " == " true" ]] && images_shown=true
673+ fi
674+ [[ " $images_shown " == " false" ]] && echo " (could not resolve compose config)"
675+
676+ echo " "
677+ echo -e " ${CYAN} Currently running image digests:${NC} "
678+ docker compose " ${flags[@]} " images 2> /dev/null \
679+ | awk ' NR>1 {printf " %-30s %s\n", $1, $4}' \
680+ || echo " (services not running)"
681+ echo " "
682+
683+ # ── model / GGUF ─────────────────────────────────────────────────────────
684+ echo -e " ${CYAN} Model configuration (.env):${NC} "
685+ local -a model_keys=(TIER LLM_MODEL GGUF_FILE CTX_SIZE GPU_BACKEND N_GPU_LAYERS)
686+ local key
687+ for key in " ${model_keys[@]} " ; do
688+ local val
689+ val=$( grep " ^${key} =" " $INSTALL_DIR /.env" 2> /dev/null | cut -d= -f2 | tr -d ' [:space:]' )
690+ printf " %-16s %s\n" " ${key} :" " ${val:- (not set)} "
619691 done
692+ echo " "
693+
694+ # ── .env keys the update path reads/writes ────────────────────────────────
695+ echo -e " ${CYAN} .env keys the update path will read or write:${NC} "
696+ local -a update_keys=(DREAM_VERSION TIER LLM_MODEL GGUF_FILE CTX_SIZE GPU_BACKEND N_GPU_LAYERS)
620697
621- # Recreate container
622- log " Recreating $service ..."
623- docker compose " ${flags[@]} " up -d " $service "
624-
625- # Run service-specific repair script if one exists
626- local repair_script=" $INSTALL_DIR /scripts/repair/repair-${service} .sh"
627- if [[ -x " $repair_script " ]]; then
628- local port=" ${SERVICE_PORTS[$service]:- } "
629- local model=" ${LLM_MODEL:- qwen3-14b} "
630- log " Running config repair for $service ..."
631- bash " $repair_script " " http://localhost:${port} " " $model " && \
632- success " $service repaired and configured" || \
633- warn " $service started but config seed failed — may need manual setup"
698+ if [[ -n " $api_json " ]] && echo " $api_json " | jq -e ' .env_keys' > /dev/null 2>&1 ; then
699+ echo " $api_json " | jq -r ' .env_keys | to_entries[] | " \(.key): \(.value)"' 2> /dev/null \
700+ || true
634701 else
635- success " $service repaired"
702+ for key in " ${update_keys[@]} " ; do
703+ local val
704+ val=$( grep " ^${key} =" " $INSTALL_DIR /.env" 2> /dev/null | cut -d= -f2 | tr -d ' [:space:]' )
705+ printf " %-16s %s\n" " ${key} :" " ${val:- (not set)} "
706+ done
636707 fi
708+ echo " "
709+
710+ # ── summary ───────────────────────────────────────────────────────────────
711+ echo -e " ${CYAN} To apply this update:${NC} dream update"
712+ echo " "
713+ echo -e " ${YELLOW} Dry run complete. Nothing was changed.${NC} "
637714}
638715
639716cmd_update () {
640717 check_install
641718 cd " $INSTALL_DIR "
642719
720+ # Parse flags
721+ local dry_run=" false"
643722 local force_flag=" false"
644- local args=()
645723 while [[ $# -gt 0 ]]; do
646724 case " $1 " in
725+ --dry-run|-n) dry_run=" true" ; shift ;;
647726 --force|-f) force_flag=" true" ; shift ;;
648- * ) args+=(" $1 " ); shift ;;
727+ --* ) error " Unknown option: $1 " ;;
728+ -* ) error " Unknown option: $1 " ;;
729+ * ) error " Unexpected argument: $1 " ;;
649730 esac
650731 done
651732
733+ if [[ " $dry_run " == " true" ]]; then
734+ cmd_dry_run
735+ return 0
736+ fi
737+
652738 local flags_str
653739 flags_str=$( get_compose_flags)
654740 local -a flags
@@ -1910,6 +1996,7 @@ ${CYAN}Examples:${NC}
19101996 # Export preset for sharing
19111997 dream preset import shared.tar.gz
19121998 # Import preset from file
1999+ dream update --dry-run # Preview changes without applying
19132000 dream backup # Create a backup
19142001 dream backup -c # Create compressed backup
19152002 dream backup -l # List all backups
0 commit comments