Skip to content

Commit 09886e9

Browse files
committed
Added script to auto connect vscode to remote linkspan
1 parent 50278dd commit 09886e9

File tree

2 files changed

+270
-0
lines changed

2 files changed

+270
-0
lines changed

scripts/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
frpc
2+
frpc.toml
Lines changed: 268 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,268 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
usage() {
5+
cat <<'EOF'
6+
Usage:
7+
./create_hpc_vscode_session.sh <base_url> <token> [options]
8+
9+
Required:
10+
base_url e.g. https://example.com
11+
token auth token used in header: X-Tunnel-Authorization: tunnel <token>
12+
13+
Options:
14+
--tunnel-secret VALUE (default: abc)
15+
--discovery-host VALUE (default: hub.dev.cybershuttle.org)
16+
--discovery-port VALUE (default: 7000)
17+
--discovery-token VALUE (default: mysecret)
18+
--password VALUE (optional; included in POST body if provided)
19+
--tunnel-type VALUE (default: xtcp)
20+
--bind-port VALUE (optional; override bind port for visitor config)
21+
22+
Examples:
23+
./create_hpc_vscode_session.sh https://example.com TOKEN \
24+
--tunnel-secret abc \
25+
--discovery-host hub.dev.cybershuttle.org \
26+
--discovery-port 7000 \
27+
--discovery-token mysecret \
28+
--password pass123
29+
EOF
30+
}
31+
32+
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
33+
usage
34+
exit 0
35+
fi
36+
37+
if [[ $# -lt 2 ]]; then
38+
usage >&2
39+
exit 1
40+
fi
41+
42+
BASE_URL="${1%/}"
43+
TOKEN="$2"
44+
shift 2
45+
46+
# Defaults
47+
TUNNEL_SECRET="abc"
48+
DISCOVERY_HOST="hub.dev.cybershuttle.org"
49+
DISCOVERY_PORT="7000"
50+
DISCOVERY_TOKEN="mysecret"
51+
PASSWORD="test"
52+
TUNNEL_TYPE="xtcp"
53+
BIND_PORT=""
54+
55+
# Parse flags
56+
while [[ $# -gt 0 ]]; do
57+
case "$1" in
58+
--tunnel-secret)
59+
TUNNEL_SECRET="${2:-}"; shift 2 ;;
60+
--discovery-host)
61+
DISCOVERY_HOST="${2:-}"; shift 2 ;;
62+
--discovery-port)
63+
DISCOVERY_PORT="${2:-}"; shift 2 ;;
64+
--discovery-token)
65+
DISCOVERY_TOKEN="${2:-}"; shift 2 ;;
66+
--password)
67+
PASSWORD="${2:-}"; shift 2 ;;
68+
--tunnel-type)
69+
TUNNEL_TYPE="${2:-}"; shift 2 ;;
70+
--bind-port)
71+
BIND_PORT="${2:-}"; shift 2 ;;
72+
*)
73+
echo "Unknown option: $1" >&2
74+
usage >&2
75+
exit 1
76+
;;
77+
esac
78+
done
79+
80+
if [[ -z "$DISCOVERY_PORT" || ! "$DISCOVERY_PORT" =~ ^[0-9]+$ ]]; then
81+
echo "Error: --discovery-port must be a number" >&2
82+
exit 1
83+
fi
84+
85+
# Dependencies: jq preferred; fallback to python3
86+
have_jq=0
87+
if command -v jq >/dev/null 2>&1; then
88+
have_jq=1
89+
elif ! command -v python3 >/dev/null 2>&1; then
90+
echo "Error: need either 'jq' or 'python3' installed to parse JSON." >&2
91+
exit 1
92+
fi
93+
94+
auth_header="X-Tunnel-Authorization: tunnel ${TOKEN}"
95+
96+
# Build session request body
97+
if [[ $have_jq -eq 1 ]]; then
98+
session_body="$(jq -n --arg password "$PASSWORD" '{password: $password, mount_user_home: false}')"
99+
else
100+
pw_esc="$(printf '%s' "$PASSWORD" | python3 -c 'import json,sys; print(json.dumps(sys.stdin.read())[1:-1])')"
101+
session_body="{\"password\":\"${pw_esc}\",\"mount_user_home\":false}"
102+
fi
103+
104+
# 1) Create VSCode session -> get bind_port
105+
session_resp="$(
106+
curl -sS -X POST \
107+
-H "$auth_header" \
108+
-H "Content-Type: application/json" \
109+
"${BASE_URL}/api/v1/vscode/sessions" \
110+
-d "$session_body"
111+
)"
112+
113+
if [[ $have_jq -eq 1 ]]; then
114+
bind_port="$(printf '%s' "$session_resp" | jq -r '.bind_port // empty')"
115+
else
116+
bind_port="$(python3 - <<'PY'
117+
import json,sys
118+
d=json.loads(sys.stdin.read())
119+
print(d.get("bind_port",""))
120+
PY
121+
<<<"$session_resp")"
122+
fi
123+
124+
if [[ -z "${bind_port}" || ! "${bind_port}" =~ ^[0-9]+$ ]]; then
125+
echo "Error: failed to parse bind_port from response:" >&2
126+
echo "$session_resp" >&2
127+
exit 1
128+
fi
129+
130+
# Random tunnel name: frptunnel-<random>
131+
rand_suffix="$(LC_ALL=C tr -dc '0-9' </dev/urandom | head -c 6 || true)"
132+
if [[ -z "$rand_suffix" ]]; then
133+
rand_suffix="$RANDOM$RANDOM"
134+
fi
135+
tunnel_name="frptunnel-${rand_suffix}"
136+
137+
# Build JSON body (include password only if provided)
138+
json_escape_py='import json,sys; print(json.dumps(sys.stdin.read())[1:-1])'
139+
esc() { python3 -c "$json_escape_py"; }
140+
141+
# Use jq to build JSON safely if available; else build carefully with python escaping for strings
142+
if [[ $have_jq -eq 1 ]]; then
143+
tunnel_body="$(
144+
jq -n \
145+
--arg tunnelName "$tunnel_name" \
146+
--arg tunnelType "$TUNNEL_TYPE" \
147+
--arg tunnelSecret "$TUNNEL_SECRET" \
148+
--arg discoveryHost "$DISCOVERY_HOST" \
149+
--arg discoveryToken "$DISCOVERY_TOKEN" \
150+
--argjson port "$bind_port" \
151+
--argjson discoveryPort "$DISCOVERY_PORT" \
152+
'{
153+
tunnelName: $tunnelName,
154+
port: $port,
155+
tunnelType: $tunnelType,
156+
tunnelSecret: $tunnelSecret,
157+
discoveryHost: $discoveryHost,
158+
discoveryPort: $discoveryPort,
159+
discoveryToken: $discoveryToken
160+
}'
161+
)"
162+
else
163+
# Escape strings using python3, keep numbers numeric
164+
tn_esc="$(printf '%s' "$tunnel_name" | esc)"
165+
tt_esc="$(printf '%s' "$TUNNEL_TYPE" | esc)"
166+
ts_esc="$(printf '%s' "$TUNNEL_SECRET" | esc)"
167+
dh_esc="$(printf '%s' "$DISCOVERY_HOST" | esc)"
168+
dt_esc="$(printf '%s' "$DISCOVERY_TOKEN" | esc)"
169+
tunnel_body="$(cat <<JSON
170+
{
171+
"tunnelName": "${tn_esc}",
172+
"port": ${bind_port},
173+
"tunnelType": "${tt_esc}",
174+
"tunnelSecret": "${ts_esc}",
175+
"discoveryHost": "${dh_esc}",
176+
"discoveryPort": ${DISCOVERY_PORT},
177+
"discoveryToken": "${dt_esc}"
178+
}
179+
JSON
180+
)"
181+
fi
182+
183+
# 2) Create FRP tunnel
184+
tunnel_resp="$(
185+
curl -sS -X POST \
186+
-H "$auth_header" \
187+
-H "Content-Type: application/json" \
188+
"${BASE_URL}/api/v1/tunnels/frp" \
189+
-d "$tunnel_body"
190+
)"
191+
192+
if [[ $have_jq -eq 1 ]]; then
193+
created_name="$(printf '%s' "$tunnel_resp" | jq -r '.tunnelName // empty')"
194+
else
195+
created_name="$(python3 - <<'PY'
196+
import json,sys
197+
d=json.loads(sys.stdin.read())
198+
print(d.get("tunnelName",""))
199+
PY
200+
<<<"$tunnel_resp")"
201+
fi
202+
203+
if [[ -z "${created_name}" ]]; then
204+
echo "Error: failed to parse tunnelName from response:" >&2
205+
echo "$tunnel_resp" >&2
206+
exit 1
207+
fi
208+
209+
echo "$created_name"
210+
211+
# Determine the bindPort to use (optional override or from session response)
212+
if [[ -n "$BIND_PORT" ]]; then
213+
visitor_bind_port="$BIND_PORT"
214+
else
215+
visitor_bind_port="8032"
216+
fi
217+
218+
# Write FRP client visitor configuration to frpc.toml
219+
cat > frpc.toml <<EOF
220+
serverAddr = "${DISCOVERY_HOST}"
221+
serverPort = ${DISCOVERY_PORT}
222+
auth.token = "${DISCOVERY_TOKEN}"
223+
loginFailExit = false
224+
225+
[[visitors]]
226+
name = "xtcp_client"
227+
type = "xtcp"
228+
serverName = "${created_name}"
229+
secretKey = "${TUNNEL_SECRET}"
230+
bindPort = ${visitor_bind_port}
231+
bindAddr = "0.0.0.0"
232+
keepTunnelOpen = true
233+
EOF
234+
235+
echo "FRP client configuration written to frpc.toml"
236+
237+
# Remove any existing known_hosts entry for this localhost port
238+
if [[ -f ~/.ssh/known_hosts ]]; then
239+
sed -i.bak "/\[localhost\]:${visitor_bind_port}/d" ~/.ssh/known_hosts
240+
echo "Removed known_hosts entries for [localhost]:${visitor_bind_port}"
241+
fi
242+
243+
# Print connection instructions in bold
244+
echo ""
245+
echo -e "\033[1;32m╔════════════════════════════════════════════════════════════╗\033[0m"
246+
echo -e "\033[1;32m║ ║\033[0m"
247+
echo -e "\033[1;32m║ \033[1;37mConnect to job using: ssh -p ${visitor_bind_port} localhost\033[1;32m ║\033[0m"
248+
echo -e "\033[1;32m║ ║\033[0m"
249+
echo -e "\033[1;32m╚════════════════════════════════════════════════════════════╝\033[0m"
250+
echo ""
251+
252+
# Check if frpc binary is available
253+
if ! command -v ./frpc >/dev/null 2>&1 && ! command -v frpc >/dev/null 2>&1; then
254+
echo "Error: frpc binary not found." >&2
255+
echo "Please download it from: https://github.com/fatedier/frp/releases/tag/v0.67.0" >&2
256+
echo "Extract the archive and place 'frpc' in the current directory or in your PATH." >&2
257+
exit 1
258+
fi
259+
260+
# Determine which frpc to use
261+
if [[ -x ./frpc ]]; then
262+
FRPC_BIN="./frpc"
263+
else
264+
FRPC_BIN="frpc"
265+
fi
266+
267+
echo "Starting frpc client..."
268+
exec "$FRPC_BIN" -c frpc.toml

0 commit comments

Comments
 (0)