@@ -226,6 +226,84 @@ is_file_path() {
226226 [[ " $arg " == /* ]] || [[ " $arg " == ~ * ]] || [[ " $arg " == ./* ]] || [[ " $arg " == ../* ]] || [[ " $arg " == * .json ]]
227227}
228228
229+ # Publish the cctrace live UI (container port 9317, binds 0.0.0.0) to the
230+ # host loopback so the browser can reach it. Probe host ports from 9317 so
231+ # concurrent traced containers land on predictable neighbors (#425).
232+ DEVA_TRACE_UI_URL=" "
233+ setup_trace_ui_port () {
234+ local port=9317
235+ local tries=0
236+ local free_port=" "
237+ while [ " $tries " -lt 12 ]; do
238+ if ! (exec 3<> " /dev/tcp/127.0.0.1/$port " ) 2> /dev/null; then
239+ free_port=" $port "
240+ break
241+ fi
242+ port=$(( port + 1 ))
243+ tries=$(( tries + 1 ))
244+ done
245+
246+ if [ -z " $free_port " ]; then
247+ echo " warning: no free host port in 9317-9328; trace UI will not be reachable from the host" >&2
248+ return 0
249+ fi
250+
251+ DOCKER_ARGS+=(" -p" " 127.0.0.1:${free_port} :9317" )
252+ DEVA_TRACE_UI_URL=" http://127.0.0.1:${free_port} "
253+ }
254+
255+ # Print the trace UI URL before the TUI takes the screen, and open the host
256+ # browser the moment the UI answers. "new" trusts DEVA_TRACE_UI_URL (the
257+ # container doesn't exist yet); "existing" asks docker for the live mapping.
258+ announce_trace_ui () {
259+ [ " ${DEVA_TRACE_ACTIVE:- false} " = true ] || return 0
260+ [ " ${DRY_RUN:- false} " = true ] && return 0
261+
262+ local url=" $DEVA_TRACE_UI_URL "
263+ if [ " ${1:- new} " = " existing" ]; then
264+ # docker port exits non-zero for unpublished mappings; under
265+ # set -euo pipefail that must not kill the launch.
266+ local mapping
267+ mapping=$( docker port " $CONTAINER_NAME " 9317/tcp 2> /dev/null | head -1 || true)
268+ if [ -z " $mapping " ]; then
269+ echo " warning: trace UI not reachable — container was created without the trace port; recreate it (deva rm) or use --rm" >&2
270+ return 0
271+ fi
272+ url=" http://127.0.0.1:${mapping##*: } "
273+ fi
274+ [ -n " $url " ] || return 0
275+
276+ echo " Trace UI: $url (live once the agent starts)" >&2
277+ maybe_open_trace_ui " $url "
278+ }
279+
280+ # Poll-then-open in the background: the browser opens exactly when the UI
281+ # is up, never against a connection-refused page. DEVA_TRACE_OPEN=0 disables.
282+ maybe_open_trace_ui () {
283+ local url=" $1 "
284+ [ " ${DEVA_TRACE_OPEN:- 1} " = " 1" ] || return 0
285+
286+ local opener=" "
287+ if command -v open > /dev/null 2>&1 ; then
288+ opener=" open"
289+ elif command -v xdg-open > /dev/null 2>&1 ; then
290+ opener=" xdg-open"
291+ fi
292+ [ -n " $opener " ] || return 0
293+
294+ (
295+ local i=0
296+ while [ " $i " -lt 60 ]; do
297+ if curl -sf -o /dev/null --max-time 1 " $url /" 2> /dev/null; then
298+ " $opener " " $url " > /dev/null 2>&1 || true
299+ exit 0
300+ fi
301+ sleep 0.5
302+ i=$(( i + 1 ))
303+ done
304+ ) > /dev/null 2>&1 &
305+ }
306+
229307AUTH_PROVISION_MODE=false
230308
231309expand_auth_file_tilde () {
0 commit comments