@@ -643,6 +643,94 @@ generate_container_slug() {
643643 generate_container_slug_for_path " $( pwd) "
644644}
645645
646+ extract_auth_file_stem () {
647+ local path=" $1 "
648+ local base
649+ base=" $( basename " $path " ) "
650+ base=" ${base% .credentials.json} "
651+ base=" ${base% .json} "
652+ base=" $( sanitize_slug_component " $base " ) "
653+ printf ' %s' " ${base: 0: 20} "
654+ }
655+
656+ generate_auth_tag () {
657+ local agent=" $1 "
658+ local auth_method=" ${2:- } "
659+ local creds_file=" ${3:- } "
660+ local env_override=" ${4:- false} "
661+
662+ if [ " $env_override " = true ]; then
663+ printf ' %s' " env"
664+ return
665+ fi
666+
667+ if [ -z " $auth_method " ]; then
668+ printf ' %s' " auth-default"
669+ return
670+ fi
671+
672+ case " $agent :$auth_method " in
673+ claude:claude|codex:chatgpt|gemini:oauth|gemini:gemini-app-oauth)
674+ printf ' %s' " auth-default"
675+ return
676+ ;;
677+ esac
678+
679+ case " $auth_method " in
680+ credentials-file)
681+ if [ -n " $creds_file " ]; then
682+ local stem
683+ stem=" $( extract_auth_file_stem " $creds_file " ) "
684+ printf ' %s' " auth-file-${stem} "
685+ else
686+ printf ' %s' " auth-file"
687+ fi
688+ ;;
689+ api-key|gemini-api-key)
690+ local key_val=" "
691+ case " $agent " in
692+ claude) key_val=" ${ANTHROPIC_API_KEY:- } " ;;
693+ codex) key_val=" ${OPENAI_API_KEY:- } " ;;
694+ gemini) key_val=" ${GEMINI_API_KEY:- ${GOOGLE_API_KEY:- } } " ;;
695+ esac
696+ if [ -n " $key_val " ] && [ ${# key_val} -ge 4 ]; then
697+ printf ' %s' " api-key-${key_val: -4} "
698+ else
699+ printf ' %s' " api-key"
700+ fi
701+ ;;
702+ * )
703+ printf ' %s' " $( sanitize_slug_component " $auth_method " ) "
704+ ;;
705+ esac
706+ }
707+
708+ compute_shape_hash () {
709+ local image_ref=" $1 "
710+ local volume_input=" ${2:- } "
711+ local config_input=" ${3:- } "
712+ local combined=" $image_ref "
713+ [ -n " $volume_input " ] && combined=" ${combined} |${volume_input} "
714+ [ -n " $config_input " ] && combined=" ${combined} |${config_input} "
715+ short_hash " $combined " 8
716+ }
717+
718+ build_container_name () {
719+ local prefix=" $1 "
720+ local agent=" $2 "
721+ local auth_tag=" $3 "
722+ local slug=" $4 "
723+ local shape_hash=" $5 "
724+ local ephemeral=" ${6:- false} "
725+ local pid=" ${7:- } "
726+
727+ local name=" ${prefix} --${agent} --${auth_tag} --${slug} ..${shape_hash} "
728+ if [ " $ephemeral " = true ] && [ -n " $pid " ]; then
729+ name=" ${name} --${pid} "
730+ fi
731+ printf ' %s' " $name "
732+ }
733+
646734slug_candidates_for_path () {
647735 local path=" $1 "
648736 local parent project
@@ -740,15 +828,19 @@ project_container_rows() {
740828 return
741829 fi
742830
831+ local esc_prefix
832+ esc_prefix=$( printf ' %s' " $DEVA_CONTAINER_PREFIX " | sed -e ' s/[.[\\^$*+?{}()|]/\\&/g' )
743833 local pattern=" "
744834 local slug escaped
745835 for slug in $slugs ; do
746836 [ -n " $slug " ] || continue
747837 escaped=$( printf ' %s' " $slug " | sed -e ' s/[.[\\^$*+?{}()|]/\\&/g' )
838+ local new_fmt=" ^${esc_prefix} --.*--${escaped} ([.-]|$)"
839+ local old_fmt=" ^${esc_prefix} -${escaped} ([.-]|$)"
748840 if [ -n " $pattern " ]; then
749- pattern=" ${pattern} |- ${escaped} ([.-]|$ )"
841+ pattern=" ${pattern} |( ${new_fmt} )|( ${old_fmt} )"
750842 else
751- pattern=" - ${escaped} ([.-]|$ )"
843+ pattern=" ( ${new_fmt} )|( ${old_fmt} )"
752844 fi
753845 done
754846
@@ -769,15 +861,19 @@ project_container_rows() {
769861
770862extract_agent_from_name () {
771863 local name=" $1 "
772- local rest=" ${name# " ${DEVA_CONTAINER_PREFIX} " -} "
864+ local rest=" ${name# " ${DEVA_CONTAINER_PREFIX} " } "
865+
866+ if [[ " $rest " =~ ^--([a-z]+)-- ]]; then
867+ printf ' %s' " ${BASH_REMATCH[1]} "
868+ return
869+ fi
773870
774- # Ephemeral pattern: ends with -<agent>-<pid> where pid is all digits
775871 if [[ " $rest " =~ -([a-z]+)-([0-9]+)$ ]]; then
776- local agent=" ${BASH_REMATCH[1]} "
777- printf ' %s' " $agent "
778- else
779- printf ' share'
872+ printf ' %s' " ${BASH_REMATCH[1]} "
873+ return
780874 fi
875+
876+ printf ' share'
781877}
782878
783879pick_container () {
@@ -902,37 +998,33 @@ prepare_base_docker_args() {
902998 local slug
903999 slug=" $( generate_container_slug) "
9041000
905- local volume_hash =" "
1001+ local volume_input =" "
9061002 if [ ${# USER_VOLUMES[@]} -gt 0 ]; then
907- volume_hash =$( compute_volume_hash )
1003+ volume_input =$( printf ' %s\n ' " ${USER_VOLUMES[@]} " | sort | tr ' \n ' ' | ' )
9081004 fi
9091005
910- # Include config-home in container identity when explicit.
911- # Use CONFIG_HOME if set, else CONFIG_ROOT (root mode clears CONFIG_HOME).
912- local config_hash=" "
913- local config_hash_source=" "
1006+ local config_input=" "
9141007 if [ " $CONFIG_HOME_FROM_CLI " = true ]; then
9151008 if [ -n " $CONFIG_HOME " ]; then
916- config_hash_source =" $CONFIG_HOME "
1009+ config_input =" $CONFIG_HOME "
9171010 elif [ -n " $CONFIG_ROOT " ]; then
918- config_hash_source =" $CONFIG_ROOT "
1011+ config_input =" $CONFIG_ROOT "
9191012 fi
9201013 fi
921- [ -n " $config_hash_source " ] && config_hash=$( short_hash " $config_hash_source " 6)
9221014
923- local image_hash=" "
924- image_hash=$( short_hash " $( docker_image_ref) " 6)
1015+ local image_ref
1016+ image_ref=" $( docker_image_ref) "
1017+ local shape_hash
1018+ shape_hash=$( compute_shape_hash " $image_ref " " $volume_input " " $config_input " )
9251019
926- local suffix= " "
927- [ -n " $image_hash " ] && suffix= " ..i ${image_hash} "
928- [ -n " $volume_hash " ] && suffix= " ${suffix} ..v ${volume_hash} "
929- [ -n " $config_hash " ] && suffix= " ${suffix} ..c ${config_hash} "
1020+ local auth_tag= " auth-default "
1021+ container_name= $( build_container_name \
1022+ " $DEVA_CONTAINER_PREFIX " " $ACTIVE_AGENT " " $auth_tag " \
1023+ " $slug " " $shape_hash " " $EPHEMERAL_MODE " " $$ " )
9301024
9311025 if [ " $EPHEMERAL_MODE " = true ]; then
932- container_name=" ${DEVA_CONTAINER_PREFIX} -${slug}${suffix} -${ACTIVE_AGENT} -$$ "
9331026 DOCKER_ARGS=(run --rm " ${DOCKER_TERMINAL_ARGS[@]} " )
9341027 else
935- container_name=" ${DEVA_CONTAINER_PREFIX} -${slug}${suffix} "
9361028 DOCKER_ARGS=(run -d)
9371029 fi
9381030
@@ -947,7 +1039,6 @@ prepare_base_docker_args() {
9471039 --add-host host.docker.internal:host-gateway
9481040 )
9491041
950- # Attach labels to identify workspace and container grouping
9511042 local ws_hash
9521043 ws_hash=$( workspace_hash)
9531044 DOCKER_ARGS+=(
@@ -957,14 +1048,9 @@ prepare_base_docker_args() {
9571048 --label " deva.workspace_hash=${ws_hash} "
9581049 --label " deva.agent=${ACTIVE_AGENT} "
9591050 --label " deva.ephemeral=${EPHEMERAL_MODE} "
960- --label " deva.image=$( docker_image_ref) "
1051+ --label " deva.image=$image_ref "
1052+ --label " deva.shape_hash=${shape_hash} "
9611053 )
962- if [ -n " $volume_hash " ]; then
963- DOCKER_ARGS+=(--label " deva.volhash=${volume_hash} " )
964- fi
965- if [ -n " $image_hash " ]; then
966- DOCKER_ARGS+=(--label " deva.image_hash=${image_hash} " )
967- fi
9681054
9691055 if [ -n " ${LANG:- } " ]; then DOCKER_ARGS+=(-e " LANG=$LANG " ); fi
9701056 if [ -n " ${LC_ALL:- } " ]; then DOCKER_ARGS+=(-e " LC_ALL=$LC_ALL " ); fi
@@ -1427,12 +1513,12 @@ short_hash() {
14271513 local input=" $1 "
14281514 local length=" ${2:- 8} "
14291515
1430- if command -v md5sum > /dev/null 2>&1 ; then
1431- printf ' %s' " $input " | md5sum | cut -c1-" $length "
1516+ if command -v sha256sum > /dev/null 2>&1 ; then
1517+ printf ' %s' " $input " | sha256sum | cut -c1-" $length "
14321518 elif command -v shasum > /dev/null 2>&1 ; then
1433- printf ' %s' " $input " | shasum | cut -c1-" $length "
1519+ printf ' %s' " $input " | shasum -a 256 | cut -c1-" $length "
14341520 else
1435- printf ' %s' " $input " | cksum | cut -d ' ' -f1 | cut -c1-" $length "
1521+ printf ' %s' " $input " | md5sum | cut -c1-" $length "
14361522 fi
14371523}
14381524
@@ -2097,7 +2183,7 @@ if [ "$MANAGEMENT_MODE" = "shell" ] || [ "$MANAGEMENT_MODE" = "ps" ] || [ "$MANA
20972183 else
20982184 slug=" $( generate_container_slug) "
20992185 escaped_slug=$( printf ' %s' " $slug " | sed ' s/[.[\\^$*+?{}()|]/\\&/g' )
2100- rgx=" ^${DEVA_CONTAINER_PREFIX} - ${escaped_slug} (\\ .\\ .|-|$)"
2186+ rgx=" ^${DEVA_CONTAINER_PREFIX} (--.*-- ${escaped_slug} |- ${escaped_slug} ) (\\ .\\ .|-|$)"
21012187 all_names=$( docker ps -a --format ' {{.Names}}' )
21022188 matching_names=$( echo " $all_names " | grep -E -- " $rgx " || true)
21032189 fi
@@ -2164,7 +2250,7 @@ if [ "$MANAGEMENT_MODE" = "shell" ] || [ "$MANAGEMENT_MODE" = "ps" ] || [ "$MANA
21642250 if [ -z " $matching_stopped " ]; then
21652251 slug=" $( generate_container_slug) "
21662252 escaped_slug=$( printf ' %s' " $slug " | sed ' s/[.[\\^$*+?{}()|]/\\&/g' )
2167- rgx=" ^${DEVA_CONTAINER_PREFIX} - ${escaped_slug} (\\ .\\ .|-|$)"
2253+ rgx=" ^${DEVA_CONTAINER_PREFIX} (--.*-- ${escaped_slug} |- ${escaped_slug} ) (\\ .\\ .|-|$)"
21682254 all_stopped=$( docker ps -a --filter " status=exited" --format ' {{.Names}}' )
21692255 matching_stopped=$( echo " $all_stopped " | grep -E -- " $rgx " || true)
21702256 fi
@@ -2494,91 +2580,60 @@ else
24942580 agent_prepare
24952581fi
24962582
2497- # Update container name based on auth method
2498- if [ -n " ${AUTH_METHOD :- } " ] ; then
2499- # Determine if we need auth suffix
2500- needs_auth_suffix =false
2583+ # Rewrite container name now that AUTH_METHOD is known from agent_prepare().
2584+ # prepare_base_docker_args used auth-default; update if auth is non-default.
2585+ {
2586+ _needs_rewrite =false
25012587 _env_auth_override=false
2502- if [ " $ACTIVE_AGENT " = " claude" ] && [ " $AUTH_METHOD " != " claude" ]; then
2503- needs_auth_suffix=true
2504- elif [ " $ACTIVE_AGENT " = " codex" ] && [ " $AUTH_METHOD " != " chatgpt" ]; then
2505- needs_auth_suffix=true
2506- elif [ " $ACTIVE_AGENT " = " gemini" ] && [ " $AUTH_METHOD " != " oauth" ] && [ " $AUTH_METHOD " != " gemini-app-oauth" ]; then
2507- needs_auth_suffix=true
2508- fi
2509-
2510- # Env-var auth override: default method but auth env vars reaching container.
2511- # Container name must change when effective auth source changes.
2512- if [ " $needs_auth_suffix " = false ] && has_auth_override; then
2513- needs_auth_suffix=true
2514- _env_auth_override=true
2515- fi
25162588
2517- if [ " $needs_auth_suffix " = true ]; then
2518- slug=" $( generate_container_slug) "
2519- volume_hash=" "
2520- if [ ${# USER_VOLUMES[@]} -gt 0 ]; then
2521- volume_hash=$( compute_volume_hash)
2522- fi
2589+ if [ -n " ${AUTH_METHOD:- } " ]; then
2590+ case " ${ACTIVE_AGENT} :${AUTH_METHOD} " in
2591+ claude:claude|codex:chatgpt|gemini:oauth|gemini:gemini-app-oauth) ;;
2592+ * ) _needs_rewrite=true ;;
2593+ esac
25232594
2524- # Recompute config hash to preserve in auth-rewritten name
2525- auth_config_hash=" "
2526- auth_config_src=" "
2527- if [ " $CONFIG_HOME_FROM_CLI " = true ]; then
2528- if [ -n " $CONFIG_HOME " ]; then
2529- auth_config_src=" $CONFIG_HOME "
2530- elif [ -n " $CONFIG_ROOT " ]; then
2531- auth_config_src=" $CONFIG_ROOT "
2532- fi
2595+ if [ " $_needs_rewrite " = false ] && has_auth_override; then
2596+ _needs_rewrite=true
2597+ _env_auth_override=true
25332598 fi
2534- [ -n " $auth_config_src " ] && auth_config_hash= $( short_hash " $auth_config_src " 6 )
2599+ fi
25352600
2536- image_hash=$( short_hash " $( docker_image_ref) " 6)
2601+ if [ " $_needs_rewrite " = true ]; then
2602+ _rw_slug=" $( generate_container_slug) "
25372603
2538- # Hash credential file path for credentials-file auth
2539- creds_hash=" "
2540- if [ " $AUTH_METHOD " = " credentials-file" ] && [ -n " ${CUSTOM_CREDENTIALS_FILE:- } " ]; then
2541- creds_hash=$( short_hash " $CUSTOM_CREDENTIALS_FILE " 8)
2604+ _rw_vol_input=" "
2605+ if [ ${# USER_VOLUMES[@]} -gt 0 ]; then
2606+ _rw_vol_input=$( printf ' %s\n' " ${USER_VOLUMES[@]} " | sort | tr ' \n' ' |' )
25422607 fi
2543-
2544- new_container_name=" "
2545- if [ " $_env_auth_override " = true ]; then
2546- auth_suffix=" env"
2547- else
2548- auth_suffix=" ${AUTH_METHOD} "
2608+ _rw_cfg_input=" "
2609+ if [ " $CONFIG_HOME_FROM_CLI " = true ]; then
2610+ [ -n " $CONFIG_HOME " ] && _rw_cfg_input=" $CONFIG_HOME "
2611+ [ -z " $_rw_cfg_input " ] && [ -n " $CONFIG_ROOT " ] && _rw_cfg_input=" $CONFIG_ROOT "
25492612 fi
2550- [ -n " $creds_hash " ] && auth_suffix=" ${AUTH_METHOD} -${creds_hash} "
2551- auth_suffix=" ${auth_suffix} -${ACTIVE_AGENT} "
25522613
2553- # Build suffix chain: volume + config + auth
2554- name_suffix=" "
2555- [ -n " $image_hash " ] && name_suffix=" ..i${image_hash} "
2556- [ -n " $volume_hash " ] && name_suffix=" ${name_suffix} ..v${volume_hash} "
2557- [ -n " $auth_config_hash " ] && name_suffix=" ${name_suffix} ..c${auth_config_hash} "
2558- name_suffix=" ${name_suffix} ..${auth_suffix} "
2614+ _rw_shape=$( compute_shape_hash " $( docker_image_ref) " " $_rw_vol_input " " $_rw_cfg_input " )
2615+ _rw_auth_tag=$( generate_auth_tag " $ACTIVE_AGENT " " $AUTH_METHOD " " ${CUSTOM_CREDENTIALS_FILE:- } " " $_env_auth_override " )
25592616
2560- if [ " $EPHEMERAL_MODE " = true ]; then
2561- new_container_name=" ${DEVA_CONTAINER_PREFIX} -${slug}${name_suffix} -${ACTIVE_AGENT} -$$ "
2562- else
2563- new_container_name=" ${DEVA_CONTAINER_PREFIX} -${slug}${name_suffix} "
2564- fi
2617+ _rw_name=$( build_container_name \
2618+ " $DEVA_CONTAINER_PREFIX " " $ACTIVE_AGENT " " $_rw_auth_tag " \
2619+ " $_rw_slug " " $_rw_shape " " $EPHEMERAL_MODE " " $$ " )
25652620
2566- # Update container name in DOCKER_ARGS
25672621 for (( i = 0 ; i < ${# DOCKER_ARGS[@]} ; i++ )) ; do
25682622 if [ " ${DOCKER_ARGS[$i]} " = " --name" ] && [ $(( i + 1 )) -lt ${# DOCKER_ARGS[@]} ]; then
2569- DOCKER_ARGS[i + 1]=" $new_container_name "
2623+ DOCKER_ARGS[i + 1]=" $_rw_name "
25702624 break
25712625 fi
25722626 done
2573- fi
25742627
2575- # Label auth method for easier filtering
2576- DOCKER_ARGS+=(--label " deva.auth= ${AUTH_METHOD} " )
2628+ DOCKER_ARGS+=(--label " deva.auth_tag= ${_rw_auth_tag} " )
2629+ fi
25772630
2578- # Export container introspection variables
2579- DOCKER_ARGS+=(-e " DEVA_AUTH_METHOD=${AUTH_METHOD} " )
2580- [ -n " ${AUTH_DETAILS:- } " ] && DOCKER_ARGS+=(-e " DEVA_AUTH_DETAILS=${AUTH_DETAILS} " )
2581- fi
2631+ if [ -n " ${AUTH_METHOD:- } " ]; then
2632+ DOCKER_ARGS+=(--label " deva.auth=${AUTH_METHOD} " )
2633+ DOCKER_ARGS+=(-e " DEVA_AUTH_METHOD=${AUTH_METHOD} " )
2634+ [ -n " ${AUTH_DETAILS:- } " ] && DOCKER_ARGS+=(-e " DEVA_AUTH_DETAILS=${AUTH_DETAILS} " )
2635+ fi
2636+ }
25822637
25832638# Determine container name early for env injection
25842639CONTAINER_NAME= " "
0 commit comments