vscode-remote-release
vscode-remote-release copied to clipboard
code command not working in an external terminal or tmux
- VSCode Version: 1.44.1
- Local OS Version: Ubuntu 20.04
- Remote OS Version: Ubuntu 18.04
- Remote Extension/Connection Type: SSH
Steps to Reproduce:
- Connect to a remote server via Remote SSH
- Open the terminal pane and start a tmux server
- Run
codeand it saysCommand is only available in WSL or inside a Visual Studio Code terminal.
Does this issue occur when you try this locally?: No Does this issue occur when you try this locally and all extensions are disabled?: Yes
I have found that the terminal work if from the terminal execute the next command for open vscode, you could try with code --verbose
➜ qr_hfe git:(master) code -s
Unable to connect to VS Code server.
Error in request
➜ qr_hfe git:(master) code -v
1.45.1
5763d909d5f12fe19f215cbfdd29a91c0fa9208a
x64
➜ qr_hfe git:(master) code --verbose
Ignoring option verbose: not supported for code.
At least one file or folder must be provided.
I use the following workaround
cat /usr/bin/vscode_fix_path
#!/bin/bash
OLD_CSUM=`echo $PATH | grep -oP "(?<=\/home\/$USER\/.vscode-server-insiders\/bin\/).*?(?=\/bin)" | head -1`
NEW_CSUM=`ls -tr /home/$USER/.vscode-server-insiders/bin/ | tail -n 1`
export PATH=`echo $PATH | sed "s/$OLD_CSUM/$NEW_CSUM/g"`
export GIT_ASKPASS=`echo $GIT_ASKPASS | sed "s/$OLD_CSUM/$NEW_CSUM/g"`
export VSCODE_GIT_ASKPASS_MAIN=`echo $VSCODE_GIT_ASKPASS_MAIN | sed "s/$OLD_CSUM/$NEW_CSUM/g"`
export VSCODE_GIT_ASKPASS_NODE=`echo $VSCODE_GIT_ASKPASS_NODE | sed "s/$OLD_CSUM/$NEW_CSUM/g"`
export VSCODE_IPC_HOOK_CLI=`ls -tr /tmp/vscode-ipc-* | tail -n 1`
cat /usr/bin/vscode_shell
#!/bin/zsh
tmux new-session -A -D -s <SESSION>
tmux list-windows -t <SESSION> | cut -d: -f1|xargs -I{} tmux send-keys -t $session:{} ENTER "source /usr/bin/vscode_fix_path" ENTER
return 0
And in remote ssh settings "terminal.integrated.shell.linux": "/usr/bin/vscode_shell",
I have the same problem but with Extension/Connection Type: Containers. I'm happy to open up a different issue if anyone feels it is unrelated. I imagine it's the same root problem, though.
I had a similar problem with remote WSL (code either wouldn't work or would open in a new window) which is also fixed by resetting the environment variables. I found that in some cases @PavanNikhilesh's script didn't work due to it finding the wrong files but I think I found a pretty simple solution for updating the environment variables.
In .tmux.conf:
# update VSCODE variables from integrated terminal so that `code` command opens in current window
set-option -ga update-environment ' VSCODE_GIT_ASKPASS_NODE VSCODE_GIT_ASKPASS_MAIN VSCODE_IPC_HOOK_CLI PATH GIT_ASKPASS'
vscode_fix_path.sh
#!/bin/bash
export "`tmux showenv PATH`"
export "`tmux showenv GIT_ASKPASS`"
export "`tmux showenv VSCODE_GIT_ASKPASS_MAIN`"
export "`tmux showenv VSCODE_GIT_ASKPASS_NODE`"
export "`tmux showenv VSCODE_IPC_HOOK_CLI`"
update-environment will make sure tmux has the right environment when making new windows/panes and then we can use the previous idea of having a script update the already running shells.
This is a pretty common problem for various SSH variables as well so there's a bit written about it.
I just run the script myself since I have persistent applications and it's not too hard to fix the shells when I notice a problem.
I came up with a reliable workaround:
-
In the login shell (i.e. not within tmux), run
nc localhost 9876 -lk | while read filename; do code "$filename"; done & -
In any of your tmux windows, you can run
echo filename | nc localhost 9876
You can streamline this by adding #1 to your bashrc, and creating an alias for #2.
The biggest caveat is that the filename needs to be relative to your login shell's current directory.
I just ran into this myself. There's probably no way for this to be fixed automatically by vscode but maybe this caveat could be documented somewhere alongside with a suggested fix similar to what @melink14 posted above? It looks like the cleanest solution so far.
Any updates on this? I still have the need to call code filename from tmux in my current workflow.
I found the most convenient solution! Both and zsh can work.
Add the following content to ~/.bashrc or ~/.zshrc
socket=$(ls -1t /run/user/$UID/vscode-ipc-*.sock 2> /dev/null | head -1)
export VSCODE_IPC_HOOK_CLI=${socket}
Reference: https://stackoverflow.com/questions/62201080/is-it-possible-to-use-the-code-command-in-sshed-terminal-to-open-vs-code-on-l https://gist.github.com/ianloic/ddd8105da589d10760638af6f5306ae7
Here's a funny screenshot where I have two tmux panes side by side, with identical environment variables. The left one cannot call code, while the right one can. The left pane was created in an earlier Remote SSH session, while the right is newer and created in the current Remote SSH session.
Here is a list of the environment variables that differ between the left and right panes. Nothing seems relevant to code:
[2:zeus:~]% diff left.env right.env | grep -Ee '^(<|>)' | cut -d= -f 1 | cut -d" " -f2 | sort -u
'$'
LINENO
MATCH
MBEGIN
MEND
MENUSELECTOLDPWD
pipestatus
RANDOM
SECONDS
sz
TMUX_PANE
TTY
TTYIDLE
ZLE_LINE_ABORTED
I doesn't seem like code is deciding it is unavailable based on the environment, it must be getting information from somewhere else, like xterm.js or some shell integration escape codes (although I have shell integration disabled):
"terminal.integrated.environmentChangesIndicator": "on",
"terminal.integrated.env.linux": {},
"terminal.integrated.environmentChangesRelaunch": false,
"terminal.integrated.inheritEnv": false,
"terminal.integrated.enablePersistentSessions": false,
"terminal.integrated.shellIntegration.enabled": false,
Any ideas on what could be going on or possible fixes are welcome.
I hope this will work and is simplest:
alias code="${VSCODE_GIT_ASKPASS_NODE%/*}/bin/remote-cli/code"
I hope this will work and is simplest:
alias code="${VSCODE_GIT_ASKPASS_NODE%/*}/bin/remote-cli/code"
This saves my day. Thanks a lot.
The previous solutions don't work for me. The directory /run/user/$UID does not exist, and ${VSCODE_GIT_ASKPASS_NODE%/*}/bin/remote-cli/code is not found. But I got the key to the solution, which is to set the correct value to env VSCODE_IPC_HOOK_CLI.
Paste code below in your .bashrc, then open a new terminal within your current vscode, execute cmd mcw TMUX_SESSION_NAME, then the code in tmux works fine~
function make_code_work() {
# make code work in tmux
if [ -z "$1" ]; then
echo "Provide a tmux session name"
return 1
fi
SESSION_NAME=$1
t=$(set | grep VSCODE_IPC_HOOK_CLI= | head -n 1 | cut -d'=' -f2)
if [ -z "$t" ]; then
echo "VSCODE_IPC_HOOK_CLI is not set. Are you running this in a VS Code terminal?"
return 1
fi
echo "Socket to your current vscode: $t"
cmd="export VSCODE_IPC_HOOK_CLI=$t"
echo cmd=$cmd
tmux list-windows -t $SESSION_NAME -F '#{window_id}' | while read WINDOW_ID; do
tmux list-panes -t $WINDOW_ID -F '#{pane_id}' | while read PANE_ID; do
echo "Executing command in window $WINDOW_ID, pane $PANE_ID"
tmux send-keys -t $PANE_ID "$cmd" C-m
done
done
}
alias mcw='make_code_work'
https://github.com/microsoft/vscode-remote-release/issues/2763#issuecomment-2124135704
Building on this answer, I added an update to the tmux environment for the session (for new panes that are created), and I added checks to only run outside of tmux in the vscode terminal (where the variable should be correct) and only when the pane has one process (to avoid setting the variable on remote connections, probably a better way as this will skip panes running jobs)
function tmux-session-fix() {
local session_id=$1
local socket_name="$2"
if [ -z "${session_id}" ]; then
echo "Usage: tmux-session-fix <session_id> ?<socket_name>"
return 1
fi
if [ -n "${socket_name}" ]; then
local socket_path="/tmp/tmux-${UID}/${socket_name}"
else
local socket_path="/tmp/tmux-${UID}/default"
fi
if [ "${TERM_PROGRAM}" != "vscode" ]; then
echo "This command is only meant to be run outside of tmux, in the VS Code shell. Returning."
return 1
fi
local vscode_ipc_hook_cli; vscode_ipc_hook_cli=$(set | grep VSCODE_IPC_HOOK_CLI= | head -n1 | cut -d'=' -f2)
if [ -z "${vscode_ipc_hook_cli}" ]; then
echo "VSCODE_IPC_HOOK_CLI is not set. Are you running this in a VS Code terminal? Returning."
return 1
fi
cmd_vscode_ipc_hook_cli="export VSCODE_IPC_HOOK_CLI=${vscode_ipc_hook_cli}"
echo "Command: ${cmd_vscode_ipc_hook_cli}"
# Set tmux environment variable for new panes
tmux -S "${socket_path}" set-environment -t "${session_id}" VSCODE_IPC_HOOK_CLI "${vscode_ipc_hook_cli}"
# Update all panes that only have one process running
tmux -S "${socket_path}" list-windows -t="${session_id}" -F "#{window_id}" \
| while read -r window_id; do
tmux -S "${socket_path}" list-panes -t="${window_id}" -F '#{pane_id} #{pane_tty}' \
| while read -r pane_id pane_tty; do
local pid_count; pid_count=$(ps -t "${pane_tty}" -o pid= | wc -l)
if [ "${pid_count}" -gt 1 ]; then
echo "Pane ${pane_id} has more than one process running. Skipping"
continue
else
echo "Executing command in socket: ${socket_path}, session: ${session_id}, window: ${window_id}, pane: ${pane_id}"
tmux -S "${socket_path}" send-keys -t "${pane_id}" "${cmd_vscode_ipc_hook_cli}" C-m
fi
done
done
}
This is my second time investigating this problem; the first time I was put off by both a) complexity in getting a correct value for VSCODE_IPC_HOOK_CLI, b) really bad complexity trying to change the environment for existing and new tmux panes. I didn't solve it as a result, and coming back to it, I see more of the same. So I said "to hell with it" and went caveman on the problem by just adding this to my zshrc:
if [[ $TERM_PROGRAM == "vscode" ]]; then
echo $VSCODE_IPC_HOOK_CLI >! ${ZDOTDIR}/vscode_ipc.txt
fi
alias code='VSCODE_IPC_HOOK_CLI=$(cat $ZDOTDIR/vscode_ipc.txt) code'
Yes, you have to use a file. But this solution is dead simple, easy to understand and debug, and seems to always work so far.
Edit: I will mention, if you set your vscode terminal profile as launching tmux directly, then I'm not 100% sure it will work, or it may depend which exact startup file you put this in, etc. For me personally, I prefer to have vscode start a zsh terminal, and then I run tmux myself - just to have control of whether I'm attaching, or spawning a new client, etc - I didn't really see much point in automating that. If you use that setup, then I'm pretty sure this will work well.
Edit 2: it occurred to me at some point that if you were running multiple vscode windows, you'd run into issues with this approach. I tend not to do this; if you do do this then you'll have to commit to more complex approaches, as at that point you will need to have a way to "tell" a particular tmux session which vscode GUI to be associated with; the previous two solutions seem promising for this.
I seem to keep coming back to this problem, as I encounter small edge cases with existing solutions :-). My previous solution had some of the limitations I mentioned. I think this new solution works in all cases, including with multiple vscode windows. It's basically a combination of the idea stated by others to use tmux update-env, and my idea of changing the code command itself to "pull" the correct value (which is a lot easier than "pushing" it).
The first step is just to add this to your tmux.conf
set-option -ga update-environment VSCODE_IPC_HOOK_CLI
The second step is to add this function to your .zshrc or wherever
code() {
local vscode_ipc=$(tmux show-env VSCODE_IPC_HOOK_CLI | cut -d '=' -f2) 2>/dev/null
if [[ -v TMUX && vscode_ipc != "" ]]; then
VSCODE_IPC_HOOK_CLI=$vscode_ipc command code "$@"
else
command code "$@"
fi
}
The first step ensures that every time you spawn or attach a tmux session, VSCODE_IPC_HOOK_CLI's value will be copied into the tmux session's environment - note that this does not change the environment variables of existing panes! Then, instead of trying to push this variable to all the panes which is quite error prone, we just write a small function code function which grabs the value of VSCODE_IPC_HOOK_CLI from the current session, instead of using the environment variable. If the value is not available, then you are not currently attached to a vscode window. If the value is available and we're inside tmux we use that value instead of what the environment provides.
Outside of attaching to a tmux session both inside and outside vscode simultaneously, I can't think of any other situation this would break down in, and I do think this is the best solution so far, for the specific case of running tmux inside vscode.
Can't believe how hard it is to make this work, and in the end I still have one grievance, which is that a cli.js file keeps opening on its own, since I can't get tmux to correctly communicate with VSCode on Windows through a socket.
Currently, I make it work by having this on my .tmux.conf:
# inherit environment variables from wsl
set-option -g update-environment "\
PATH \
WSLENV \
DISPLAY \
XAUTHORITY \
SSH_AUTH_SOCK \
DBUS_SESSION_BUS_ADDRESS \
VSCODE_IPC_HOOK_CLI \
VSCODE_GIT_ASKPASS_NODE \
VSCODE_GIT_ASKPASS_MAIN \
GIT_ASKPASS \
LANG \
LC_ALL"
And this on my .zshrc:
# override the "code" command to always include the --remote flag for Ubuntu
code() {
if [[ "$1" = "." ]]; then
# When using ".", expand it to the current directory.
command code --remote wsl+Ubuntu "$(pwd)"
else
command code --remote wsl+Ubuntu "$@"
fi
}
Then I can use code . within a tmux session, and it will open VSCode and remote connect to WSL.
Posting my own solution to this in case it helps others. My issue was that upon re-attaching to tmux (or maybe some other trigger), my code command stopped working:
Error: connect ECONNREFUSED /run/user/79563/vscode-ipc-f69e973c-6565-42a7-a98b-975c098eea04.sock
at PipeConnectWrap.afterConnect [as oncomplete] (node:net:1611:16) {
errno: -111,
code: 'ECONNREFUSED',
syscall: 'connect',
address: '/run/user/79563/vscode-ipc-f69e973c-6565-42a7-a98b-975c098eea04.sock'
}
The issue happens because of the VSCODE_IPC_HOOK_CLI env var being set upon connecting via ssh in VSCode, but that env var not being propagated to open shells in existing tmux sessions. I'm not an expert but I think what's happening is a new socket connection is being created upon ssh to the VSCode Server, while the old socket eventually expires (or something like that). Open shells for existing sessions continue to use the expired socket causing the code command to fail. My solution was adding the following to my .bashrc file:
export_vscode_env() {
local env_file="${1:-/tmp/vscode_env.sh}"
echo "export VSCODE_IPC_HOOK_CLI=\"$VSCODE_IPC_HOOK_CLI\"" > "$env_file"
echo "VSCODE_IPC_HOOK_CLI exported to $env_file"
}
# Import VSCODE_IPC_HOOK_CLI from a file
import_vscode_env() {
local env_file="${1:-/tmp/vscode_env.sh}"
if [[ -f "$env_file" ]]; then
source "$env_file"
else
echo "Error: Environment file $env_file not found"
return 1
fi
}
tmux() {
export_vscode_env
command tmux "$@"
if [ -n "$TMUX" ]; then
tmux set-environment VSCODE_IPC_HOOK_CLI "$VSCODE_IPC_HOOK_CLI"
fi
}
That'll set the environment variable for code anytime you call the code command (Note that setting the environment variable in for example, .tmux.conf via set-option -g update-environment only allows new sessions/windows/panes to inherit the new value. It doesn't do anything for existing sessions). That's why I overrode my code command in tmux to source the env var containing the updated VSCODE_IPC_HOOK_CLI value. Now anytime I call code it'll automatically pick up the correct value.
I really think VSCode developers should take a look at this in depth. It's a very annoying problem and not obvious to solve unless you spend a lot of time understanding what VSCode does under the hood (it took me a lot of searching to even figure out the problem was with $VSCODE_IPC_HOOK_CLI). Hope this helps anyone who was as frustrated as me trying to get this to work.
Instead of doing the env.sh file, you could just do source tmux show-environment -s I have that command running as a prexec hook. That seems to work just fine for me.
My fish hook:
function refresh_tmux_vars --on-event="fish_preexec"
if set -q TMUX
bass (tmux show-environment -s)
end
end