wsl-chrome-bridge 0.2.0 → 0.3.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-zh.md +25 -9
- package/README.md +25 -8
- package/dist/bridge-options.d.ts +2 -0
- package/dist/bridge-options.js +6 -0
- package/dist/bridge-options.js.map +1 -1
- package/dist/bridge-powershell-scripts.d.ts +11 -0
- package/dist/bridge-powershell-scripts.js +475 -0
- package/dist/bridge-powershell-scripts.js.map +1 -0
- package/dist/bridge-runner.js +1006 -369
- package/dist/bridge-runner.js.map +1 -1
- package/dist/bridge-watchdog.d.ts +1 -0
- package/dist/bridge-watchdog.js +189 -0
- package/dist/bridge-watchdog.js.map +1 -0
- package/docs/BRIDGE_CONNECTION_LIFECYCLE-zh.md +199 -0
- package/docs/BRIDGE_CONNECTION_LIFECYCLE.md +198 -0
- package/package.json +4 -3
package/README-zh.md
CHANGED
|
@@ -114,15 +114,6 @@ DISPLAY = ":9999"
|
|
|
114
114
|
> FAQ 章節有介紹 `--executablePath` 的路徑尋找的方式。
|
|
115
115
|
> 其他設定寫法可參考 `agent-config-sample/.codex/`。
|
|
116
116
|
|
|
117
|
-
|
|
118
|
-
## 開發時所用的技術棧
|
|
119
|
-
- Node 24
|
|
120
|
-
- TypeScript v6
|
|
121
|
-
- Commander v14
|
|
122
|
-
- ws v8.20
|
|
123
|
-
- 測試框架: Vitest v4.1
|
|
124
|
-
|
|
125
|
-
|
|
126
117
|
## 安裝與建置
|
|
127
118
|
|
|
128
119
|
```bash
|
|
@@ -187,6 +178,15 @@ ls -la ~/.local/share/mise/shims/wsl-chrome-bridge
|
|
|
187
178
|
而這個判斷是在 bridge 之前就完成,所以 `wsl-chrome-bridge` 無法在後段覆蓋該決策。
|
|
188
179
|
如果你希望在 Windows 看到實際 Chrome 視窗,請於 MCP `env` 額外設定 `DISPLAY`(例如 `DISPLAY=:999`)。
|
|
189
180
|
|
|
181
|
+
### 上游 MCP 結束時,Headless 與非 Headless 行為
|
|
182
|
+
|
|
183
|
+
`wsl-chrome-bridge` 在上游 MCP(例如 `chrome-devtools-mcp` / `playwright-mcp`)結束時,採用以下生命週期策略:
|
|
184
|
+
|
|
185
|
+
- 若 Chrome 是以 headless 啟動,bridge 一律會結束該 Chrome 行程。
|
|
186
|
+
- 若 Chrome 不是以 headless 啟動(headed / non-headless),bridge 一律保留該 Chrome 行程。
|
|
187
|
+
|
|
188
|
+
這是刻意設計的行為,與原生 Playwright 在部分流程下的預設行為可能不同。
|
|
189
|
+
|
|
190
190
|
### 為什麼 Playwright 可能出現 `--no-sandbox` 警告
|
|
191
191
|
|
|
192
192
|
`playwright-mcp` 若未明確指定 browser channel,可能會產生含 `--no-sandbox` 的啟動參數。
|
|
@@ -243,6 +243,8 @@ WSL_CHROME_BRIDGE_USER_DATA_DIR = "%TEMP%\\wsl-chrome-bridge\\chrome-profile-xxx
|
|
|
243
243
|
- `WSL_CHROME_BRIDGE_REMOTE_DEBUG_PORT=9222` : 以環境變數指定 Windows Chrome debug port。若都沒指定,bridge 改為隨機 port,不再固定 9222。
|
|
244
244
|
- bridge 會在 Windows 端先探測 port 可用性。若是固定 port 模式且該 port 已被占用,會在啟動前直接報錯。
|
|
245
245
|
- `WSL_CHROME_BRIDGE_DEBUG_FILE=/tmp/xxx.log` : 以環境變數指定 bridge debug log 輸出路徑。
|
|
246
|
+
- `WSL_CHROME_BRIDGE_DEBUG_LEVEL=all|important` : 可選 debug 詳細度。`important`(預設)只記錄 session / 開頁導覽 / 斷線相關的重要 method 與錯誤回應;`all` 會記錄全部 CDP relay 訊息。
|
|
247
|
+
- `WSL_CHROME_BRIDGE_DEBUG_RAW_DIR=/tmp/wsl-chrome-bridge-raw` : 以環境變數指定 raw CDP 內容輸出目錄。每筆 request/response/event 會獨立寫入一個 `raw-<timestamp>.log` 檔案,且 `WSL_CHROME_BRIDGE_DEBUG_FILE` 的 relay log 會包含對應的 `rawPath`。
|
|
246
248
|
|
|
247
249
|
### 已確認無法使用的參數
|
|
248
250
|
|
|
@@ -252,3 +254,17 @@ WSL_CHROME_BRIDGE_USER_DATA_DIR = "%TEMP%\\wsl-chrome-bridge\\chrome-profile-xxx
|
|
|
252
254
|
|
|
253
255
|
|
|
254
256
|
> 其他 `chrome-devtools-mcp` 提供的原始參數未必全部能使用,本程式尚在開發中,也沒有完全測試過所有原始參數,故只列出目前已經測試過的。
|
|
257
|
+
|
|
258
|
+
## For Developer
|
|
259
|
+
|
|
260
|
+
開發者可參考 bridge 連線生命週期與恢復策略文件:
|
|
261
|
+
|
|
262
|
+
- [docs/BRIDGE_CONNECTION_LIFECYCLE-zh.md](./docs/BRIDGE_CONNECTION_LIFECYCLE-zh.md)
|
|
263
|
+
|
|
264
|
+
開發時所用技術棧:
|
|
265
|
+
|
|
266
|
+
- Node 24
|
|
267
|
+
- TypeScript v6
|
|
268
|
+
- Commander v14
|
|
269
|
+
- ws v8.20
|
|
270
|
+
- 測試框架: Vitest v4.1
|
package/README.md
CHANGED
|
@@ -119,14 +119,6 @@ In bridge + system Chrome usage, `--browser chrome` helps avoid upstream `--no-s
|
|
|
119
119
|
> See the FAQ for how to locate `--executablePath`.
|
|
120
120
|
> For more configuration patterns, see `agent-config-sample/.codex/`.
|
|
121
121
|
|
|
122
|
-
## Development Stack
|
|
123
|
-
|
|
124
|
-
- Node 24
|
|
125
|
-
- TypeScript v6
|
|
126
|
-
- Commander v14
|
|
127
|
-
- ws v8.20
|
|
128
|
-
- Test framework: Vitest v4.1
|
|
129
|
-
|
|
130
122
|
## Build
|
|
131
123
|
|
|
132
124
|
```bash
|
|
@@ -190,6 +182,15 @@ If `DISPLAY` is not set, Playwright often switches to headless behavior (even wh
|
|
|
190
182
|
That decision happens upstream before bridge logic starts, so `wsl-chrome-bridge` cannot override it later.
|
|
191
183
|
If you want to see a visible Chrome window on Windows, set `DISPLAY` in MCP `env` (for example `DISPLAY=:999`).
|
|
192
184
|
|
|
185
|
+
### Headless vs headed behavior on upstream MCP exit
|
|
186
|
+
|
|
187
|
+
`wsl-chrome-bridge` applies the following lifecycle policy when upstream MCP exits (for example `chrome-devtools-mcp` / `playwright-mcp`):
|
|
188
|
+
|
|
189
|
+
- If Chrome was started in headless mode, bridge always terminates that Chrome process.
|
|
190
|
+
- If Chrome was started in headed mode (non-headless), bridge always keeps that Chrome process.
|
|
191
|
+
|
|
192
|
+
This behavior is intentional and can differ from native Playwright defaults in some workflows.
|
|
193
|
+
|
|
193
194
|
### Why Playwright may show `--no-sandbox` warning
|
|
194
195
|
|
|
195
196
|
When `playwright-mcp` is launched without an explicit browser channel, it may resolve to launch args that include `--no-sandbox`. In headed Chrome, this shows the warning banner:
|
|
@@ -243,6 +244,8 @@ WSL_CHROME_BRIDGE_USER_DATA_DIR = "%TEMP%\\wsl-chrome-bridge\\chrome-profile-xxx
|
|
|
243
244
|
- `WSL_CHROME_BRIDGE_REMOTE_DEBUG_PORT=9222`: optional environment variable for Windows Chrome debug port. If no port is specified, bridge uses a random port instead of fixed `9222`.
|
|
244
245
|
- Bridge now probes port availability on Windows before launching Chrome. In fixed-port mode, startup fails fast if that port is already occupied.
|
|
245
246
|
- `WSL_CHROME_BRIDGE_DEBUG_FILE=/tmp/xxx.log`: optional debug output file in WSL.
|
|
247
|
+
- `WSL_CHROME_BRIDGE_DEBUG_LEVEL=all|important`: optional debug verbosity level. `important` (default) logs only important session/navigation/disconnect-related CDP methods and error responses. `all` logs all CDP relay traffic.
|
|
248
|
+
- `WSL_CHROME_BRIDGE_DEBUG_RAW_DIR=/tmp/wsl-chrome-bridge-raw`: optional directory to store full raw CDP payload files. Each request/response/event payload is written as a separate `raw-<timestamp>.log` file, and `WSL_CHROME_BRIDGE_DEBUG_FILE` includes the corresponding `rawPath` for each relay log entry.
|
|
246
249
|
|
|
247
250
|
### Known incompatible original arguments
|
|
248
251
|
|
|
@@ -251,3 +254,17 @@ The following original `chrome-devtools-mcp` option is currently known as incomp
|
|
|
251
254
|
- `--browser-url`: this enables remote-connection mode in `chrome-devtools-mcp` instead of pipe mode, so it cannot work with `wsl-chrome-bridge`.
|
|
252
255
|
|
|
253
256
|
> Other original `chrome-devtools-mcp` arguments are not all fully validated yet. This project is still under development, so only currently tested limitations are listed here.
|
|
257
|
+
|
|
258
|
+
## For Developer
|
|
259
|
+
|
|
260
|
+
Developer lifecycle and recovery reference:
|
|
261
|
+
|
|
262
|
+
- [docs/BRIDGE_CONNECTION_LIFECYCLE.md](./docs/BRIDGE_CONNECTION_LIFECYCLE.md)
|
|
263
|
+
|
|
264
|
+
Development stack:
|
|
265
|
+
|
|
266
|
+
- Node 24
|
|
267
|
+
- TypeScript v6
|
|
268
|
+
- Commander v14
|
|
269
|
+
- ws v8.20
|
|
270
|
+
- Test framework: Vitest v4.1
|
package/dist/bridge-options.d.ts
CHANGED
|
@@ -7,6 +7,8 @@ export interface BridgeLaunchPlan {
|
|
|
7
7
|
usePipeTransport: boolean;
|
|
8
8
|
/** Optional bridge debug log file path in WSL. */
|
|
9
9
|
bridgeDebugFile: string | null;
|
|
10
|
+
/** Optional directory for writing one raw CDP payload file per message. */
|
|
11
|
+
bridgeDebugRawDir: string | null;
|
|
10
12
|
/** Optional Chrome executable override from environment variable. */
|
|
11
13
|
bridgeChromeExecutablePath: string | null;
|
|
12
14
|
/** User-data-dir value as seen by the bridge (WSL side). */
|
package/dist/bridge-options.js
CHANGED
|
@@ -74,6 +74,7 @@ export function planBridgeLaunch(chromeArgs, env = {}) {
|
|
|
74
74
|
let userDataDir = null;
|
|
75
75
|
let windowsUserDataDir = null;
|
|
76
76
|
let bridgeDebugFile = null;
|
|
77
|
+
let bridgeDebugRawDir = null;
|
|
77
78
|
let bridgeChromeExecutablePath = null;
|
|
78
79
|
let bridgeRemoteDebugPort = null;
|
|
79
80
|
let requestedLocalDebugPort = null;
|
|
@@ -202,6 +203,10 @@ export function planBridgeLaunch(chromeArgs, env = {}) {
|
|
|
202
203
|
bridgeDebugFile = envDebugFile;
|
|
203
204
|
}
|
|
204
205
|
}
|
|
206
|
+
const envDebugRawDir = env.WSL_CHROME_BRIDGE_DEBUG_RAW_DIR?.trim();
|
|
207
|
+
if (envDebugRawDir) {
|
|
208
|
+
bridgeDebugRawDir = envDebugRawDir;
|
|
209
|
+
}
|
|
205
210
|
const localProxyPort = requestedLocalDebugPort;
|
|
206
211
|
let windowsDebugPortSource = "auto-random";
|
|
207
212
|
let windowsDebugPort = selectRandomDebugPort();
|
|
@@ -233,6 +238,7 @@ export function planBridgeLaunch(chromeArgs, env = {}) {
|
|
|
233
238
|
passthroughArgs,
|
|
234
239
|
usePipeTransport,
|
|
235
240
|
bridgeDebugFile,
|
|
241
|
+
bridgeDebugRawDir,
|
|
236
242
|
bridgeChromeExecutablePath,
|
|
237
243
|
userDataDir,
|
|
238
244
|
windowsUserDataDir,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bridge-options.js","sourceRoot":"","sources":["../src/bridge-options.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"bridge-options.js","sourceRoot":"","sources":["../src/bridge-options.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Returns all PowerShell script sources used by the bridge runner.
|
|
3
|
+
*/
|
|
4
|
+
export declare function getBridgePowerShellScripts(): {
|
|
5
|
+
launchChrome: string;
|
|
6
|
+
findExistingChrome: string;
|
|
7
|
+
getVersion: string;
|
|
8
|
+
resolvePort: string;
|
|
9
|
+
stopChromeByProfilePort: string;
|
|
10
|
+
relay: string;
|
|
11
|
+
};
|
|
@@ -0,0 +1,475 @@
|
|
|
1
|
+
const LAUNCH_CHROME_PS = `
|
|
2
|
+
param(
|
|
3
|
+
[string]$ChromePath,
|
|
4
|
+
[Parameter(ValueFromRemainingArguments = $true)]
|
|
5
|
+
[string[]]$ChromeArgs
|
|
6
|
+
)
|
|
7
|
+
$ErrorActionPreference = 'Stop'
|
|
8
|
+
$ChromeArgs = $ChromeArgs | ForEach-Object { [Environment]::ExpandEnvironmentVariables($_) }
|
|
9
|
+
$proc = Start-Process -FilePath $ChromePath -ArgumentList $ChromeArgs -PassThru
|
|
10
|
+
Write-Output $proc.Id
|
|
11
|
+
`;
|
|
12
|
+
const FIND_EXISTING_CHROME_PS = `
|
|
13
|
+
param([string]$UserDataDir)
|
|
14
|
+
$ErrorActionPreference = 'Stop'
|
|
15
|
+
|
|
16
|
+
function Normalize-UserDataDir {
|
|
17
|
+
param([string]$Value)
|
|
18
|
+
|
|
19
|
+
if ([string]::IsNullOrWhiteSpace($Value)) {
|
|
20
|
+
return $null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
$trimmed = $Value.Trim('"')
|
|
24
|
+
$expanded = [Environment]::ExpandEnvironmentVariables($trimmed)
|
|
25
|
+
try {
|
|
26
|
+
$resolved = (Resolve-Path -LiteralPath $expanded -ErrorAction Stop).Path
|
|
27
|
+
} catch {
|
|
28
|
+
$resolved = $expanded
|
|
29
|
+
}
|
|
30
|
+
return $resolved.ToLowerInvariant()
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
$target = Normalize-UserDataDir -Value $UserDataDir
|
|
34
|
+
if ([string]::IsNullOrWhiteSpace($target)) {
|
|
35
|
+
Write-Output '{"found":false}'
|
|
36
|
+
exit 0
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
$matchUserDataPattern = '(?i)--user-data-dir(?:=|\\s+)(?:"([^"]+)"|([^\\s"]+))'
|
|
40
|
+
$matchPortPattern = '(?i)--remote-debugging-port(?:=|\\s+)(\\d{1,5})'
|
|
41
|
+
$matchHeadlessPattern = '(?i)(?:^|\\s)--headless(?:=(?:"[^"]+"|[^\\s"]+))?(?=\\s|$)'
|
|
42
|
+
$candidates = @()
|
|
43
|
+
|
|
44
|
+
$processes = Get-CimInstance Win32_Process -Filter "Name='chrome.exe'"
|
|
45
|
+
foreach ($proc in $processes) {
|
|
46
|
+
$line = $proc.CommandLine
|
|
47
|
+
if ([string]::IsNullOrWhiteSpace($line)) {
|
|
48
|
+
continue
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
$userDataMatch = [Regex]::Match($line, $matchUserDataPattern)
|
|
52
|
+
if (-not $userDataMatch.Success) {
|
|
53
|
+
continue
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
$capturedUserDataDir = if ($userDataMatch.Groups[1].Success) {
|
|
57
|
+
$userDataMatch.Groups[1].Value
|
|
58
|
+
} else {
|
|
59
|
+
$userDataMatch.Groups[2].Value
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
$normalizedUserDataDir = Normalize-UserDataDir -Value $capturedUserDataDir
|
|
63
|
+
if ([string]::IsNullOrWhiteSpace($normalizedUserDataDir) -or $normalizedUserDataDir -ne $target) {
|
|
64
|
+
continue
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
$portMatch = [Regex]::Match($line, $matchPortPattern)
|
|
68
|
+
if (-not $portMatch.Success) {
|
|
69
|
+
continue
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
$parsedPort = [int]$portMatch.Groups[1].Value
|
|
73
|
+
if ($parsedPort -lt 1 -or $parsedPort -gt 65535) {
|
|
74
|
+
continue
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
$candidates += [PSCustomObject]@{
|
|
78
|
+
pid = [int]$proc.ProcessId
|
|
79
|
+
port = $parsedPort
|
|
80
|
+
headless = [Regex]::IsMatch($line, $matchHeadlessPattern)
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
if ($candidates.Count -eq 0) {
|
|
85
|
+
Write-Output '{"found":false}'
|
|
86
|
+
exit 0
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
$selected = $candidates | Sort-Object -Property pid -Descending | Select-Object -First 1
|
|
90
|
+
Write-Output ($selected | ConvertTo-Json -Compress -Depth 4)
|
|
91
|
+
`;
|
|
92
|
+
const GET_VERSION_PS = `
|
|
93
|
+
param([int]$Port)
|
|
94
|
+
$ErrorActionPreference = 'Stop'
|
|
95
|
+
$uri = "http://127.0.0.1:$Port/json/version"
|
|
96
|
+
try {
|
|
97
|
+
$json = Invoke-RestMethod -UseBasicParsing -Uri $uri -TimeoutSec 2
|
|
98
|
+
if ($null -eq $json) { exit 1 }
|
|
99
|
+
$raw = $json | ConvertTo-Json -Compress -Depth 12
|
|
100
|
+
Write-Output $raw
|
|
101
|
+
exit 0
|
|
102
|
+
} catch {
|
|
103
|
+
exit 1
|
|
104
|
+
}
|
|
105
|
+
`;
|
|
106
|
+
const RESOLVE_PORT_PS = `
|
|
107
|
+
param(
|
|
108
|
+
[int]$RequestedPort,
|
|
109
|
+
[string]$Mode
|
|
110
|
+
)
|
|
111
|
+
$ErrorActionPreference = 'Stop'
|
|
112
|
+
|
|
113
|
+
function Test-PortAvailable {
|
|
114
|
+
param([int]$Port)
|
|
115
|
+
try {
|
|
116
|
+
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, $Port)
|
|
117
|
+
$listener.Start()
|
|
118
|
+
$listener.Stop()
|
|
119
|
+
return $true
|
|
120
|
+
} catch {
|
|
121
|
+
return $false
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function Get-RandomFreePort {
|
|
126
|
+
$listener = [System.Net.Sockets.TcpListener]::new([System.Net.IPAddress]::Loopback, 0)
|
|
127
|
+
$listener.Start()
|
|
128
|
+
$allocated = ([System.Net.IPEndPoint]$listener.LocalEndpoint).Port
|
|
129
|
+
$listener.Stop()
|
|
130
|
+
return $allocated
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if ($Mode -eq "fixed") {
|
|
134
|
+
if (-not (Test-PortAvailable -Port $RequestedPort)) {
|
|
135
|
+
Write-Error "Port $RequestedPort is already in use on Windows."
|
|
136
|
+
exit 2
|
|
137
|
+
}
|
|
138
|
+
Write-Output $RequestedPort
|
|
139
|
+
exit 0
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if ($Mode -eq "random") {
|
|
143
|
+
if ($RequestedPort -gt 0 -and (Test-PortAvailable -Port $RequestedPort)) {
|
|
144
|
+
Write-Output $RequestedPort
|
|
145
|
+
exit 0
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
for ($i = 0; $i -lt 50; $i++) {
|
|
149
|
+
$candidate = Get-RandomFreePort
|
|
150
|
+
if (Test-PortAvailable -Port $candidate) {
|
|
151
|
+
Write-Output $candidate
|
|
152
|
+
exit 0
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
Write-Error "Failed to allocate an available Windows debug port."
|
|
157
|
+
exit 3
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
Write-Error "Unsupported mode: $Mode"
|
|
161
|
+
exit 4
|
|
162
|
+
`;
|
|
163
|
+
const STOP_CHROME_BY_PROFILE_AND_PORT_PS = `
|
|
164
|
+
param(
|
|
165
|
+
[string]$UserDataDir,
|
|
166
|
+
[int]$RemoteDebugPort
|
|
167
|
+
)
|
|
168
|
+
$ErrorActionPreference = 'Stop'
|
|
169
|
+
|
|
170
|
+
function Normalize-UserDataDir {
|
|
171
|
+
param([string]$Value)
|
|
172
|
+
|
|
173
|
+
if ([string]::IsNullOrWhiteSpace($Value)) {
|
|
174
|
+
return $null
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
$trimmed = $Value.Trim('"')
|
|
178
|
+
$expanded = [Environment]::ExpandEnvironmentVariables($trimmed)
|
|
179
|
+
try {
|
|
180
|
+
$resolved = (Resolve-Path -LiteralPath $expanded -ErrorAction Stop).Path
|
|
181
|
+
} catch {
|
|
182
|
+
$resolved = $expanded
|
|
183
|
+
}
|
|
184
|
+
return $resolved.ToLowerInvariant()
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
$targetUserDataDir = Normalize-UserDataDir -Value $UserDataDir
|
|
188
|
+
if ([string]::IsNullOrWhiteSpace($targetUserDataDir) -or $RemoteDebugPort -lt 1 -or $RemoteDebugPort -gt 65535) {
|
|
189
|
+
Write-Output '{"killed":false,"pid":null,"reason":"invalid-input"}'
|
|
190
|
+
exit 0
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
$matchUserDataPattern = '(?i)--user-data-dir(?:=|\\s+)(?:"([^"]+)"|([^\\s"]+))'
|
|
194
|
+
$matchPortPattern = '(?i)--remote-debugging-port(?:=|\\s+)(\\d{1,5})'
|
|
195
|
+
$candidates = @()
|
|
196
|
+
|
|
197
|
+
$processes = Get-CimInstance Win32_Process -Filter "Name='chrome.exe'"
|
|
198
|
+
foreach ($proc in $processes) {
|
|
199
|
+
$line = $proc.CommandLine
|
|
200
|
+
if ([string]::IsNullOrWhiteSpace($line)) {
|
|
201
|
+
continue
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
# Only target the browser root process (renderer/gpu/etc carry --type=...).
|
|
205
|
+
if ($line -match '(?i)--type=') {
|
|
206
|
+
continue
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
$userDataMatch = [Regex]::Match($line, $matchUserDataPattern)
|
|
210
|
+
if (-not $userDataMatch.Success) {
|
|
211
|
+
continue
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
$capturedUserDataDir = if ($userDataMatch.Groups[1].Success) {
|
|
215
|
+
$userDataMatch.Groups[1].Value
|
|
216
|
+
} else {
|
|
217
|
+
$userDataMatch.Groups[2].Value
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
$normalizedUserDataDir = Normalize-UserDataDir -Value $capturedUserDataDir
|
|
221
|
+
if ([string]::IsNullOrWhiteSpace($normalizedUserDataDir) -or $normalizedUserDataDir -ne $targetUserDataDir) {
|
|
222
|
+
continue
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
$portMatch = [Regex]::Match($line, $matchPortPattern)
|
|
226
|
+
if (-not $portMatch.Success) {
|
|
227
|
+
continue
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
$parsedPort = [int]$portMatch.Groups[1].Value
|
|
231
|
+
if ($parsedPort -ne $RemoteDebugPort) {
|
|
232
|
+
continue
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
$candidates += [PSCustomObject]@{
|
|
236
|
+
pid = [int]$proc.ProcessId
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
if ($candidates.Count -eq 0) {
|
|
241
|
+
Write-Output '{"killed":false,"pid":null,"reason":"not-found"}'
|
|
242
|
+
exit 0
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
$selected = $candidates | Sort-Object -Property pid -Descending | Select-Object -First 1
|
|
246
|
+
Stop-Process -Id $selected.pid -Force -ErrorAction SilentlyContinue
|
|
247
|
+
Write-Output ('{"killed":true,"pid":' + $selected.pid + ',"reason":"killed"}')
|
|
248
|
+
`;
|
|
249
|
+
const RELAY_CSHARP = String.raw `
|
|
250
|
+
using System;
|
|
251
|
+
using System.Collections.Generic;
|
|
252
|
+
using System.IO;
|
|
253
|
+
using System.Net.WebSockets;
|
|
254
|
+
using System.Net.Sockets;
|
|
255
|
+
using System.Text;
|
|
256
|
+
using System.Threading;
|
|
257
|
+
using System.Threading.Tasks;
|
|
258
|
+
|
|
259
|
+
public class CDPRelay
|
|
260
|
+
{
|
|
261
|
+
private static string EscapeForLog(string value)
|
|
262
|
+
{
|
|
263
|
+
return (value ?? string.Empty).Replace("\r", "\\r").Replace("\n", "\\n");
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
private static void AppendField(StringBuilder sb, string key, string value)
|
|
267
|
+
{
|
|
268
|
+
if (sb.Length > 0)
|
|
269
|
+
{
|
|
270
|
+
sb.Append(";");
|
|
271
|
+
}
|
|
272
|
+
sb.Append(key);
|
|
273
|
+
sb.Append("=");
|
|
274
|
+
sb.Append(EscapeForLog(value ?? string.Empty));
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
private static string BuildInnerChain(Exception ex)
|
|
278
|
+
{
|
|
279
|
+
if (ex == null)
|
|
280
|
+
{
|
|
281
|
+
return string.Empty;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
var parts = new List<string>();
|
|
285
|
+
var current = ex;
|
|
286
|
+
var depth = 0;
|
|
287
|
+
while (current != null && depth < 5)
|
|
288
|
+
{
|
|
289
|
+
var type = current.GetType().FullName ?? current.GetType().Name;
|
|
290
|
+
var hresult = "0x" + current.HResult.ToString("X8");
|
|
291
|
+
var message = EscapeForLog(current.Message ?? string.Empty);
|
|
292
|
+
parts.Add(type + "|" + hresult + "|" + message);
|
|
293
|
+
current = current.InnerException;
|
|
294
|
+
depth += 1;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (current != null)
|
|
298
|
+
{
|
|
299
|
+
parts.Add("truncated");
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return string.Join(" -> ", parts.ToArray());
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
private static string StackTop(string stackTrace)
|
|
306
|
+
{
|
|
307
|
+
if (string.IsNullOrEmpty(stackTrace))
|
|
308
|
+
{
|
|
309
|
+
return string.Empty;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
var normalized = stackTrace.Replace("\r", string.Empty);
|
|
313
|
+
var lines = normalized.Split('\n');
|
|
314
|
+
if (lines.Length == 0)
|
|
315
|
+
{
|
|
316
|
+
return string.Empty;
|
|
317
|
+
}
|
|
318
|
+
return lines[0].Trim();
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
private static string FormatSocketState(ClientWebSocket ws)
|
|
322
|
+
{
|
|
323
|
+
var sb = new StringBuilder();
|
|
324
|
+
AppendField(sb, "wsState", ws != null ? ws.State.ToString() : string.Empty);
|
|
325
|
+
if (ws != null)
|
|
326
|
+
{
|
|
327
|
+
AppendField(
|
|
328
|
+
sb,
|
|
329
|
+
"wsCloseStatus",
|
|
330
|
+
ws.CloseStatus.HasValue ? ws.CloseStatus.Value.ToString() : string.Empty
|
|
331
|
+
);
|
|
332
|
+
AppendField(sb, "wsCloseDescription", ws.CloseStatusDescription ?? string.Empty);
|
|
333
|
+
}
|
|
334
|
+
return sb.ToString();
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
private static string FormatException(Exception ex, ClientWebSocket ws)
|
|
338
|
+
{
|
|
339
|
+
var sb = new StringBuilder();
|
|
340
|
+
var type = ex.GetType().FullName ?? ex.GetType().Name;
|
|
341
|
+
AppendField(sb, "type", type);
|
|
342
|
+
AppendField(sb, "hresult", "0x" + ex.HResult.ToString("X8"));
|
|
343
|
+
AppendField(sb, "message", ex.Message ?? string.Empty);
|
|
344
|
+
|
|
345
|
+
var webSocketException = ex as WebSocketException;
|
|
346
|
+
if (webSocketException != null)
|
|
347
|
+
{
|
|
348
|
+
AppendField(sb, "webSocketErrorCode", webSocketException.WebSocketErrorCode.ToString());
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
var socketException = ex as SocketException;
|
|
352
|
+
if (socketException == null && ex.InnerException != null)
|
|
353
|
+
{
|
|
354
|
+
socketException = ex.InnerException as SocketException;
|
|
355
|
+
}
|
|
356
|
+
if (socketException != null)
|
|
357
|
+
{
|
|
358
|
+
AppendField(sb, "socketErrorCode", socketException.ErrorCode.ToString());
|
|
359
|
+
AppendField(sb, "socketError", socketException.SocketErrorCode.ToString());
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
AppendField(sb, "innerChain", BuildInnerChain(ex.InnerException));
|
|
363
|
+
AppendField(sb, "stackTop", StackTop(ex.StackTrace));
|
|
364
|
+
AppendField(sb, "socketContext", FormatSocketState(ws));
|
|
365
|
+
return sb.ToString();
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
public static void Run(string wsUrl)
|
|
369
|
+
{
|
|
370
|
+
Console.InputEncoding = new UTF8Encoding(false);
|
|
371
|
+
Console.OutputEncoding = new UTF8Encoding(false);
|
|
372
|
+
|
|
373
|
+
var ws = new ClientWebSocket();
|
|
374
|
+
ws.Options.KeepAliveInterval = TimeSpan.FromSeconds(30);
|
|
375
|
+
var cts = new CancellationTokenSource();
|
|
376
|
+
var token = cts.Token;
|
|
377
|
+
try
|
|
378
|
+
{
|
|
379
|
+
ws.ConnectAsync(new Uri(wsUrl), token).GetAwaiter().GetResult();
|
|
380
|
+
Console.Error.WriteLine("CONNECTED");
|
|
381
|
+
Console.Error.Flush();
|
|
382
|
+
|
|
383
|
+
var reader = Task.Run(() =>
|
|
384
|
+
{
|
|
385
|
+
var buf = new byte[4 * 1024 * 1024];
|
|
386
|
+
try
|
|
387
|
+
{
|
|
388
|
+
while (ws.State == WebSocketState.Open && !token.IsCancellationRequested)
|
|
389
|
+
{
|
|
390
|
+
var sb = new StringBuilder();
|
|
391
|
+
WebSocketReceiveResult recv;
|
|
392
|
+
do
|
|
393
|
+
{
|
|
394
|
+
var seg = new ArraySegment<byte>(buf);
|
|
395
|
+
recv = ws.ReceiveAsync(seg, token).GetAwaiter().GetResult();
|
|
396
|
+
if (recv.MessageType == WebSocketMessageType.Close)
|
|
397
|
+
{
|
|
398
|
+
Console.Error.WriteLine("READER_CLOSE:" + FormatSocketState(ws));
|
|
399
|
+
Console.Error.Flush();
|
|
400
|
+
return;
|
|
401
|
+
}
|
|
402
|
+
sb.Append(Encoding.UTF8.GetString(buf, 0, recv.Count));
|
|
403
|
+
} while (!recv.EndOfMessage);
|
|
404
|
+
|
|
405
|
+
Console.Out.WriteLine(sb.ToString());
|
|
406
|
+
Console.Out.Flush();
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
catch (OperationCanceledException) { }
|
|
410
|
+
catch (Exception ex)
|
|
411
|
+
{
|
|
412
|
+
Console.Error.WriteLine("READER_ERROR:" + FormatException(ex, ws));
|
|
413
|
+
Console.Error.Flush();
|
|
414
|
+
}
|
|
415
|
+
}, token);
|
|
416
|
+
|
|
417
|
+
string line;
|
|
418
|
+
while ((line = Console.In.ReadLine()) != null)
|
|
419
|
+
{
|
|
420
|
+
if (ws.State != WebSocketState.Open) break;
|
|
421
|
+
var bytes = Encoding.UTF8.GetBytes(line);
|
|
422
|
+
var seg = new ArraySegment<byte>(bytes);
|
|
423
|
+
ws.SendAsync(seg, WebSocketMessageType.Text, true, token).GetAwaiter().GetResult();
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
cts.Cancel();
|
|
427
|
+
reader.Wait(TimeSpan.FromSeconds(2));
|
|
428
|
+
}
|
|
429
|
+
catch (Exception ex)
|
|
430
|
+
{
|
|
431
|
+
Console.Error.WriteLine("FATAL:" + FormatException(ex, ws));
|
|
432
|
+
Console.Error.Flush();
|
|
433
|
+
}
|
|
434
|
+
finally
|
|
435
|
+
{
|
|
436
|
+
if (ws.State == WebSocketState.Open)
|
|
437
|
+
{
|
|
438
|
+
try
|
|
439
|
+
{
|
|
440
|
+
ws.CloseAsync(WebSocketCloseStatus.NormalClosure, "", CancellationToken.None).GetAwaiter().GetResult();
|
|
441
|
+
}
|
|
442
|
+
catch { }
|
|
443
|
+
}
|
|
444
|
+
ws.Dispose();
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
`;
|
|
449
|
+
function createRelayPowerShellScript(csharpSource) {
|
|
450
|
+
return `
|
|
451
|
+
param([string]$WsUrl)
|
|
452
|
+
$ErrorActionPreference = 'Stop'
|
|
453
|
+
[Console]::InputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
454
|
+
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
455
|
+
$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
|
|
456
|
+
Add-Type -TypeDefinition @'
|
|
457
|
+
${csharpSource}
|
|
458
|
+
'@
|
|
459
|
+
[CDPRelay]::Run($WsUrl)
|
|
460
|
+
`;
|
|
461
|
+
}
|
|
462
|
+
/**
|
|
463
|
+
* Returns all PowerShell script sources used by the bridge runner.
|
|
464
|
+
*/
|
|
465
|
+
export function getBridgePowerShellScripts() {
|
|
466
|
+
return {
|
|
467
|
+
launchChrome: LAUNCH_CHROME_PS,
|
|
468
|
+
findExistingChrome: FIND_EXISTING_CHROME_PS,
|
|
469
|
+
getVersion: GET_VERSION_PS,
|
|
470
|
+
resolvePort: RESOLVE_PORT_PS,
|
|
471
|
+
stopChromeByProfilePort: STOP_CHROME_BY_PROFILE_AND_PORT_PS,
|
|
472
|
+
relay: createRelayPowerShellScript(RELAY_CSHARP)
|
|
473
|
+
};
|
|
474
|
+
}
|
|
475
|
+
//# sourceMappingURL=bridge-powershell-scripts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bridge-powershell-scripts.js","sourceRoot":"","sources":["../src/bridge-powershell-scripts.ts"],"names":[],"mappings":"AAAA,MAAM,gBAAgB,GAAG;;;;;;;;;;CAUxB,CAAC;AAEF,MAAM,uBAAuB,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+E/B,CAAC;AAEF,MAAM,cAAc,GAAG;;;;;;;;;;;;;CAatB,CAAC;AAEF,MAAM,eAAe,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwDvB,CAAC;AAEF,MAAM,kCAAkC,GAAG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAqF1C,CAAC;AAEF,MAAM,YAAY,GAAG,MAAM,CAAC,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAuM9B,CAAC;AAEF,SAAS,2BAA2B,CAAC,YAAoB;IACvD,OAAO;;;;;;;EAOP,YAAY;;;CAGb,CAAC;AACF,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,0BAA0B;IAQxC,OAAO;QACL,YAAY,EAAE,gBAAgB;QAC9B,kBAAkB,EAAE,uBAAuB;QAC3C,UAAU,EAAE,cAAc;QAC1B,WAAW,EAAE,eAAe;QAC5B,uBAAuB,EAAE,kCAAkC;QAC3D,KAAK,EAAE,2BAA2B,CAAC,YAAY,CAAC;KACjD,CAAC;AACJ,CAAC"}
|