diff --git a/.github/release-notes/v0.2.0-beta.9.json b/.github/release-notes/v0.2.0-beta.9.json
new file mode 100644
index 0000000..60e25e5
--- /dev/null
+++ b/.github/release-notes/v0.2.0-beta.9.json
@@ -0,0 +1,39 @@
+{
+ "format": "codex-styler-release-notes-v1",
+ "version": "0.2.0-beta.9",
+ "defaultLocale": "en",
+ "locales": {
+ "en": {
+ "summary": "Managed launch now selects the stable Codex workspace from every trusted loopback CDP page and reports bounded, actionable failures.",
+ "highlights": [
+ "All trusted loopback page targets are checked, so avatar overlays and other auxiliary Codex pages no longer hide the main workspace.",
+ "The same ready workspace WebSocket must pass three consecutive samples even when target ordering changes.",
+ "Per-candidate and overall time bounds prevent a temporary or unresponsive renderer from blocking launch indefinitely.",
+ "The affected multi-page macOS launch and theme-application path was verified with Codex 26.715.61943 on Apple Silicon."
+ ],
+ "fixes": [
+ "Fixed Restart Codex & apply remaining disconnected when an avatar-overlay page appeared before the main workspace.",
+ "Fixed the first trusted CDP candidate preventing later ready candidates from being evaluated.",
+ "Fixed failed launch discovery remaining permanently in the launching state with no lifecycle event.",
+ "Added redacted launch-error diagnostics while preserving loopback-only validation and renderer-replacement recovery.",
+ "Cleared the frontend dependency audit by resolving astro, fast-uri, js-yaml, nanoid, and postcss to patched releases."
+ ]
+ },
+ "zh-CN": {
+ "summary": "受控启动现在会从全部可信的本地 CDP 页面中选择稳定的 Codex 主工作区,并以有界、可诊断的方式处理失败。",
+ "highlights": [
+ "检查全部可信的本地 page 目标,头像浮层及其他 Codex 辅助页面不再遮蔽主工作区。",
+ "即使目标顺序发生变化,同一工作区 WebSocket 仍须连续三次通过就绪检查。",
+ "单候选和整体等待均有明确上限,临时或无响应的渲染器不会再无限阻塞启动。",
+ "已在 Apple Silicon 上使用 Codex 26.715.61943 验证受影响的多页面 macOS 启动与主题应用链路。"
+ ],
+ "fixes": [
+ "修复头像浮层页面排在主工作区之前时,“重启 Codex 并应用”始终保持未连接的问题。",
+ "修复第一个可信 CDP 候选阻止后续已就绪候选继续检查的问题。",
+ "修复启动发现失败后永久停留在 launching 且没有生命周期事件的问题。",
+ "新增脱敏的启动错误诊断,同时保留仅限本机回环的校验与渲染器替换恢复机制。",
+ "已将 astro、fast-uri、js-yaml、nanoid 与 postcss 升级到补丁版本,清零前端依赖审计告警。"
+ ]
+ }
+ }
+}
diff --git a/.github/release-notes/v0.2.0-beta.9.md b/.github/release-notes/v0.2.0-beta.9.md
new file mode 100644
index 0000000..f1b5665
--- /dev/null
+++ b/.github/release-notes/v0.2.0-beta.9.md
@@ -0,0 +1,106 @@
+# Codex Styler 0.2.0-beta.9 — Reliable Multi-page Codex Launch
+
+Beta 9 fixes managed launch on Codex sessions that expose more than one trusted
+CDP page. Styler now checks every loopback candidate, selects the stable main
+workspace, and records an actionable failure instead of remaining indefinitely
+in `launching` when no usable renderer appears.
+
+Release tag: `v0.2.0-beta.9`
+
+## Download and install
+
+### macOS 13+ on Apple Silicon
+
+1. Download `Codex-Styler_0.2.0-beta.9_aarch64-unsigned.dmg`.
+2. Compare its SHA-256 digest with `SHA256SUMS.txt`.
+3. Open the DMG and drag Codex Styler to Applications.
+4. On first launch, Control-click Codex Styler and choose **Open**.
+
+The macOS app is ad-hoc signed, does not have an Apple Developer ID signature,
+and is not notarized. Do not disable Gatekeeper globally.
+
+### Windows 11 x64 — community-validation Beta
+
+1. Download `Codex-Styler_0.2.0-beta.9_x64-unsigned-setup.exe`.
+2. Compare its SHA-256 digest with `SHA256SUMS.txt`.
+3. Review the GitHub build attestation, then run the per-user installer.
+
+The Windows installer is not Authenticode-signed, so SmartScreen may show an
+unknown-publisher warning. It is built and structurally verified on a
+GitHub-hosted Windows runner, but still requires independent real-device
+evidence before RC.
+
+## Multi-page CDP compatibility
+
+- Styler evaluates all trusted `page` targets returned by the loopback-only CDP
+ endpoint instead of assuming the first target is the Codex workspace.
+- Auxiliary pages such as avatar overlays, startup, login, upgrade, or settings
+ surfaces can appear before or after the main page without blocking it.
+- Every candidate must retain the reserved `127.0.0.1` WebSocket port and pass
+ the live workspace readiness check.
+- Candidate checks run independently with a bounded timeout, so an unresponsive
+ temporary renderer cannot prevent a later workspace from being selected.
+- The same workspace WebSocket must be ready in three consecutive polls even if
+ the order of CDP targets changes between samples.
+
+## Failure recovery and diagnostics
+
+- The complete discovery operation has a hard time limit.
+- A failed launch now transitions to an explicit error state, retains the
+ redacted error message, and records a `launch/error` lifecycle event.
+- Diagnostics no longer report an indefinitely stale `launching` state with no
+ lifecycle history after target discovery fails.
+- Existing renderer-replacement recovery during apply remains active.
+
+## Validation
+
+- The regression suite covers the overlay before the workspace, the workspace
+ before the overlay, multiple unready trusted pages, a timed-out first
+ candidate, candidate-order changes, stable sampling, and bounded failure.
+- Rust formatting, Clippy with warnings denied, all Rust tests, all desktop
+ frontend tests, TypeScript checks, and production builds are release gates.
+- The frontend dependency audit resolves `astro`, `fast-uri`, `js-yaml`,
+ `nanoid`, and `postcss` to patched releases with no remaining advisory.
+- An affected Apple Silicon Mac running Codex `26.715.61943` exercised managed
+ restart and theme application with an avatar-overlay page preceding the main
+ workspace. The focused regression passed after this fix.
+
+The affected-device run confirms this launch/apply regression. It does not by
+itself claim the full route, companion, pause, restore, update, uninstall, and
+multi-device evidence required for RC or for adding that Codex build to the
+complete public tested matrix.
+
+## Release evidence
+
+- `SHA256SUMS.txt` covers both installers and the macOS updater archive.
+- `Codex-Styler_0.2.0-beta.9.spdx.json` exports the dependency graph as SPDX
+ 2.3.
+- GitHub artifact attestations cover both installers; platform updater
+ artifacts carry Tauri signatures.
+- `latest.json` contains macOS Apple Silicon and Windows x64 update entries.
+- `release-notes.json` contains validated English and Simplified Chinese
+ summaries for the pre-download update panel.
+
+Verify installers with GitHub CLI:
+
+```bash
+gh attestation verify Codex-Styler_0.2.0-beta.9_aarch64-unsigned.dmg \
+ --repo xuhuanstudio/codex-styler
+
+gh attestation verify Codex-Styler_0.2.0-beta.9_x64-unsigned-setup.exe \
+ --repo xuhuanstudio/codex-styler
+```
+
+## Beta limitations
+
+- Windows startup, managed restart, update, restore, and uninstall still need
+ independent community real-device reports before RC.
+- Composer Interactions depend on Codex's native configuration semantics and
+ may require an adapter update after a substantial upstream control redesign.
+- Complex-background automatic segmentation, video backgrounds, multi-entity
+ scenes, 3D/WebGL, macOS Intel, Windows ARM64, and Linux remain outside 0.2.
+- macOS notarization and Windows Authenticode signing remain required before v1
+ Stable.
+
+Codex Styler is unofficial and is not affiliated with, endorsed by, or
+sponsored by OpenAI.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 441851e..4a1ddc2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,31 @@ All notable changes will be documented here. The project follows Semantic Versio
## [Unreleased]
+## [0.2.0-beta.9] - 2026-08-09
+
+### Changed
+
+- Managed launch now evaluates every trusted loopback CDP page concurrently and
+ selects the workspace renderer that passes the live readiness check, instead
+ of assuming the first trusted page is the main Codex window.
+- Ready-renderer tracking is keyed by WebSocket identity, so target ordering can
+ change while the same workspace still satisfies three consecutive stable
+ samples.
+
+### Fixed
+
+- Codex installations that expose an avatar overlay or another auxiliary app
+ page before the main workspace no longer remain disconnected after
+ **Restart Codex & apply**.
+- A slow or unresponsive auxiliary renderer can no longer block discovery of a
+ later ready workspace; each candidate and the overall discovery operation
+ now have explicit time bounds.
+- Failed managed launches now enter an actionable error state and record a
+ redacted `launch/error` lifecycle event instead of remaining indefinitely in
+ `launching` with an empty diagnostic history.
+- Cleared the frontend dependency audit by resolving `astro`, `fast-uri`,
+ `js-yaml`, `nanoid`, and `postcss` to fixed releases.
+
## [0.2.0-beta.8] - 2026-07-20
### Added
diff --git a/COMPATIBILITY.md b/COMPATIBILITY.md
index 4e08888..cc5e35a 100644
--- a/COMPATIBILITY.md
+++ b/COMPATIBILITY.md
@@ -2,13 +2,13 @@
Compatibility is claimed only after real-device validation.
-| Platform | Architecture | Beta 8 status | RC requirement |
-| ---------- | ------------- | ----------------------------------------------------------------------- | ----------------------------------------- |
-| macOS 13+ | Apple Silicon | Ad-hoc signed Beta; locally built, installed, and exercised | Two complete real-device runs |
-| Windows 11 | x64 | CI-built unsigned Beta; lifecycle fixes await community device evidence | Two complete independent real-device runs |
-| macOS | Intel | Not verified | Out of 0.2 scope |
-| Windows | ARM64 | Not verified | Out of 0.2 scope |
-| Linux | — | Unsupported | Out of 0.2 scope |
+| Platform | Architecture | Beta 9 status | RC requirement |
+| ---------- | ------------- | ------------------------------------------------------------------------------- | ----------------------------------------- |
+| macOS 13+ | Apple Silicon | Ad-hoc signed Beta; multi-page CDP restart/apply verified on an affected device | Two complete real-device runs |
+| Windows 11 | x64 | CI-built unsigned Beta; lifecycle fixes await community device evidence | Two complete independent real-device runs |
+| macOS | Intel | Not verified | Out of 0.2 scope |
+| Windows | ARM64 | Not verified | Out of 0.2 scope |
+| Linux | — | Unsupported | Out of 0.2 scope |
## Codex versions and runtime strategy
@@ -22,6 +22,13 @@ The health check runs after initial application and again after Codex route or D
The semantic adapter currently has direct fixture evidence for Codex versions `26.707.72221` and `26.707.91948`. A version enters the public tested matrix only after detection, launch, application, route changes, companion update, pause, restore, normal restart, and the supported-platform lifecycle are verified on a real device.
+Beta 9 also has focused Apple Silicon evidence for Codex `26.715.61943` in a
+multi-page CDP session where an avatar-overlay page preceded the main
+workspace. Managed restart and theme application succeeded after the target
+selection fix. This focused regression run is recorded separately and does not
+claim the complete lifecycle evidence required to add that Codex version to the
+public tested matrix.
+
Codex Styler does not promise permanent compatibility with every Codex release. Run Settings → **Diagnostics & compatibility**, preview the redacted report, and attach the exported ZIP manually to a compatibility issue when reporting a regression.
## Installation discovery
diff --git a/README.md b/README.md
index 7a1ad7d..cfa1971 100644
--- a/README.md
+++ b/README.md
@@ -15,21 +15,21 @@
简体中文 ·
- Download Beta ·
+ Download Beta ·
Website ·
Documentation
-
+
> [!IMPORTANT]
-> Codex Styler 0.2 is a Creator Beta. The macOS DMG is ad-hoc signed but not notarized, and the Windows EXE is not Authenticode-signed. Windows Beta 8 retains the rebuilt Store launch and bounded restart lifecycle and still awaits community real-device verification; v1 Stable remains gated on complete evidence and platform code signing.
+> Codex Styler 0.2 is a Creator Beta. The macOS DMG is ad-hoc signed but not notarized, and the Windows EXE is not Authenticode-signed. Beta 9 adds bounded multi-page CDP target selection verified on an affected Apple Silicon Mac; Windows still awaits community real-device verification, and v1 Stable remains gated on complete evidence and platform code signing.

