Skip to content

Commit e6838b5

Browse files
committed
release: v1.10.22
1 parent 5524aed commit e6838b5

16 files changed

+227
-122
lines changed

abx_plugins/plugins/chrome/chrome_utils.js

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ function waitForDebugPort(port, timeout = 30000) {
238238
async function killZombieChrome(snapDir = null, options = {}) {
239239
snapDir = snapDir || getSnapDir();
240240
let killed = 0;
241+
const currentPid = process.pid;
241242
const quiet = Boolean(options.quiet);
242243
const excludeCurrentRuntimeDirs = options.excludeCurrentRuntimeDirs !== false;
243244
const excludeCrawlDirs = new Set(
@@ -587,6 +588,7 @@ async function killZombieChrome(snapDir = null, options = {}) {
587588
try {
588589
const pid = parseInt(fs.readFileSync(pidFile, 'utf8').trim(), 10);
589590
if (isNaN(pid) || pid <= 0) continue;
591+
if (pid === currentPid) continue;
590592
if (!isProcessAlive(pid)) {
591593
try { fs.unlinkSync(pidFile); } catch (error) {}
592594
continue;
@@ -617,6 +619,9 @@ async function killZombieChrome(snapDir = null, options = {}) {
617619
if (handledHookPids.has(pid)) {
618620
continue;
619621
}
622+
if (pid === currentPid) {
623+
continue;
624+
}
620625
const currentWorkingDir = getProcessWorkingDir(pid);
621626
if (!currentWorkingDir) {
622627
continue;
@@ -3519,9 +3524,6 @@ async function closeBrowserInChromeSession(options = {}) {
35193524
if (!closed) {
35203525
closed = await killChrome(pid, outputDir);
35213526
}
3522-
if (closed) {
3523-
closed = await waitForChromeProcessTreeExit(pid, debugPort, forceKillTimeoutMs);
3524-
}
35253527
} else if (cdpUrl) {
35263528
closed = await waitForBrowserEndpointGone(cdpUrl, forceKillTimeoutMs);
35273529
}

abx_plugins/plugins/chrome/on_CrawlSetup__90_chrome_launch.daemon.bg.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ let chromeCdpUrl = null;
5454
let chromeProcessIsLocal = getEnv('CHROME_CDP_URL', '') ? false : getEnvBool('CHROME_IS_LOCAL', true);
5555
let shouldCloseOnCleanup = false;
5656
let puppeteer = null;
57+
let cleanupPromise = null;
5758

5859
function getPortFromCdpUrl(cdpUrl) {
5960
if (!cdpUrl) return null;
@@ -63,6 +64,10 @@ function getPortFromCdpUrl(cdpUrl) {
6364

6465
// Cleanup handler for SIGTERM
6566
async function cleanup() {
67+
if (cleanupPromise) {
68+
return cleanupPromise;
69+
}
70+
cleanupPromise = (async () => {
6671
if (shouldCloseOnCleanup) {
6772
console.log(`shutting down ${CHROME_BINARY} cleanly...`);
6873
const closed = await closeBrowserInChromeSession({
@@ -87,6 +92,8 @@ async function cleanup() {
8792
console.log(JSON.stringify({ succeeded: true, skipped: chromeCdpUrl ? true : false })); // we didn't launch it (we connected over CDP), and we didn't kill it, so we skipped basically the whole hook
8893
}
8994
process.exit(0);
95+
})();
96+
return cleanupPromise;
9097
}
9198

9299
// Register signal handlers

abx_plugins/plugins/chrome/on_Snapshot__09_chrome_launch.daemon.bg.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,13 @@ let chromePid = null;
3636
let chromeCdpUrl = null;
3737
let chromeProcessIsLocal = getEnv('CHROME_CDP_URL', '') ? false : getEnvBool('CHROME_IS_LOCAL', true);
3838
let shouldCloseOnCleanup = false;
39+
let cleanupPromise = null;
3940

4041
async function cleanup() {
42+
if (cleanupPromise) {
43+
return cleanupPromise;
44+
}
45+
cleanupPromise = (async () => {
4146
if (shouldCloseOnCleanup) {
4247
const closed = await closeBrowserInChromeSession({
4348
cdpUrl: chromeCdpUrl,
@@ -52,6 +57,8 @@ async function cleanup() {
5257
}
5358
}
5459
process.exit(0);
60+
})();
61+
return cleanupPromise;
5562
}
5663

5764
process.on('SIGTERM', cleanup);

abx_plugins/plugins/chrome/tests/chrome_test_helpers.py

Lines changed: 52 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,42 @@ def do_GET(self) -> None:
290290
)
291291
return
292292

293+
if path == "/claudechrome":
294+
self._write(
295+
200,
296+
"""<!doctype html>
297+
<html>
298+
<head>
299+
<meta charset="utf-8">
300+
<title>Claude Chrome Test Page</title>
301+
<style>
302+
body { margin: 20px; font-family: sans-serif; }
303+
.hidden-content { display: none; }
304+
#expand-btn {
305+
padding: 10px 20px;
306+
font-size: 16px;
307+
cursor: pointer;
308+
background: #4a90d9;
309+
color: white;
310+
border: none;
311+
border-radius: 4px;
312+
}
313+
</style>
314+
</head>
315+
<body>
316+
<h1>Test Page for Claude Chrome</h1>
317+
<p>This page has a button that reveals hidden content.</p>
318+
<button id="expand-btn" onclick="document.getElementById('hidden').style.display='block'; this.textContent='Expanded!';">
319+
Show More
320+
</button>
321+
<div id="hidden" class="hidden-content">
322+
<p>This content was hidden and is now visible after clicking the button.</p>
323+
</div>
324+
</body>
325+
</html>""",
326+
)
327+
return
328+
293329
self._write(
294330
404,
295331
"<html><head><title>Not Found</title></head><body><h1>404</h1></body></html>",
@@ -375,6 +411,7 @@ def _build_test_urls(
375411
"popup_child_url": f"{base}/popup-child",
376412
"static_file_url": f"{base}/static/test.txt",
377413
"json_url": f"{base}/api/data.json",
414+
"claudechrome_url": f"{base}/claudechrome",
378415
}
379416
if https_base_url:
380417
https_base = https_base_url.rstrip("/")
@@ -1244,23 +1281,25 @@ def launch_chromium_session(
12441281
chrome_launch_process._stderr_log = stderr_log
12451282

12461283
cdp_url = None
1247-
launch_failed = False
1284+
launch_exit_code = None
12481285
launch_stdout = ""
12491286
launch_stderr = ""
12501287

12511288
for _ in range(timeout):
1252-
if chrome_launch_process.poll() is not None:
1253-
stdout_handle.flush()
1254-
stderr_handle.flush()
1255-
launch_stdout = stdout_log.read_text(encoding="utf-8", errors="replace")
1256-
launch_stderr = stderr_log.read_text(encoding="utf-8", errors="replace")
1257-
launch_failed = True
1258-
break
12591289
cdp_file = chrome_dir / "cdp_url.txt"
12601290
if cdp_file.exists():
12611291
cdp_url = cdp_file.read_text().strip()
12621292
if cdp_url:
12631293
break
1294+
process_status = chrome_launch_process.poll()
1295+
if process_status is not None:
1296+
stdout_handle.flush()
1297+
stderr_handle.flush()
1298+
if cdp_file.exists():
1299+
cdp_url = cdp_file.read_text().strip()
1300+
if cdp_url:
1301+
break
1302+
launch_exit_code = process_status
12641303
time.sleep(1)
12651304

12661305
if cdp_url:
@@ -1276,17 +1315,20 @@ def launch_chromium_session(
12761315
chrome_launch_process._chrome_pid = None
12771316
return chrome_launch_process, cdp_url
12781317

1279-
if not launch_failed:
1318+
if launch_exit_code is None:
12801319
chrome_launch_process.kill()
12811320
stdout_handle.flush()
12821321
stderr_handle.flush()
12831322
launch_stdout = stdout_log.read_text(encoding="utf-8", errors="replace")
12841323
launch_stderr = stderr_log.read_text(encoding="utf-8", errors="replace")
1324+
else:
1325+
launch_stdout = stdout_log.read_text(encoding="utf-8", errors="replace")
1326+
launch_stderr = stderr_log.read_text(encoding="utf-8", errors="replace")
12851327

12861328
stdout_handle.close()
12871329
stderr_handle.close()
12881330

1289-
if launch_failed:
1331+
if launch_exit_code is not None:
12901332
raise RuntimeError(
12911333
f"Chromium launch failed:\nStdout: {launch_stdout}\nStderr: {launch_stderr}",
12921334
)

abx_plugins/plugins/chrome/tests/test_chrome.py

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1696,12 +1696,6 @@ def test_cdp_url_is_not_published_before_extensions_metadata():
16961696
saw_cdp = False
16971697

16981698
while time.time() < deadline:
1699-
if chrome_launch_process.poll() is not None:
1700-
stdout, stderr = chrome_launch_process.communicate()
1701-
pytest.fail(
1702-
f"Chrome launch exited early:\nStdout: {stdout}\nStderr: {stderr}",
1703-
)
1704-
17051699
saw_extensions = extensions_file.exists()
17061700
saw_cdp = cdp_file.exists()
17071701

@@ -1713,6 +1707,13 @@ def test_cdp_url_is_not_published_before_extensions_metadata():
17131707
if saw_cdp and saw_extensions:
17141708
break
17151709

1710+
if chrome_launch_process.poll() is not None:
1711+
stdout, stderr = chrome_launch_process.communicate()
1712+
pytest.fail(
1713+
f"Chrome launch exited early:\nStdout: {stdout}\nStderr: {stderr}",
1714+
)
1715+
time.sleep(1)
1716+
17161717
time.sleep(0.1)
17171718

17181719
assert saw_extensions, "chrome launch should create extensions.json"
@@ -1749,15 +1750,15 @@ def test_crawl_wait_accepts_http_cdp_url_for_external_browser(chrome_test_url):
17491750

17501751
try:
17511752
for _ in range(30):
1753+
if (provider_chrome_dir / "cdp_url.txt").exists() and (
1754+
provider_chrome_dir / "chrome.pid"
1755+
).exists():
1756+
break
17521757
if provider_process.poll() is not None:
17531758
stdout, stderr = provider_process.communicate()
17541759
pytest.fail(
17551760
f"provider launch exited early:\nStdout: {stdout}\nStderr: {stderr}",
17561761
)
1757-
if (provider_chrome_dir / "cdp_url.txt").exists() and (
1758-
provider_chrome_dir / "chrome.pid"
1759-
).exists():
1760-
break
17611762
time.sleep(1)
17621763

17631764
provider_cdp_url = (provider_chrome_dir / "cdp_url.txt").read_text().strip()
@@ -1812,8 +1813,8 @@ def test_crawl_wait_accepts_http_cdp_url_for_external_browser(chrome_test_url):
18121813
f"crawl wait should succeed for adopted HTTP endpoint:\n"
18131814
f"Stdout: {crawl_wait.stdout}\nStderr: {crawl_wait.stderr}"
18141815
)
1815-
assert "pid=external" in crawl_wait.stderr
1816-
assert provider_http_url in crawl_wait.stderr
1816+
assert "pid=external" in crawl_wait.stdout
1817+
assert provider_http_url in crawl_wait.stdout
18171818
finally:
18181819
_cleanup_launch_process(provider_process, provider_chrome_dir)
18191820

@@ -2007,13 +2008,13 @@ def test_shared_dir_crawl_snapshot_file_order_and_gating(chrome_test_url):
20072008
)
20082009

20092010
for _ in range(30):
2011+
if all(path.exists() for path in shared_files.values()):
2012+
break
20102013
if chrome_launch_process.poll() is not None:
20112014
stdout, stderr = chrome_launch_process.communicate()
20122015
pytest.fail(
20132016
f"Chrome launch exited early:\nStdout: {stdout}\nStderr: {stderr}",
20142017
)
2015-
if all(path.exists() for path in shared_files.values()):
2016-
break
20172018
time.sleep(1)
20182019

20192020
assert all(path.exists() for path in shared_files.values()), (
@@ -2028,7 +2029,9 @@ def test_shared_dir_crawl_snapshot_file_order_and_gating(chrome_test_url):
20282029
extensions_before = (
20292030
extensions_file.read_text() if extensions_file.exists() else None
20302031
)
2031-
assert cdp_url_before.startswith("ws://127.0.0.1:"), cdp_url_before
2032+
assert cdp_url_before.startswith(("ws://127.0.0.1:", "ws://localhost:")), (
2033+
cdp_url_before
2034+
)
20322035
port_before = str(_port_from_cdp_url(cdp_url_before))
20332036
os.kill(int(chrome_pid_before), 0)
20342037
assert _fetch_devtools_targets(cdp_url_before), (
@@ -2051,8 +2054,8 @@ def test_shared_dir_crawl_snapshot_file_order_and_gating(chrome_test_url):
20512054
f"crawl wait should succeed before snapshot setup:\n"
20522055
f"Stdout: {crawl_wait.stdout}\nStderr: {crawl_wait.stderr}"
20532056
)
2054-
assert f"pid={chrome_pid_before}" in crawl_wait.stderr
2055-
assert f"127.0.0.1:{port_before}" in crawl_wait.stderr
2057+
assert f"pid={chrome_pid_before}" in crawl_wait.stdout
2058+
assert f":{port_before}" in crawl_wait.stdout
20562059
assert not any(path.exists() for path in snapshot_files.values()), (
20572060
"crawl wait should not create snapshot-scoped files"
20582061
)
@@ -2255,13 +2258,13 @@ def test_shared_dir_extensions_metadata_created_and_preserved_when_enabled(
22552258
env=env,
22562259
)
22572260
for _ in range(30):
2261+
if extensions_file.exists() and (chrome_dir / "cdp_url.txt").exists():
2262+
break
22582263
if chrome_launch_process.poll() is not None:
22592264
stdout, stderr = chrome_launch_process.communicate()
22602265
pytest.fail(
22612266
f"Chrome launch exited early:\nStdout: {stdout}\nStderr: {stderr}",
22622267
)
2263-
if extensions_file.exists() and (chrome_dir / "cdp_url.txt").exists():
2264-
break
22652268
time.sleep(1)
22662269

22672270
assert extensions_file.exists(), (
@@ -2364,7 +2367,7 @@ def test_chrome_wait_rejects_stale_cdp_markers(chrome_test_url):
23642367
assert result.returncode == 1, (
23652368
f"chrome_wait should fail for stale CDP markers: {result.stderr}\nStdout: {result.stdout}"
23662369
)
2367-
payload = json.loads(result.stdout.strip())
2370+
payload = json.loads(result.stdout.strip().splitlines()[-1])
23682371
assert payload["status"] == "failed"
23692372
assert (
23702373
payload["output_str"]
@@ -2427,8 +2430,7 @@ def test_crawl_wait_retries_until_published_cdp_endpoint_becomes_connectable(
24272430
"crawl wait should retry until the published endpoint becomes connectable:\n"
24282431
f"Stdout: {stdout}\nStderr: {stderr}"
24292432
)
2430-
assert stdout.strip() == "", stdout
2431-
assert "Chrome session ready" in stderr, stderr
2433+
assert "Chromium ready pid=external" in stdout, stdout
24322434
finally:
24332435
if wait_process is not None and wait_process.poll() is None:
24342436
wait_process.kill()
@@ -3101,15 +3103,15 @@ def test_chrome_cleanup_on_crawl_end():
31013103

31023104
# Wait for Chrome launch state files and fail fast on early hook exit.
31033105
for _ in range(15):
3106+
if (chrome_dir / "cdp_url.txt").exists() and (
3107+
chrome_dir / "chrome.pid"
3108+
).exists():
3109+
break
31043110
if chrome_launch_process.poll() is not None:
31053111
stdout, stderr = chrome_launch_process.communicate()
31063112
pytest.fail(
31073113
f"Chrome launch process exited early:\nStdout: {stdout}\nStderr: {stderr}",
31083114
)
3109-
if (chrome_dir / "cdp_url.txt").exists() and (
3110-
chrome_dir / "chrome.pid"
3111-
).exists():
3112-
break
31133115
time.sleep(1)
31143116

31153117
# Verify Chrome is running

0 commit comments

Comments
 (0)