Skip to content

Commit fa3e538

Browse files
committed
fix Windows failed-start appearance rollback
1 parent 9e67987 commit fa3e538

16 files changed

Lines changed: 1941 additions & 124 deletions

windows/README.en.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,8 @@ Starting with Codex Store `26.715.10079.0`, the owl runtime may convert package-
201201

202202
Field results in issue #235 now confirm two independent failures: WindowsApps returns `access-denied` for direct launch on `26.715.10079.0`, while `26.721.3404.0` retains the raw CDP arguments but its production runtime still opens no listener. Either result means that Codex/Windows combination cannot enable the skin within the project's safety boundary. The fallback is currently a safe diagnostic and rollback path, not a compatibility guarantee for affected owl builds. Do not take ownership of WindowsApps or patch the official package; keep the complete error and follow issue #235 for upstream compatibility status.
203203

204+
If debug launch or visible renderer verification fails, the launcher first confirms that every Codex process started by this attempt is closed, then restores only this attempt's appearance-key values that are still unchanged. Newer config edits are preserved instead of replacing the whole file from an old backup. If Codex cannot be confirmed closed, recovery stops and preserves the current config rather than racing the running app. This rollback covers caught script failures only; it is not evidence that an affected official Codex build has restored CDP support.
205+
204206
### The skin stops working after a Codex update
205207

206208
Run the installer and launch shortcut again. The scripts rediscover the currently registered Store package instead of trusting an executable path from an older app version.

windows/README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,8 @@ Codex Store `26.715.10079.0` 起,owl runtime 可能把应用包激活参数转
178178

179179
Issue #235 的实机结果已经确认两种独立失败:`26.715.10079.0` 的 WindowsApps ACL 会返回 `access-denied``26.721.3404.0` 可保留原始 CDP 参数,但 production runtime 仍不监听端口。两种结果都意味着当前 Codex/Windows 组合无法在项目安全边界内启用皮肤;该回退目前是安全诊断与回滚机制,不是对受影响 owl 版本的兼容性保证。不要接管 WindowsApps 所有权或修改官方包;请保留完整错误并关注 Issue #235 的上游兼容状态。
180180

181+
如果调试启动或可见渲染验证失败,启动器会先确认本轮启动的 Codex 已全部关闭,再只恢复本次改动且仍保持原值的外观键;较新的配置编辑会保留,不会被整份旧备份覆盖。若无法确认 Codex 已关闭,恢复会停止并保留当前配置,避免与仍在运行的应用竞争写入。此回滚只覆盖脚本捕获的失败,不代表受影响的官方 Codex 版本已经恢复 CDP 支持。
182+
181183
### Codex 更新后皮肤失效
182184

183185
重新运行安装器和启动快捷方式。脚本会重新发现当前注册的 Store 包,不依赖旧版本的可执行文件路径。