@@ -115,14 +115,14 @@ Companions are selected independently from themes. Native Refined, Nocturne Stud
Every shipped image and sprite is original project artwork. The reference repositories are studied for ideas only; their assets and source are not redistributed.
-## Download Creator Beta 8
+## Download Creator Beta 9
-- **[macOS 13+ / Apple Silicon — DMG](https://github.com/xuhuanstudio/codex-styler/releases/download/v0.2.0-beta.8/Codex-Styler_0.2.0-beta.8_aarch64-unsigned.dmg)**
-- **[Windows 11 / x64 — installer EXE](https://github.com/xuhuanstudio/codex-styler/releases/download/v0.2.0-beta.8/Codex-Styler_0.2.0-beta.8_x64-unsigned-setup.exe)**
+- **[macOS 13+ / Apple Silicon — DMG](https://github.com/xuhuanstudio/codex-styler/releases/download/v0.2.0-beta.9/Codex-Styler_0.2.0-beta.9_aarch64-unsigned.dmg)**
+- **[Windows 11 / x64 — installer EXE](https://github.com/xuhuanstudio/codex-styler/releases/download/v0.2.0-beta.9/Codex-Styler_0.2.0-beta.9_x64-unsigned-setup.exe)**
On macOS, open the DMG, drag Codex Styler to Applications, then Control-click the app and choose **Open** on first launch. On Windows, SmartScreen may warn because this Beta does not yet have an Authenticode certificate; inspect the published checksum and provenance before continuing. Never disable Gatekeeper or SmartScreen globally.
-The [pre-release page](https://github.com/xuhuanstudio/codex-styler/releases/tag/v0.2.0-beta.8) includes SHA-256 checksums, an SPDX SBOM, build attestations, updater artifacts, tested scope, and known limitations. Intel macOS is not included. Windows Beta 8 is CI-built and structurally verified but remains marked as awaiting community real-device validation.
+The [pre-release page](https://github.com/xuhuanstudio/codex-styler/releases/tag/v0.2.0-beta.9) includes SHA-256 checksums, an SPDX SBOM, build attestations, updater artifacts, tested scope, and known limitations. Intel macOS is not included. Windows Beta 9 is CI-built and structurally verified but remains marked as awaiting community real-device validation.
## Run from source
diff --git a/README.zh-CN.md b/README.zh-CN.md
index 9f0a615..eb3d829 100644
--- a/README.zh-CN.md
+++ b/README.zh-CN.md
@@ -15,13 +15,13 @@
English ·
- 下载 Beta ·
+ 下载 Beta ·
项目网站 ·
使用文档
> [!IMPORTANT]
-> Codex Styler 0.2 当前处于创作器 Beta 阶段。macOS DMG 采用临时 ad-hoc 签名但尚未公证,Windows EXE 尚未进行 Authenticode 代码签名;Windows Beta 8 延续已重构的 Store 启动与有界重启链路,但仍等待社区实机验证,完成证据闭环和双平台签名前不会标记为稳定版。
+> Codex Styler 0.2 当前处于创作器 Beta 阶段。macOS DMG 采用临时 ad-hoc 签名但尚未公证,Windows EXE 尚未进行 Authenticode 代码签名;Beta 9 新增经过受影响 Apple Silicon Mac 验证的有界多页面 CDP 目标选择,Windows 仍等待社区实机验证,完成证据闭环和双平台签名前不会标记为稳定版。

@@ -107,14 +107,14 @@ Codex Styler 管理器可以跟随系统,也可以固定使用亮色或暗色
所有随项目分发的图片和序列帧都是原创资源。参考仓库只用于研究思路,不复制或重新分发其代码与素材。
-## 下载创作器 Beta 8
+## 下载创作器 Beta 9
-- **[macOS 13+ / Apple Silicon — DMG](https://github.com/xuhuanstudio/codex-styler/releases/download/v0.2.0-beta.8/Codex-Styler_0.2.0-beta.8_aarch64-unsigned.dmg)**
-- **[Windows 11 / x64 — 安装 EXE](https://github.com/xuhuanstudio/codex-styler/releases/download/v0.2.0-beta.8/Codex-Styler_0.2.0-beta.8_x64-unsigned-setup.exe)**
+- **[macOS 13+ / Apple Silicon — DMG](https://github.com/xuhuanstudio/codex-styler/releases/download/v0.2.0-beta.9/Codex-Styler_0.2.0-beta.9_aarch64-unsigned.dmg)**
+- **[Windows 11 / x64 — 安装 EXE](https://github.com/xuhuanstudio/codex-styler/releases/download/v0.2.0-beta.9/Codex-Styler_0.2.0-beta.9_x64-unsigned-setup.exe)**
在 macOS 上打开 DMG,把 Codex Styler 拖入“应用程序”,首次启动时按住 Control 点击应用并选择“打开”。Windows 版因为当前 Beta 尚无 Authenticode 证书,SmartScreen 可能显示提醒;继续前请先核对 Release 中的校验和与构建证明。不要全局关闭 Gatekeeper 或 SmartScreen。
-[预发布页面](https://github.com/xuhuanstudio/codex-styler/releases/tag/v0.2.0-beta.8)同时提供 SHA-256 校验文件、SPDX SBOM、构建证明、更新包、已验证范围和已知限制。本次不提供 Intel Mac 安装包;Windows Beta 8 由 CI 构建并完成结构校验,但仍明确标记为等待社区实机验证。
+[预发布页面](https://github.com/xuhuanstudio/codex-styler/releases/tag/v0.2.0-beta.9)同时提供 SHA-256 校验文件、SPDX SBOM、构建证明、更新包、已验证范围和已知限制。本次不提供 Intel Mac 安装包;Windows Beta 9 由 CI 构建并完成结构校验,但仍明确标记为等待社区实机验证。
## 从源码运行
diff --git a/ROADMAP.md b/ROADMAP.md
index fd07c64..f97051d 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -107,8 +107,9 @@ depends on an undocumented manual restart or hidden application state.
6. `v0.2.0-beta.6`: ordinary-user setup journey, one application model, compact resource details, and readability/accessibility convergence.
7. `v0.2.0-beta.7`: creator task completion, observable validation, and full error/recovery coverage without new formats.
8. `v0.2.0-beta.8`: verified composer interactions, current-Codex compatibility recovery, polished generated interaction assets, and documentation-complete release evidence.
-9. `v0.2.0-rc.1`: at least two independent complete macOS and Windows device runs; no P0/P1; migration, upgrade, and recovery pass.
-10. `v0.2.0`: only when RC evidence supports it.
+9. `v0.2.0-beta.9`: bounded multi-page CDP workspace discovery, explicit launch-failure diagnostics, and affected-device macOS regression evidence.
+10. `v0.2.0-rc.1`: at least two independent complete macOS and Windows device runs; no P0/P1; migration, upgrade, and recovery pass.
+11. `v0.2.0`: only when RC evidence supports it.
## v1 Stable
diff --git a/apps/desktop/package.json b/apps/desktop/package.json
index 1abbe1b..0cd577b 100644
--- a/apps/desktop/package.json
+++ b/apps/desktop/package.json
@@ -1,6 +1,6 @@
{
"name": "@codex-styler/desktop",
- "version": "0.2.0-beta.8",
+ "version": "0.2.0-beta.9",
"private": true,
"type": "module",
"scripts": {
diff --git a/apps/desktop/src-tauri/Cargo.lock b/apps/desktop/src-tauri/Cargo.lock
index b944ae1..bafc3c5 100644
--- a/apps/desktop/src-tauri/Cargo.lock
+++ b/apps/desktop/src-tauri/Cargo.lock
@@ -392,7 +392,7 @@ dependencies = [
[[package]]
name = "codex-styler"
-version = "0.2.0-beta.8"
+version = "0.2.0-beta.9"
dependencies = [
"base64 0.22.1",
"futures-util",
@@ -3130,9 +3130,9 @@ dependencies = [
[[package]]
name = "serde_json"
-version = "1.0.150"
+version = "1.0.151"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
+checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14"
dependencies = [
"itoa",
"memchr",
diff --git a/apps/desktop/src-tauri/Cargo.toml b/apps/desktop/src-tauri/Cargo.toml
index bc8f813..951cdf6 100644
--- a/apps/desktop/src-tauri/Cargo.toml
+++ b/apps/desktop/src-tauri/Cargo.toml
@@ -1,6 +1,6 @@
[package]
name = "codex-styler"
-version = "0.2.0-beta.8"
+version = "0.2.0-beta.9"
description = "Unofficial local-first theme and interactive scene editor for OpenAI Codex Desktop"
authors = ["Codex Styler contributors"]
license = "Apache-2.0"
diff --git a/apps/desktop/src-tauri/src/cdp.rs b/apps/desktop/src-tauri/src/cdp.rs
index 7a50981..185c3eb 100644
--- a/apps/desktop/src-tauri/src/cdp.rs
+++ b/apps/desktop/src-tauri/src/cdp.rs
@@ -1,11 +1,13 @@
use std::{
+ collections::{HashMap, HashSet},
+ future::Future,
net::TcpListener,
path::Path,
process::{Command, Stdio},
time::Duration,
};
-use futures_util::{SinkExt, StreamExt};
+use futures_util::{SinkExt, StreamExt, stream::FuturesUnordered};
use serde::Deserialize;
use serde_json::{Value, json};
use tokio::time::sleep;
@@ -13,6 +15,11 @@ use tokio_tungstenite::{connect_async, tungstenite::Message};
use crate::{codex::executable_from_install, error::StylerError};
+const TARGET_POLL_INTERVAL: Duration = Duration::from_millis(200);
+const TARGET_HTTP_TIMEOUT: Duration = Duration::from_millis(900);
+const RENDERER_READY_TIMEOUT: Duration = Duration::from_millis(750);
+const READY_TARGET_STABLE_SAMPLES: usize = 3;
+
#[cfg(target_os = "windows")]
use crate::codex::{is_windows_store_install, windows_store_app_user_model_ids};
@@ -112,6 +119,55 @@ struct CdpTarget {
web_socket_debugger_url: Option,
}
+#[derive(Debug, Clone, Copy)]
+struct TargetWaitPolicy {
+ attempts: usize,
+ poll_interval: Duration,
+ candidate_timeout: Duration,
+ overall_timeout: Duration,
+}
+
+impl TargetWaitPolicy {
+ fn production(attempts: usize) -> Self {
+ Self {
+ attempts,
+ poll_interval: TARGET_POLL_INTERVAL,
+ candidate_timeout: RENDERER_READY_TIMEOUT,
+ // Preserve the existing polling budget while placing a hard bound
+ // around HTTP, websocket, and renderer work performed inside it.
+ overall_timeout: TARGET_POLL_INTERVAL
+ .saturating_mul(attempts as u32)
+ .saturating_add(Duration::from_secs(2)),
+ }
+ }
+}
+
+#[derive(Debug, Default)]
+struct ReadyTargetTracker {
+ streaks: HashMap,
+}
+
+impl ReadyTargetTracker {
+ fn observe(&mut self, ready_websockets: Vec) -> Option {
+ let mut seen = HashSet::new();
+ let ready_websockets = ready_websockets
+ .into_iter()
+ .filter(|websocket_url| seen.insert(websocket_url.clone()))
+ .collect::>();
+
+ self.streaks
+ .retain(|websocket_url, _| seen.contains(websocket_url));
+ for websocket_url in ready_websockets {
+ let samples = self.streaks.entry(websocket_url.clone()).or_default();
+ *samples += 1;
+ if *samples >= READY_TARGET_STABLE_SAMPLES {
+ return Some(websocket_url);
+ }
+ }
+ None
+ }
+}
+
pub async fn launch_and_connect(install_path: &str) -> Result {
let port = reserve_loopback_port()?;
let executable =
@@ -129,42 +185,101 @@ pub async fn launch_and_connect(install_path: &str) -> Result Result {
let client = reqwest::Client::builder()
- .timeout(Duration::from_millis(900))
+ .timeout(TARGET_HTTP_TIMEOUT)
.build()?;
let endpoint = format!("http://127.0.0.1:{port}/json/list");
- let mut stable_websocket = None;
- let mut stable_samples = 0;
- for _ in 0..attempts {
- if let Ok(response) = client.get(&endpoint).send().await
- && let Ok(targets) = response.json::>().await
- && let Some(target) = targets.into_iter().find(is_trusted_codex_target)
- && let Some(websocket_url) = target.web_socket_debugger_url
- && is_loopback_debugger_url(&websocket_url, port)
- {
- if renderer_is_ready(&websocket_url).await {
- if stable_websocket.as_deref() == Some(websocket_url.as_str()) {
- stable_samples += 1;
- } else {
- stable_websocket = Some(websocket_url.clone());
- stable_samples = 1;
- }
- // Electron can expose a trusted page before the React shell has
- // finished replacing its bootstrap renderer. Requiring three
- // consecutive ready samples prevents applying to that transient
- // page and losing the runtime during the first launch.
- if stable_samples >= 3 {
- return Ok(websocket_url);
- }
+ wait_for_ready_target_with(
+ port,
+ TargetWaitPolicy::production(attempts),
+ move || {
+ let client = client.clone();
+ let endpoint = endpoint.clone();
+ async move {
+ let response = client.get(endpoint).send().await.ok()?;
+ response.json::>().await.ok()
+ }
+ },
+ |websocket_url| async move { renderer_is_ready(&websocket_url).await },
+ )
+ .await
+}
+
+async fn wait_for_ready_target_with(
+ port: u16,
+ policy: TargetWaitPolicy,
+ mut fetch_targets: FetchTargets,
+ mut check_ready: CheckReady,
+) -> Result
+where
+ FetchTargets: FnMut() -> FetchFuture,
+ FetchFuture: Future