@dbalabka/chrome-wsl 0.3.2 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -4
- package/chrome-wsl +173 -12
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -16,6 +16,7 @@ A small WSL helper to open Google Chrome on Windows with remote debugging (port
|
|
|
16
16
|
- WSL with `powershell.exe` available.
|
|
17
17
|
- Windows Chrome installed at `C:\Program Files\Google\Chrome\Application\chrome.exe` (adjust the path in the script if different).
|
|
18
18
|
- Network/apt access to install `socat` on first run (or preinstall manually).
|
|
19
|
+
- Run the script from inside WSL; non-WSL Linux is not supported (Docker has limited proxy-only support, see below).
|
|
19
20
|
|
|
20
21
|
## Usage
|
|
21
22
|
|
|
@@ -64,10 +65,14 @@ Then run:
|
|
|
64
65
|
chrome-wsl --uninstall
|
|
65
66
|
```
|
|
66
67
|
|
|
67
|
-
##
|
|
68
|
-
|
|
69
|
-
- Chrome
|
|
70
|
-
|
|
68
|
+
## Docker
|
|
69
|
+
|
|
70
|
+
`chrome-wsl` can also take care of starting a proxy inside the Docker container and allow to access the MCP server from localhost. It helps to use the same Chrome DevTools MCP configuration for agents running inside the docker container as well as outside.
|
|
71
|
+
```shell
|
|
72
|
+
npx @dbalabka/chrome-wsl --container=<name>
|
|
73
|
+
npx @dbalabka/chrome-wsl --stop --container=<name>
|
|
74
|
+
npx @dbalabka/chrome-wsl --uninstall --container=<name>
|
|
75
|
+
```
|
|
71
76
|
|
|
72
77
|
## Chrome DevTools MCP configuration for agents
|
|
73
78
|
|
|
@@ -83,5 +88,13 @@ args = ["-y", "chrome-devtools-mcp@latest", "--browser-url=http://127.0.0.1:9222
|
|
|
83
88
|
startup_timeout_sec = 20.0
|
|
84
89
|
```
|
|
85
90
|
|
|
91
|
+
To run Codex inside the container and use the same MCP configuration and authorisation token, mount the Codex configuration folder inside the docker container using the following docker composer settings:
|
|
92
|
+
```shell
|
|
93
|
+
services:
|
|
94
|
+
app:
|
|
95
|
+
volumes:
|
|
96
|
+
- ~/.codex:/home/vscode/.codex
|
|
97
|
+
```
|
|
98
|
+
|
|
86
99
|
## License
|
|
87
100
|
MIT License. See `LICENSE` for details.
|
package/chrome-wsl
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# Compatible with bash and zsh.
|
|
3
|
+
PACKAGE_VERSION="0.4.0"
|
|
3
4
|
set -e
|
|
4
5
|
set -u
|
|
5
6
|
if command -v setopt >/dev/null 2>&1; then
|
|
@@ -12,15 +13,25 @@ PORT=9222
|
|
|
12
13
|
WINDOWS_CHROME_PATH='C:\Program Files\Google\Chrome\Application\chrome.exe'
|
|
13
14
|
FIREWALL_RULE_NAME='Chrome Remote Debug'
|
|
14
15
|
PID_FILE="/tmp/start-chrome-wsl.pids"
|
|
16
|
+
HOST_IP=""
|
|
17
|
+
BROWSER_VERSION=""
|
|
15
18
|
OK_MARK="✅"
|
|
19
|
+
WARN_MARK="❗"
|
|
16
20
|
ERR_MARK="❌"
|
|
17
21
|
|
|
18
22
|
ok() {
|
|
19
|
-
|
|
23
|
+
local prefix="${CONTAINER_LABEL:+[${CONTAINER_LABEL}] }"
|
|
24
|
+
echo "${OK_MARK} ${prefix}$*"
|
|
20
25
|
}
|
|
21
26
|
|
|
22
27
|
err() {
|
|
23
|
-
|
|
28
|
+
local prefix="${CONTAINER_LABEL:+[${CONTAINER_LABEL}] }"
|
|
29
|
+
echo "${ERR_MARK} ${prefix}$*" >&2
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
warn() {
|
|
33
|
+
local prefix="${CONTAINER_LABEL:+[${CONTAINER_LABEL}] }"
|
|
34
|
+
echo "${WARN_MARK} ${prefix}$*" >&2
|
|
24
35
|
}
|
|
25
36
|
|
|
26
37
|
set_pid() {
|
|
@@ -90,13 +101,21 @@ run_powershell() {
|
|
|
90
101
|
fi
|
|
91
102
|
}
|
|
92
103
|
|
|
104
|
+
CONFIRM_MARK="❔"
|
|
93
105
|
confirm() {
|
|
94
106
|
local prompt=$1
|
|
95
107
|
local reply
|
|
96
|
-
|
|
108
|
+
local prefix="${CONTAINER_LABEL:+[${CONTAINER_LABEL}] }"
|
|
109
|
+
read -r -p "${CONFIRM_MARK} ${prefix}${prompt} [y/N] " reply
|
|
97
110
|
[[ "${reply}" =~ ^[Yy]$ ]]
|
|
98
111
|
}
|
|
99
112
|
|
|
113
|
+
version_suffix() {
|
|
114
|
+
if [[ -n "${BROWSER_VERSION:-}" ]]; then
|
|
115
|
+
printf " (Chrome %s)" "$BROWSER_VERSION"
|
|
116
|
+
fi
|
|
117
|
+
}
|
|
118
|
+
|
|
100
119
|
chrome_running() {
|
|
101
120
|
run_powershell "if (Get-Process -Name chrome -ErrorAction SilentlyContinue) { exit 0 } else { exit 1 }"
|
|
102
121
|
}
|
|
@@ -118,6 +137,14 @@ port_listening_info() {
|
|
|
118
137
|
run_powershell "\$conns = Get-NetTCPConnection -LocalPort ${PORT} -ErrorAction SilentlyContinue | Select-Object -Property LocalAddress,LocalPort,RemoteAddress,RemotePort,State,OwningProcess; foreach (\$c in \$conns) { \$p = Get-Process -Id \$c.OwningProcess -ErrorAction SilentlyContinue; \$name = if (\$p) { \$p.Name } else { 'unknown' }; Write-Output (\"{0}:{1} owner={2} pid={3} state={4}\" -f \$c.LocalAddress, \$c.LocalPort, \$name, \$c.OwningProcess, \$c.State) }"
|
|
119
138
|
}
|
|
120
139
|
|
|
140
|
+
get_browser_version() {
|
|
141
|
+
local host_ip=${1:-$HOST_IP}
|
|
142
|
+
[[ -z "${host_ip:-}" ]] && return 0
|
|
143
|
+
curl -s "http://${host_ip}:${PORT}/json/version" \
|
|
144
|
+
| sed -n "s/.*\"Browser\"[[:space:]]*:[[:space:]]*\"\\([^\"]*\\)\".*/\\1/p" \
|
|
145
|
+
| head -n 1
|
|
146
|
+
}
|
|
147
|
+
|
|
121
148
|
get_wsl_host_ip() {
|
|
122
149
|
local host_ip
|
|
123
150
|
host_ip=$(ip route | awk '/^default via / {print $3; exit}')
|
|
@@ -131,13 +158,24 @@ get_wsl_host_ip() {
|
|
|
131
158
|
check_portproxy() {
|
|
132
159
|
local host_ip=$1
|
|
133
160
|
local output regex
|
|
134
|
-
output=$(run_powershell "netsh interface portproxy show all" | tr -d '
|
|
135
|
-
|
|
161
|
+
output=$(run_powershell "netsh interface portproxy show all" | tr -d '
|
|
162
|
+
')
|
|
163
|
+
regex="${host_ip//./\.}[[:space:]]+${PORT}[[:space:]]+127\.0\.0\.1[[:space:]]+${PORT}"
|
|
136
164
|
if echo "$output" | grep -Eq "$regex"; then
|
|
137
165
|
ok "Portproxy ${host_ip}:${PORT} -> 127.0.0.1:${PORT} is configured."
|
|
138
166
|
return 0
|
|
139
167
|
fi
|
|
140
168
|
|
|
169
|
+
warn "Portproxy on ${host_ip}:${PORT} is missing; attempting to create it (may prompt for admin)."
|
|
170
|
+
if RUN_AS_ADMIN=1 run_powershell "netsh interface portproxy add v4tov4 listenaddress=${host_ip} listenport=${PORT} connectaddress=127.0.0.1 connectport=${PORT}"; then
|
|
171
|
+
output=$(run_powershell "netsh interface portproxy show all" | tr -d '
|
|
172
|
+
')
|
|
173
|
+
if echo "$output" | grep -Eq "$regex"; then
|
|
174
|
+
ok "Created portproxy ${host_ip}:${PORT} -> 127.0.0.1:${PORT}."
|
|
175
|
+
return 0
|
|
176
|
+
fi
|
|
177
|
+
fi
|
|
178
|
+
|
|
141
179
|
cat <<EOF
|
|
142
180
|
${ERR_MARK} Portproxy on ${host_ip}:${PORT} is missing.
|
|
143
181
|
Run this in an **admin PowerShell** window:
|
|
@@ -154,7 +192,7 @@ check_firewall_rule() {
|
|
|
154
192
|
return 0
|
|
155
193
|
fi
|
|
156
194
|
|
|
157
|
-
|
|
195
|
+
warn "Firewall rule \"${FIREWALL_RULE_NAME}\" is missing; attempting to create it (may prompt for admin)."
|
|
158
196
|
if RUN_AS_ADMIN=1 run_powershell "New-NetFirewallRule -DisplayName \"${FIREWALL_RULE_NAME}\" -Direction Inbound -LocalPort ${PORT} -Protocol TCP -Action Allow" \
|
|
159
197
|
&& run_powershell "${rule_check_cmd}"; then
|
|
160
198
|
ok "Created firewall rule \"${FIREWALL_RULE_NAME}\"."
|
|
@@ -209,13 +247,40 @@ uninstall_socat() {
|
|
|
209
247
|
fi
|
|
210
248
|
}
|
|
211
249
|
|
|
250
|
+
# Remove portproxy entry
|
|
251
|
+
uninstall_portproxy() {
|
|
252
|
+
local host_ip=${1:-$HOST_IP}
|
|
253
|
+
if [[ -z "${host_ip:-}" ]]; then
|
|
254
|
+
host_ip=$(get_wsl_host_ip)
|
|
255
|
+
fi
|
|
256
|
+
local output regex
|
|
257
|
+
output=$(run_powershell "netsh interface portproxy show all" | tr -d '\r')
|
|
258
|
+
regex="${host_ip//./\\.}[[:space:]]+${PORT}[[:space:]]+127\\.0\\.0\\.1[[:space:]]+${PORT}"
|
|
259
|
+
if ! echo "$output" | grep -Eq "$regex"; then
|
|
260
|
+
ok "Portproxy ${host_ip}:${PORT} is already absent."
|
|
261
|
+
return 0
|
|
262
|
+
fi
|
|
263
|
+
|
|
264
|
+
if ! confirm "Remove portproxy ${host_ip}:${PORT} -> 127.0.0.1:${PORT}?"; then
|
|
265
|
+
ok "Skipped portproxy removal."
|
|
266
|
+
return 0
|
|
267
|
+
fi
|
|
268
|
+
|
|
269
|
+
if RUN_AS_ADMIN=1 run_powershell "netsh interface portproxy delete v4tov4 listenaddress=${host_ip} listenport=${PORT}" \
|
|
270
|
+
&& ! (run_powershell "netsh interface portproxy show all" | tr -d '\r' | grep -Eq "$regex"); then
|
|
271
|
+
ok "Removed portproxy ${host_ip}:${PORT} -> 127.0.0.1:${PORT}."
|
|
272
|
+
else
|
|
273
|
+
err "Failed to remove portproxy ${host_ip}:${PORT}."
|
|
274
|
+
fi
|
|
275
|
+
}
|
|
276
|
+
|
|
212
277
|
ensure_socat() {
|
|
213
278
|
if command -v socat >/dev/null 2>&1; then
|
|
214
279
|
ok "socat is already installed."
|
|
215
280
|
return 0
|
|
216
281
|
fi
|
|
217
282
|
|
|
218
|
-
|
|
283
|
+
warn "socat not found. Installing via apt..."
|
|
219
284
|
sudo apt-get update
|
|
220
285
|
sudo apt-get install -y socat
|
|
221
286
|
}
|
|
@@ -232,6 +297,40 @@ start_socat() {
|
|
|
232
297
|
ok "Started socat (logging to /tmp/socat-9222.log)."
|
|
233
298
|
}
|
|
234
299
|
|
|
300
|
+
is_docker() {
|
|
301
|
+
[[ -f /.dockerenv ]] && return 0
|
|
302
|
+
grep -qaE 'docker|containerd|kubepods' /proc/1/cgroup 2>/dev/null
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
container_exec() {
|
|
306
|
+
local container=$1; shift
|
|
307
|
+
local script_path
|
|
308
|
+
script_path=$(readlink -f "$0")
|
|
309
|
+
ensure_docker
|
|
310
|
+
local tty_flag="-i"
|
|
311
|
+
if [ -t 0 ]; then
|
|
312
|
+
tty_flag="-it"
|
|
313
|
+
fi
|
|
314
|
+
if ! cat "$script_path" | docker exec -i "${container}" sh -c "cat > /tmp/chrome-wsl.sh"; then
|
|
315
|
+
err "Failed to copy script into container ${container}."
|
|
316
|
+
exit 1
|
|
317
|
+
fi
|
|
318
|
+
local inner_cmd="CONTAINER_LABEL=${container} chmod +x /tmp/chrome-wsl.sh && CONTAINER_LABEL=${container} /tmp/chrome-wsl.sh $*"
|
|
319
|
+
docker exec ${tty_flag} "${container}" sh -lc "${inner_cmd}"
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
get_docker_host_ip() {
|
|
323
|
+
getent hosts host.docker.internal 2>/dev/null | awk '{print $1; exit}'
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
ensure_docker() {
|
|
327
|
+
if command -v docker >/dev/null 2>&1; then
|
|
328
|
+
return 0
|
|
329
|
+
fi
|
|
330
|
+
err "Docker CLI is required for --container."
|
|
331
|
+
exit 1
|
|
332
|
+
}
|
|
333
|
+
|
|
235
334
|
stop_chrome() {
|
|
236
335
|
local chrome_pid
|
|
237
336
|
chrome_pid=$(get_pid "CHROME_PID")
|
|
@@ -280,21 +379,79 @@ start_chrome() {
|
|
|
280
379
|
fi
|
|
281
380
|
}
|
|
282
381
|
|
|
382
|
+
ensure_wsl() {
|
|
383
|
+
if grep -qEi 'Microsoft|WSL' /proc/version >/dev/null 2>&1; then
|
|
384
|
+
return 0
|
|
385
|
+
fi
|
|
386
|
+
err "This tool must be run inside WSL; exiting."
|
|
387
|
+
exit 1
|
|
388
|
+
}
|
|
389
|
+
|
|
283
390
|
main() {
|
|
284
|
-
|
|
391
|
+
local container="" uninstall_flag=0 stop_flag=0
|
|
392
|
+
for arg in "$@"; do
|
|
393
|
+
case "$arg" in
|
|
394
|
+
--container=*) container=${arg#*=} ;;
|
|
395
|
+
--uninstall) uninstall_flag=1 ;;
|
|
396
|
+
--stop) stop_flag=1 ;;
|
|
397
|
+
esac
|
|
398
|
+
done
|
|
399
|
+
|
|
400
|
+
if [[ "${1-}" == "--version" || "${1-}" == "-V" ]]; then
|
|
401
|
+
echo "chrome-wsl ${PACKAGE_VERSION:-unknown}"
|
|
402
|
+
exit 0
|
|
403
|
+
fi
|
|
404
|
+
|
|
405
|
+
if is_docker && [[ -n "$container" ]]; then
|
|
406
|
+
err "--container is only supported when running in WSL, not inside Docker."
|
|
407
|
+
exit 1
|
|
408
|
+
fi
|
|
409
|
+
|
|
410
|
+
if is_docker; then
|
|
411
|
+
if [[ $uninstall_flag -eq 1 ]]; then
|
|
412
|
+
uninstall_socat
|
|
413
|
+
exit 0
|
|
414
|
+
fi
|
|
415
|
+
|
|
416
|
+
if [[ $stop_flag -eq 1 ]]; then
|
|
417
|
+
stop_socat
|
|
418
|
+
exit 0
|
|
419
|
+
fi
|
|
420
|
+
|
|
421
|
+
HOST_IP=$(get_docker_host_ip)
|
|
422
|
+
if [[ -z "${HOST_IP:-}" ]]; then
|
|
423
|
+
err "Could not resolve host.docker.internal inside Docker."
|
|
424
|
+
exit 1
|
|
425
|
+
fi
|
|
426
|
+
|
|
427
|
+
ensure_socat
|
|
428
|
+
start_socat "$HOST_IP"
|
|
429
|
+
exit 0
|
|
430
|
+
fi
|
|
431
|
+
|
|
432
|
+
ensure_wsl
|
|
433
|
+
|
|
434
|
+
if [[ $uninstall_flag -eq 1 ]]; then
|
|
435
|
+
uninstall_portproxy
|
|
285
436
|
uninstall_firewall_rule
|
|
286
437
|
uninstall_socat
|
|
438
|
+
if [[ -n "$container" ]]; then
|
|
439
|
+
container_exec "$container" --uninstall
|
|
440
|
+
fi
|
|
287
441
|
exit 0
|
|
288
442
|
fi
|
|
289
443
|
|
|
290
|
-
if [[
|
|
444
|
+
if [[ $stop_flag -eq 1 ]]; then
|
|
291
445
|
stop_chrome
|
|
292
446
|
stop_socat
|
|
447
|
+
if [[ -n "$container" ]]; then
|
|
448
|
+
container_exec "$container" --stop
|
|
449
|
+
fi
|
|
293
450
|
exit 0
|
|
294
451
|
fi
|
|
295
452
|
|
|
296
|
-
|
|
297
|
-
host_ip
|
|
453
|
+
HOST_IP=$(get_wsl_host_ip)
|
|
454
|
+
local host_ip="$HOST_IP"
|
|
298
455
|
ok "Detected Windows host IP: ${host_ip}"
|
|
299
456
|
|
|
300
457
|
check_portproxy "$host_ip" || exit 1
|
|
@@ -302,6 +459,10 @@ main() {
|
|
|
302
459
|
ensure_socat
|
|
303
460
|
start_socat "$host_ip"
|
|
304
461
|
start_chrome
|
|
462
|
+
|
|
463
|
+
if [[ -n "$container" ]]; then
|
|
464
|
+
container_exec "$container"
|
|
465
|
+
fi
|
|
305
466
|
}
|
|
306
467
|
|
|
307
|
-
main "$@"
|
|
468
|
+
main "$@"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dbalabka/chrome-wsl",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.0",
|
|
4
4
|
"description": "WSL helper to start Windows Chrome with remote debugging and socat port forwarding.",
|
|
5
5
|
"bin": "./chrome-wsl",
|
|
6
6
|
"files": [
|
|
@@ -28,4 +28,4 @@
|
|
|
28
28
|
"publishConfig": {
|
|
29
29
|
"access": "public"
|
|
30
30
|
}
|
|
31
|
-
}
|
|
31
|
+
}
|