windows/references/runtime-notes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
- Restore does not require Node to remain installed: it preflights config backups, closes Codex to clear live DOM and CDP, stops only the verified recorded injector, applies requested config changes, then reopens the official app without debug flags.
1212
- A live recorded injector whose PID no longer matches the saved Node path, injector command line, port, Browser ID, or start time causes start/restore to abort with state preserved; it is never silently archived and replaced.
1313
- The managed theme root rejects junctions and symbolic links before initialization, import, save, switch, pause, or state writes. Windows uses the bundled Node image-metadata helper to enforce the same 10 MB, 16384px, and 50MP limits before an import is copied.
14-
- `config.toml` is read from raw bytes as strict UTF-8, written without BOM through same-directory atomic replacement, and backed up byte-for-byte. Install requires Codex to be closed; writes stage the temporary file first, then abort if the destination bytes changed immediately before replacement. Quoted keys and table-header comments are supported; escaped target keys, multiline strings/arrays, dotted target keys, or duplicate target keys fail before writing. Completed restore backups are retained as `config.restored-*.toml` so reinstall captures a fresh baseline.
14+
- `config.toml` is read from raw bytes as strict UTF-8, written without BOM through same-directory atomic replacement, and backed up byte-for-byte. Install requires Codex to be closed; writes stage the temporary file first, then abort if the destination bytes changed immediately before replacement. Quoted keys and table-header comments are supported; escaped target keys, multiline strings/arrays, dotted target keys, or duplicate target keys fail before writing. A caught startup failure restores each managed appearance key only when its current exact line still equals the value written by that attempt, preserves unrelated/current edits, and restores ownership metadata after the config commit. Recovery is blocked when the attempt's Codex processes cannot be confirmed closed. Completed restore backups are retained as `config.restored-*.toml` so reinstall captures a fresh baseline.
1515
- A per-user named mutex prevents concurrent install, start, restore, and verify operations from racing state, ports, or config writes.
1616
- The installer compares every staged runtime file with its selected source by SHA-256, then clears Internet-zone markers only from staged managed `.ps1` copies. Installed shortcuts and tray child processes use process-scoped `RemoteSigned`; Machine/User Policy remains authoritative and no persistent execution policy is changed.
1717
- Store updates are supported because the launcher queries `Get-AppxPackage OpenAI.Codex` on every launch. State paths are eligible for automatic cleanup only after their Appx full name, family name, install root, and executable are matched against a currently registered package; an active unverified old path requires manual closure.

windows/scripts/apply-community-theme.ps1

Lines changed: 69 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -315,6 +315,36 @@ function Set-DreamSkinActiveThemeFromSnapshot {
315315
return $activeFingerprint
316316
}
317317

