import type { LocalContext } from '../runtime/context'; // Bash is the interpreter for user-owned files. The CLI supplies paths/argv as // separate arguments; no user argument is interpolated into this fixed bridge. export function shellSessionArguments( context: LocalContext, argv: string[], scriptArgs: string[], command = false, timeoutMarker = '', ): string[] { const scriptIndex = command ? argv.findIndex((arg) => /\.(?:js|mjs|py|pyc|sh|ts)$/.test(arg)) : 0; const bridge = ` __ql_env=$1; __ql_before=$2; __ql_after=$3; __ql_command=$4; __ql_index=$5; __ql_count=$6; __ql_timeout=$7; shift 7 __ql_hook_args=( "\${@:1:__ql_count}" ); shift "$__ql_count" __ql_script_args=( "$@" ) # Reserve an internal descriptor before sourcing user code. In particular, fd 3 # and fd 9 remain available for user redirections and locks. Bash 3 also supports # this numeric descriptor, unlike Bash 4's dynamic descriptor syntax. if [ -n "$__ql_timeout" ]; then exec 19>&3 3>&-; fi __ql_after_started=false __ql_run_after() { __ql_after_started=true if [ -n "$__ql_timeout" ]; then printf A >&19; fi if [ "$__ql_nounset" = true ]; then set -u; fi export NODE_PATH="\${PREV_NODE_PATH:-}" unset QL_NODE_GLOBAL_PATH if [ -f "$__ql_after" ]; then . "$__ql_after" "\${__ql_hook_args[@]}"; fi if [ -n "\${task_after:-}" ]; then eval "\${task_after%;}"; fi } __ql_timeout_exit() { if [ -f "$__ql_timeout" ] && [ "$__ql_after_started" = false ]; then _task_exit_code=124 __ql_run_after exit 124 fi } if [ -f "$__ql_env" ]; then . "$__ql_env"; fi if [ -f "$__ql_before" ]; then . "$__ql_before" "\${__ql_hook_args[@]}"; fi if [ -n "\${task_before:-}" ]; then eval "\${task_before%;}"; fi __ql_nounset=false case $- in *u*) __ql_nounset=true; set +u;; esac if [ -n "\${__ql_selected_name:-}" ]; then printf -v "$__ql_selected_name" '%s' "$__ql_selected_value" export "$__ql_selected_name" fi unset __ql_selected_name __ql_selected_value __ql_args=( "\${__ql_hook_args[@]}" ) if [ "$__ql_index" -lt 0 ]; then __ql_index=0; fi __ql_file=\${__ql_args[__ql_index]} __ql_resolve=false cd "$dir_scripts" || exit 1 if [ -n "\${work_dir:-}" ] && [ -d "$work_dir" ]; then cd "$work_dir" || exit 1 __ql_resolve=true elif [[ "$__ql_file" == */* ]] && [ -d "\${__ql_file%/*}" ]; then cd "\${__ql_file%/*}" || exit 1 __ql_resolve=true fi if [ "$__ql_resolve" = true ] && [[ "$__ql_file" == */* ]]; then __ql_args[__ql_index]="./\${__ql_file##*/}" fi if [ -n "$__ql_timeout" ]; then # Preserve a user-owned EXIT trap. If it prevents our cleanup, the parent # performs the fallback after the process group has stopped. if [ -z "$(trap -p EXIT)" ]; then trap '__ql_timeout_exit' EXIT; fi printf T >&19 fi if [ "$__ql_command" = true ]; then "\${__ql_args[@]}" "\${__ql_script_args[@]}" _task_exit_code=$? else # Match task.sh: keep script exit/exec/options inside a child shell while # retaining before-hook variables and functions for the script. # Keep the supervisor alive until the child's signal/EXIT traps finish, but # do not turn user cancellation into an ordinary completion with after hooks. __ql_saved_signals=$(trap -p INT TERM HUP QUIT ALRM TSTP) __ql_cancel_code='' trap '__ql_cancel_code=$((128 + $(kill -l INT)))' INT trap '__ql_cancel_code=$((128 + $(kill -l TERM)))' TERM trap '__ql_cancel_code=$((128 + $(kill -l HUP)))' HUP trap '__ql_cancel_code=$((128 + $(kill -l QUIT)))' QUIT trap '__ql_cancel_code=$((128 + $(kill -l ALRM)))' ALRM trap '__ql_cancel_code=$((128 + $(kill -l TSTP)))' TSTP ( . "\${__ql_args[0]}" "\${__ql_script_args[@]}" ) _task_exit_code=$? trap - INT TERM HUP QUIT ALRM TSTP eval "$__ql_saved_signals" if [ -n "$__ql_cancel_code" ] && ! [ -f "$__ql_timeout" ]; then exit "$__ql_cancel_code"; fi fi if [ -n "$__ql_timeout" ] && [ -f "$__ql_timeout" ]; then _task_exit_code=124; fi __ql_run_after exit "$_task_exit_code" `; return [ '--noprofile', '--norc', '-c', bridge, 'ql-shell-session', context.paths.file_env!, context.paths.file_task_before!, context.paths.file_task_after!, String(command), String(scriptIndex), String(argv.length), timeoutMarker, ...argv, ...(command ? [] : argv.slice(1)), ...scriptArgs, ]; }