318+
function Get-DreamSkinCommunityStartFailureMessage {
319+
param(
320+
[Parameter(Mandatory = $true)][string]$Category,
321+
[Parameter(Mandatory = $true)][string]$AppearanceRecovery
322+
)
323+
$messageKey = switch ($Category) {
324+
'cdp-launch-failed' { 'CommunityStartCdpLaunchFailed' }
325+
'cdp-direct-access-denied' { 'CommunityStartCdpDirectAccessDenied' }
326+
'cdp-endpoint-unavailable' { 'CommunityStartCdpEndpointUnavailable' }
327+
'port-unavailable' { 'CommunityStartPortUnavailable' }
328+
'state-reconciliation-failed' { 'CommunityStartStateReconciliationFailed' }
329+
'injector-start-failed' { 'CommunityStartInjectorFailed' }
330+
'renderer-verification-failed' { 'CommunityStartRendererVerificationFailed' }
331+
'superseded' { 'CommunityStartSuperseded' }
332+
default { 'CommunityStartInternalFailure' }
333+
}
334+
$message = Get-DreamSkinCommunityText -Key $messageKey
335+
$recoveryKey = switch ($AppearanceRecovery) {
336+
'restored' { 'CommunityStartAppearanceRestored' }
337+
'conflict-preserved' { 'CommunityStartAppearanceConflictPreserved' }
338+
'blocked' { 'CommunityStartAppearanceBlocked' }
339+
'preserved-rendered' { 'CommunityStartAppearancePreservedRendered' }
340+
default { $null }
341+
}
342+
if ($recoveryKey) {
343+
$message += ' ' + (Get-DreamSkinCommunityText -Key $recoveryKey)
344+
}
345+
return $message
346+
}
347+
318348
function Invoke-DreamSkinCommunityStartAndVerify {
319349
param(
320350
[ValidateRange(1000, 300000)]
@@ -325,20 +355,47 @@ function Invoke-DreamSkinCommunityStartAndVerify {
325355
if (-not (Test-Path -LiteralPath $startScript -PathType Leaf)) {
326356
throw 'The managed Dream Skin start script is missing.'
327357
}
358+
$stateRoot = Join-Path $env:LOCALAPPDATA 'CodexDreamSkin'
359+
$resultToken = [guid]::NewGuid().ToString('N')
360+
$resultPath = Get-DreamSkinStartResultPath -StateRoot $stateRoot -Token $resultToken
361+
if (Test-Path -LiteralPath $resultPath) {
362+
throw (Get-DreamSkinCommunityText -Key 'CommunityStartInvalidResult')
363+
}
328364
$powershell = (Get-Command powershell.exe -ErrorAction Stop).Source
329365
$argumentLine = '-NoProfile -STA -WindowStyle Hidden -ExecutionPolicy RemoteSigned -File ' +
330366
(ConvertTo-DreamSkinProcessArgument -Value $startScript) + ' -RestartExisting' +
331367
' -RequireUnpaused -OperationLockTimeoutMilliseconds ' +
332-
"$OperationLockTimeoutMilliseconds"
333-
$startProcess = Start-Process -FilePath $powershell -ArgumentList $argumentLine `
334-
-WindowStyle Hidden -PassThru
335-
if (-not $startProcess.WaitForExit($OperationLockTimeoutMilliseconds)) {
336-
try { Stop-Process -InputObject $startProcess -Force -ErrorAction SilentlyContinue } catch {}
337-
[void]$startProcess.WaitForExit(15000)
338-
throw "Dream Skin start verification did not finish within $OperationLockTimeoutMilliseconds ms."
339-
}
340-
if ($startProcess.ExitCode -ne 0) {
341-
throw "Dream Skin could not start and visibly verify the active theme (exit code $($startProcess.ExitCode))."
368+
"$OperationLockTimeoutMilliseconds" + ' -ResultToken ' + $resultToken
369+
try {
370+
$startProcess = Start-Process -FilePath $powershell -ArgumentList $argumentLine `
371+
-WindowStyle Hidden -PassThru
372+
if (-not $startProcess.WaitForExit($OperationLockTimeoutMilliseconds)) {
373+
try { Stop-Process -InputObject $startProcess -Force -ErrorAction SilentlyContinue } catch {}
374+
[void]$startProcess.WaitForExit(15000)
375+
throw (Get-DreamSkinCommunityText -Key 'CommunityStartTimedOut')
376+
}
377+
try {
378+
$result = Read-DreamSkinStartResult -StateRoot $stateRoot -Token $resultToken
379+
} catch {
380+
throw (Get-DreamSkinCommunityText -Key 'CommunityStartInvalidResult')
381+
}
382+
$coherentSuccess = $startProcess.ExitCode -eq 0 -and "$($result.outcome)" -ceq 'success'
383+
$coherentFailure = $startProcess.ExitCode -ne 0 -and "$($result.outcome)" -ceq 'failure'
384+
if (-not ($coherentSuccess -or $coherentFailure)) {
385+
throw (Get-DreamSkinCommunityText -Key 'CommunityStartInvalidResult')
386+
}
387+
if ($coherentFailure) {
388+
$message = Get-DreamSkinCommunityStartFailureMessage `
389+
-Category "$($result.category)" -AppearanceRecovery "$($result.appearanceRecovery)"
390+
$exception = [System.InvalidOperationException]::new($message)
391+
$exception.Data['DreamSkinStartCategory'] = "$($result.category)"
392+
$exception.Data['DreamSkinAppearanceRecovery'] = "$($result.appearanceRecovery)"
393+
throw $exception
394+
}
395+
} finally {
396+
if (Test-Path -LiteralPath $resultPath) {
397+
Remove-Item -LiteralPath $resultPath -Force -ErrorAction SilentlyContinue
398+
}
342399
}
343400
}
344401

@@ -667,7 +724,8 @@ function Invoke-DreamSkinCommunityThemeTransaction {
667724
-Recovery 'Superseded' -InnerException $startFailure.Exception)
668725
}
669726
throw (New-DreamSkinCommunityApplyException `
670-
-Message 'The imported theme failed visible verification. The previous theme was reapplied and visibly verified.' `
727+
-Message ("The imported theme failed visible verification: $($startFailure.Exception.Message) " +
728+
'The previous theme was reapplied and visibly verified.') `
671729
-Recovery 'Verified' -InnerException $startFailure.Exception)
672730
}
673731

0 commit comments

Comments
 (0)