Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
using System.Diagnostics;
|
|
|
|
|
using System.Security.Cryptography;
|
|
|
|
|
using System.Text;
|
|
|
|
|
using System.Text.Json;
|
|
|
|
|
using LanMountainDesktop.Launcher.Models;
|
|
|
|
|
using LanMountainDesktop.Shared.Contracts.Launcher;
|
|
|
|
|
|
|
|
|
|
namespace LanMountainDesktop.Launcher.Services;
|
|
|
|
|
|
|
|
|
|
internal sealed class StartupAttemptRegistry
|
|
|
|
|
{
|
2026-04-23 09:45:05 +08:00
|
|
|
private static readonly TimeSpan CoordinatorHeartbeatTimeout = TimeSpan.FromSeconds(10);
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
|
|
|
|
|
private readonly string _statePath;
|
|
|
|
|
private readonly string _mutexName;
|
|
|
|
|
private string? _ownedAttemptId;
|
|
|
|
|
|
|
|
|
|
public StartupAttemptRegistry()
|
Add configurable data location (portable/system)
Introduce support for choosing and resolving the application's data root (system user dir vs. portable app folder). Adds DataLocationConfig model, DataLocationResolver (load/save/resolve/migrate), a UI prompt (DataLocationPromptWindow) and an OOBE step (DataLocationOobeStep) to let users pick and optionally migrate existing data. Wire the chosen data root into the launcher flow and host launch plan (forwarded via --data-root and LMD_DATA_ROOT), and add AppDataPathProvider to let runtime services read the effective data root (initialized in Program.Main). Update various services (logging, settings, DB, plugin/market, startup registry, etc.) to use the new provider/resolver and register the config type in the JSON context. This enables portable installs, safe migration, and runtime overrides via CLI or environment variable.
2026-04-24 12:30:05 +08:00
|
|
|
: this(ResolveDefaultStatePath())
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
{
|
|
|
|
|
}
|
|
|
|
|
|
Add configurable data location (portable/system)
Introduce support for choosing and resolving the application's data root (system user dir vs. portable app folder). Adds DataLocationConfig model, DataLocationResolver (load/save/resolve/migrate), a UI prompt (DataLocationPromptWindow) and an OOBE step (DataLocationOobeStep) to let users pick and optionally migrate existing data. Wire the chosen data root into the launcher flow and host launch plan (forwarded via --data-root and LMD_DATA_ROOT), and add AppDataPathProvider to let runtime services read the effective data root (initialized in Program.Main). Update various services (logging, settings, DB, plugin/market, startup registry, etc.) to use the new provider/resolver and register the config type in the JSON context. This enables portable installs, safe migration, and runtime overrides via CLI or environment variable.
2026-04-24 12:30:05 +08:00
|
|
|
private static string ResolveDefaultStatePath()
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var appRoot = Commands.ResolveAppRoot(CommandContext.FromArgs([]));
|
|
|
|
|
var resolver = new DataLocationResolver(appRoot);
|
Refactor data location paths and add background service
Refactor DataLocationResolver to centralize data path resolution (ResolveLauncherDataPath, ResolveDesktopDataPath, ResolveConfigPath, ResolveLauncherLogsPath, ResolveLauncherStatePath) and replace usages of the previous ".launcher" layout with a "Launcher" folder. Update API: LoadConfig/SaveConfig reorganized and ApplyLocationChoice now accepts an optional custom path and migration flag; migration logic updated accordingly. Update dependent services and views (Logger, DeploymentLocator, UpdateEngineService, OobeStateService, StartupAttemptRegistry, LauncherDebugSettingsStore, OobeWindow) to use the new resolver APIs and paths. Add LauncherBackgroundService to load/validate/cache a custom splash background image and wire it into SplashWindow (AXAML/Axaml.cs) with UI placeholders and overlay. Misc: minor cleanup of Oobe/Splash XAML and related code adjustments and logging improvements.
2026-04-25 18:14:29 +08:00
|
|
|
return Path.Combine(resolver.ResolveLauncherStatePath(), "startup-attempt.json");
|
Add configurable data location (portable/system)
Introduce support for choosing and resolving the application's data root (system user dir vs. portable app folder). Adds DataLocationConfig model, DataLocationResolver (load/save/resolve/migrate), a UI prompt (DataLocationPromptWindow) and an OOBE step (DataLocationOobeStep) to let users pick and optionally migrate existing data. Wire the chosen data root into the launcher flow and host launch plan (forwarded via --data-root and LMD_DATA_ROOT), and add AppDataPathProvider to let runtime services read the effective data root (initialized in Program.Main). Update various services (logging, settings, DB, plugin/market, startup registry, etc.) to use the new provider/resolver and register the config type in the JSON context. This enables portable installs, safe migration, and runtime overrides via CLI or environment variable.
2026-04-24 12:30:05 +08:00
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
return Path.Combine(
|
|
|
|
|
Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData),
|
|
|
|
|
"LanMountainDesktop",
|
Refactor data location paths and add background service
Refactor DataLocationResolver to centralize data path resolution (ResolveLauncherDataPath, ResolveDesktopDataPath, ResolveConfigPath, ResolveLauncherLogsPath, ResolveLauncherStatePath) and replace usages of the previous ".launcher" layout with a "Launcher" folder. Update API: LoadConfig/SaveConfig reorganized and ApplyLocationChoice now accepts an optional custom path and migration flag; migration logic updated accordingly. Update dependent services and views (Logger, DeploymentLocator, UpdateEngineService, OobeStateService, StartupAttemptRegistry, LauncherDebugSettingsStore, OobeWindow) to use the new resolver APIs and paths. Add LauncherBackgroundService to load/validate/cache a custom splash background image and wire it into SplashWindow (AXAML/Axaml.cs) with UI placeholders and overlay. Misc: minor cleanup of Oobe/Splash XAML and related code adjustments and logging improvements.
2026-04-25 18:14:29 +08:00
|
|
|
"Launcher",
|
Add configurable data location (portable/system)
Introduce support for choosing and resolving the application's data root (system user dir vs. portable app folder). Adds DataLocationConfig model, DataLocationResolver (load/save/resolve/migrate), a UI prompt (DataLocationPromptWindow) and an OOBE step (DataLocationOobeStep) to let users pick and optionally migrate existing data. Wire the chosen data root into the launcher flow and host launch plan (forwarded via --data-root and LMD_DATA_ROOT), and add AppDataPathProvider to let runtime services read the effective data root (initialized in Program.Main). Update various services (logging, settings, DB, plugin/market, startup registry, etc.) to use the new provider/resolver and register the config type in the JSON context. This enables portable installs, safe migration, and runtime overrides via CLI or environment variable.
2026-04-24 12:30:05 +08:00
|
|
|
"state",
|
|
|
|
|
"startup-attempt.json");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
internal StartupAttemptRegistry(string statePath)
|
|
|
|
|
{
|
|
|
|
|
_statePath = statePath;
|
|
|
|
|
_mutexName = $"LanMountainDesktop.Launcher.StartupAttempt.{ComputePathHash(statePath)}";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public StartupAttemptRecord StartOwnedAttempt(
|
|
|
|
|
int hostPid,
|
|
|
|
|
string launchSource,
|
|
|
|
|
string successPolicy,
|
|
|
|
|
StartupStage stage,
|
|
|
|
|
string? message)
|
|
|
|
|
{
|
|
|
|
|
var record = new StartupAttemptRecord
|
|
|
|
|
{
|
|
|
|
|
AttemptId = Guid.NewGuid().ToString("N"),
|
|
|
|
|
HostPid = hostPid,
|
2026-04-23 09:45:05 +08:00
|
|
|
CoordinatorPid = Environment.ProcessId,
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
LaunchSource = launchSource,
|
|
|
|
|
SuccessPolicy = successPolicy,
|
|
|
|
|
LastObservedStage = stage,
|
|
|
|
|
LastObservedMessage = message ?? string.Empty,
|
|
|
|
|
StartedAtUtc = DateTimeOffset.UtcNow,
|
|
|
|
|
UpdatedAtUtc = DateTimeOffset.UtcNow,
|
2026-04-23 09:45:05 +08:00
|
|
|
HeartbeatAtUtc = DateTimeOffset.UtcNow,
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
State = StartupAttemptState.Pending
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
ExecuteWithLock(() =>
|
|
|
|
|
{
|
|
|
|
|
SaveUnsafe(record);
|
|
|
|
|
_ownedAttemptId = record.AttemptId;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return Clone(record);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-23 09:45:05 +08:00
|
|
|
public bool TryReserveCoordinator(
|
|
|
|
|
string launchSource,
|
|
|
|
|
string successPolicy,
|
|
|
|
|
string coordinatorPipeName,
|
|
|
|
|
out StartupAttemptRecord reservedAttempt,
|
|
|
|
|
out StartupAttemptRecord? activeCoordinatorAttempt)
|
|
|
|
|
{
|
|
|
|
|
StartupAttemptRecord? reserved = null;
|
|
|
|
|
StartupAttemptRecord? active = null;
|
|
|
|
|
|
|
|
|
|
ExecuteWithLock(() =>
|
|
|
|
|
{
|
|
|
|
|
var existing = LoadUnsafe();
|
|
|
|
|
if (existing is not null && IsCoordinatorLive(existing))
|
|
|
|
|
{
|
|
|
|
|
active = Clone(existing);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (existing is not null && IsRecoverableCoordinatorAttempt(existing))
|
|
|
|
|
{
|
|
|
|
|
existing.CoordinatorPid = Environment.ProcessId;
|
|
|
|
|
existing.CoordinatorPipeName = coordinatorPipeName;
|
|
|
|
|
existing.HeartbeatAtUtc = DateTimeOffset.UtcNow;
|
|
|
|
|
existing.UpdatedAtUtc = DateTimeOffset.UtcNow;
|
|
|
|
|
if (existing.HostPid <= 0)
|
|
|
|
|
{
|
|
|
|
|
existing.ReservedBeforeHostStart = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (existing.State == StartupAttemptState.DetachedWaiting)
|
|
|
|
|
{
|
|
|
|
|
existing.State = StartupAttemptState.SoftTimeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_ownedAttemptId = existing.AttemptId;
|
|
|
|
|
SaveUnsafe(existing);
|
|
|
|
|
reserved = Clone(existing);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var now = DateTimeOffset.UtcNow;
|
|
|
|
|
var record = new StartupAttemptRecord
|
|
|
|
|
{
|
|
|
|
|
AttemptId = Guid.NewGuid().ToString("N"),
|
|
|
|
|
HostPid = 0,
|
|
|
|
|
CoordinatorPid = Environment.ProcessId,
|
|
|
|
|
CoordinatorPipeName = coordinatorPipeName,
|
|
|
|
|
LaunchSource = launchSource,
|
|
|
|
|
SuccessPolicy = successPolicy,
|
|
|
|
|
LastObservedStage = StartupStage.Initializing,
|
|
|
|
|
LastObservedMessage = "Launcher coordinator reserved startup ownership.",
|
|
|
|
|
StartedAtUtc = now,
|
|
|
|
|
UpdatedAtUtc = now,
|
|
|
|
|
HeartbeatAtUtc = now,
|
|
|
|
|
ReservedBeforeHostStart = true,
|
|
|
|
|
State = StartupAttemptState.Pending
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_ownedAttemptId = record.AttemptId;
|
|
|
|
|
SaveUnsafe(record);
|
|
|
|
|
reserved = Clone(record);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
reservedAttempt = reserved ?? new StartupAttemptRecord();
|
|
|
|
|
activeCoordinatorAttempt = active;
|
|
|
|
|
return reserved is not null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public StartupAttemptRecord? GetOwnedAttempt()
|
|
|
|
|
{
|
|
|
|
|
StartupAttemptRecord? result = null;
|
|
|
|
|
if (string.IsNullOrWhiteSpace(_ownedAttemptId))
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ExecuteWithLock(() =>
|
|
|
|
|
{
|
|
|
|
|
var record = LoadUnsafe();
|
|
|
|
|
if (record is not null && string.Equals(record.AttemptId, _ownedAttemptId, StringComparison.Ordinal))
|
|
|
|
|
{
|
|
|
|
|
result = Clone(record);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public StartupAttemptRecord? TryGetLiveCoordinatorAttempt()
|
|
|
|
|
{
|
|
|
|
|
StartupAttemptRecord? result = null;
|
|
|
|
|
ExecuteWithLock(() =>
|
|
|
|
|
{
|
|
|
|
|
var record = LoadUnsafe();
|
|
|
|
|
if (record is not null && IsCoordinatorLive(record))
|
|
|
|
|
{
|
|
|
|
|
result = Clone(record);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public StartupAttemptRecord? TryGetLatestAttempt()
|
|
|
|
|
{
|
|
|
|
|
StartupAttemptRecord? result = null;
|
|
|
|
|
ExecuteWithLock(() =>
|
|
|
|
|
{
|
|
|
|
|
var record = LoadUnsafe();
|
|
|
|
|
if (record is not null)
|
|
|
|
|
{
|
|
|
|
|
result = Clone(record);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public StartupAttemptRecord AssignOwnedHostProcess(
|
|
|
|
|
int hostPid,
|
|
|
|
|
StartupStage stage,
|
|
|
|
|
string? message)
|
|
|
|
|
{
|
|
|
|
|
StartupAttemptRecord? result = null;
|
|
|
|
|
UpdateOwned(record =>
|
|
|
|
|
{
|
|
|
|
|
record.HostPid = hostPid;
|
|
|
|
|
record.LastObservedStage = stage;
|
|
|
|
|
record.LastObservedMessage = message ?? record.LastObservedMessage;
|
|
|
|
|
record.ReservedBeforeHostStart = false;
|
|
|
|
|
result = Clone(record);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return result ?? StartOwnedAttempt(
|
|
|
|
|
hostPid,
|
|
|
|
|
string.Empty,
|
|
|
|
|
string.Empty,
|
|
|
|
|
stage,
|
|
|
|
|
message);
|
|
|
|
|
}
|
|
|
|
|
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
public bool AdoptAttempt(string attemptId)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(attemptId))
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var adopted = false;
|
|
|
|
|
ExecuteWithLock(() =>
|
|
|
|
|
{
|
|
|
|
|
var record = LoadUnsafe();
|
|
|
|
|
if (record is null || !string.Equals(record.AttemptId, attemptId, StringComparison.Ordinal))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!IsAttachable(record))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_ownedAttemptId = record.AttemptId;
|
|
|
|
|
if (record.State == StartupAttemptState.DetachedWaiting)
|
|
|
|
|
{
|
|
|
|
|
record.State = StartupAttemptState.SoftTimeout;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
record.UpdatedAtUtc = DateTimeOffset.UtcNow;
|
|
|
|
|
SaveUnsafe(record);
|
|
|
|
|
adopted = true;
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return adopted;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public StartupAttemptRecord? TryGetAttachableAttempt(string launchSource, string successPolicy)
|
|
|
|
|
{
|
|
|
|
|
StartupAttemptRecord? result = null;
|
|
|
|
|
ExecuteWithLock(() =>
|
|
|
|
|
{
|
|
|
|
|
var record = LoadUnsafe();
|
|
|
|
|
if (record is null ||
|
|
|
|
|
!IsAttachable(record) ||
|
|
|
|
|
!string.Equals(record.LaunchSource, launchSource, StringComparison.OrdinalIgnoreCase) ||
|
|
|
|
|
!string.Equals(record.SuccessPolicy, successPolicy, StringComparison.OrdinalIgnoreCase))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
result = Clone(record);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
return result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void MarkOwnedIpcConnected()
|
|
|
|
|
{
|
2026-04-23 09:45:05 +08:00
|
|
|
UpdateOwned(record =>
|
|
|
|
|
{
|
|
|
|
|
record.IpcConnected = true;
|
|
|
|
|
record.PublicIpcConnected = true;
|
|
|
|
|
});
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void UpdateOwnedStage(StartupStage stage, string? message, bool ipcConnected)
|
|
|
|
|
{
|
|
|
|
|
UpdateOwned(record =>
|
|
|
|
|
{
|
|
|
|
|
record.LastObservedStage = stage;
|
|
|
|
|
record.LastObservedMessage = message ?? string.Empty;
|
|
|
|
|
if (ipcConnected)
|
|
|
|
|
{
|
|
|
|
|
record.IpcConnected = true;
|
2026-04-23 09:45:05 +08:00
|
|
|
record.PublicIpcConnected = true;
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-23 09:45:05 +08:00
|
|
|
public void UpdateOwnedCoordinatorHeartbeat(LauncherCoordinatorStatus status)
|
|
|
|
|
{
|
|
|
|
|
UpdateOwned(record =>
|
|
|
|
|
{
|
|
|
|
|
record.CoordinatorPid = Environment.ProcessId;
|
|
|
|
|
record.HeartbeatAtUtc = DateTimeOffset.UtcNow;
|
|
|
|
|
record.LastObservedStage = status.LastObservedStage;
|
|
|
|
|
record.LastObservedMessage = status.LastObservedMessage;
|
|
|
|
|
record.IpcConnected = status.PublicIpcConnected;
|
|
|
|
|
record.PublicIpcConnected = status.PublicIpcConnected;
|
|
|
|
|
record.ShellStatus = status.ShellStatus?.ShellState ?? status.State;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
public void MarkOwnedSoftTimeout(string? message)
|
|
|
|
|
{
|
|
|
|
|
UpdateOwned(record =>
|
|
|
|
|
{
|
|
|
|
|
record.State = StartupAttemptState.SoftTimeout;
|
|
|
|
|
record.LastObservedMessage = message ?? record.LastObservedMessage;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-23 19:04:39 +08:00
|
|
|
public void MarkOwnedWaitingForShell(string? message)
|
|
|
|
|
{
|
|
|
|
|
UpdateOwned(record =>
|
|
|
|
|
{
|
|
|
|
|
if (record.State is StartupAttemptState.Pending or StartupAttemptState.SoftTimeout or StartupAttemptState.DetachedWaiting)
|
|
|
|
|
{
|
|
|
|
|
record.State = StartupAttemptState.WaitingForShell;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
record.LastObservedMessage = message ?? record.LastObservedMessage;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
public void MarkOwnedDetachedWaiting()
|
|
|
|
|
{
|
|
|
|
|
UpdateOwned(record =>
|
|
|
|
|
{
|
|
|
|
|
if (record.State is StartupAttemptState.Pending or StartupAttemptState.SoftTimeout)
|
|
|
|
|
{
|
|
|
|
|
record.State = StartupAttemptState.DetachedWaiting;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void MarkOwnedSucceeded(StartupStage stage, string? message)
|
|
|
|
|
{
|
|
|
|
|
UpdateOwned(record =>
|
|
|
|
|
{
|
|
|
|
|
record.State = StartupAttemptState.Succeeded;
|
|
|
|
|
record.LastObservedStage = stage;
|
|
|
|
|
record.LastObservedMessage = message ?? record.LastObservedMessage;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public void MarkOwnedFailed(StartupStage stage, string? message)
|
|
|
|
|
{
|
|
|
|
|
UpdateOwned(record =>
|
|
|
|
|
{
|
|
|
|
|
record.State = StartupAttemptState.Failed;
|
|
|
|
|
record.LastObservedStage = stage;
|
|
|
|
|
record.LastObservedMessage = message ?? record.LastObservedMessage;
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void UpdateOwned(Action<StartupAttemptRecord> update)
|
|
|
|
|
{
|
|
|
|
|
if (string.IsNullOrWhiteSpace(_ownedAttemptId))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ExecuteWithLock(() =>
|
|
|
|
|
{
|
|
|
|
|
var record = LoadUnsafe();
|
|
|
|
|
if (record is null || !string.Equals(record.AttemptId, _ownedAttemptId, StringComparison.Ordinal))
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
update(record);
|
|
|
|
|
record.UpdatedAtUtc = DateTimeOffset.UtcNow;
|
|
|
|
|
SaveUnsafe(record);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void ExecuteWithLock(Action action)
|
|
|
|
|
{
|
|
|
|
|
using var mutex = new Mutex(false, _mutexName);
|
|
|
|
|
var hasHandle = false;
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
hasHandle = mutex.WaitOne(TimeSpan.FromSeconds(2));
|
|
|
|
|
}
|
|
|
|
|
catch (AbandonedMutexException)
|
|
|
|
|
{
|
|
|
|
|
hasHandle = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!hasHandle)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
action();
|
|
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
|
|
|
|
if (hasHandle)
|
|
|
|
|
{
|
|
|
|
|
mutex.ReleaseMutex();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private StartupAttemptRecord? LoadUnsafe()
|
|
|
|
|
{
|
|
|
|
|
if (!File.Exists(_statePath))
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
var json = File.ReadAllText(_statePath);
|
2026-04-24 20:55:33 +08:00
|
|
|
return JsonSerializer.Deserialize(json, AppJsonContext.Default.StartupAttemptRecord);
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private void SaveUnsafe(StartupAttemptRecord record)
|
|
|
|
|
{
|
|
|
|
|
var directory = Path.GetDirectoryName(_statePath);
|
|
|
|
|
if (!string.IsNullOrWhiteSpace(directory))
|
|
|
|
|
{
|
|
|
|
|
Directory.CreateDirectory(directory);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-24 20:55:33 +08:00
|
|
|
File.WriteAllText(_statePath, JsonSerializer.Serialize(record, AppJsonContext.Default.StartupAttemptRecord));
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool IsAttachable(StartupAttemptRecord record)
|
|
|
|
|
{
|
2026-04-23 19:04:39 +08:00
|
|
|
if (record.State is not (
|
|
|
|
|
StartupAttemptState.Pending or
|
|
|
|
|
StartupAttemptState.SoftTimeout or
|
|
|
|
|
StartupAttemptState.DetachedWaiting or
|
|
|
|
|
StartupAttemptState.WaitingForShell))
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TryGetLiveProcess(record.HostPid, out _);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-23 09:45:05 +08:00
|
|
|
private static bool IsRecoverableCoordinatorAttempt(StartupAttemptRecord record)
|
|
|
|
|
{
|
2026-04-23 19:04:39 +08:00
|
|
|
if (record.State is not (
|
|
|
|
|
StartupAttemptState.Pending or
|
|
|
|
|
StartupAttemptState.SoftTimeout or
|
|
|
|
|
StartupAttemptState.DetachedWaiting or
|
|
|
|
|
StartupAttemptState.WaitingForShell))
|
2026-04-23 09:45:05 +08:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (record.HostPid <= 0)
|
|
|
|
|
{
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TryGetLiveProcess(record.HostPid, out _);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static bool IsCoordinatorLive(StartupAttemptRecord record)
|
|
|
|
|
{
|
2026-04-23 19:04:39 +08:00
|
|
|
if (record.State is not (
|
|
|
|
|
StartupAttemptState.Pending or
|
|
|
|
|
StartupAttemptState.SoftTimeout or
|
|
|
|
|
StartupAttemptState.DetachedWaiting or
|
|
|
|
|
StartupAttemptState.WaitingForShell))
|
2026-04-23 09:45:05 +08:00
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (record.CoordinatorPid <= 0 ||
|
|
|
|
|
string.IsNullOrWhiteSpace(record.CoordinatorPipeName) ||
|
|
|
|
|
DateTimeOffset.UtcNow - record.HeartbeatAtUtc > CoordinatorHeartbeatTimeout)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return TryGetLiveProcess(record.CoordinatorPid, out _);
|
|
|
|
|
}
|
|
|
|
|
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
private static bool TryGetLiveProcess(int processId, out Process? process)
|
|
|
|
|
{
|
|
|
|
|
process = null;
|
|
|
|
|
if (processId <= 0)
|
|
|
|
|
{
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
process = Process.GetProcessById(processId);
|
|
|
|
|
return !process.HasExited;
|
|
|
|
|
}
|
|
|
|
|
catch
|
|
|
|
|
{
|
|
|
|
|
process?.Dispose();
|
|
|
|
|
process = null;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static string ComputePathHash(string statePath)
|
|
|
|
|
{
|
|
|
|
|
var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(statePath.ToLowerInvariant()));
|
|
|
|
|
return Convert.ToHexString(bytes[..8]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private static StartupAttemptRecord Clone(StartupAttemptRecord record)
|
|
|
|
|
{
|
|
|
|
|
return new StartupAttemptRecord
|
|
|
|
|
{
|
|
|
|
|
AttemptId = record.AttemptId,
|
|
|
|
|
HostPid = record.HostPid,
|
2026-04-23 09:45:05 +08:00
|
|
|
CoordinatorPid = record.CoordinatorPid,
|
|
|
|
|
CoordinatorPipeName = record.CoordinatorPipeName,
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
StartedAtUtc = record.StartedAtUtc,
|
|
|
|
|
UpdatedAtUtc = record.UpdatedAtUtc,
|
2026-04-23 09:45:05 +08:00
|
|
|
HeartbeatAtUtc = record.HeartbeatAtUtc,
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
LaunchSource = record.LaunchSource,
|
|
|
|
|
SuccessPolicy = record.SuccessPolicy,
|
|
|
|
|
LastObservedStage = record.LastObservedStage,
|
|
|
|
|
LastObservedMessage = record.LastObservedMessage,
|
|
|
|
|
IpcConnected = record.IpcConnected,
|
2026-04-23 09:45:05 +08:00
|
|
|
PublicIpcConnected = record.PublicIpcConnected,
|
|
|
|
|
ShellStatus = record.ShellStatus,
|
|
|
|
|
ReservedBeforeHostStart = record.ReservedBeforeHostStart,
|
Add startup visual modes and attempt registry
Implement startup visual behavior, de-duplicate startup attempts, and improve failure UX.
Key changes:
- Add spec and docs for startup visuals and timing contract (.trae/specs and docs/LAUNCHER_STARTUP_VISUALS.md).
- Introduce StartupVisualPreferences contract and resolver; create SplashWindow via resolved mode.
- Add StartupAttemptRecord model and a file-backed StartupAttemptRegistry to persist and coordinate in-progress startup attempts (attach/adopt, soft/hard timeouts, IPC/connect state, lifecycle updates).
- Update LauncherFlowCoordinator to: adopt/attach to existing attempts, track IPC connection and soft/hard timeouts (30s/120s), show delayed UI state, attempt foreground recovery via public IPC, compose detailed launch result metadata, and mark registry states (soft timeout, detached waiting, succeeded, failed).
- Add TryActivateExistingInstanceAsync to attempt activating an existing desktop via IPC.
- Change failure flow: ShowFailureWindowAsync now returns user choice; ErrorWindow updated to present Activate/Wait/Open Logs/Exit semantics and new layouts/styles; improved button wiring and debug/dev mode handling.
- Add UI and resource tweaks (ErrorWindow and SplashWindow changes), project asset link for nightly logo, and unit tests for StartupVisualPreferences.
These changes prevent duplicate desktop processes during slow startups, provide clearer UX for delayed startups, and persist startup attempt state across Launcher invocations for safer recovery/attach behavior.
2026-04-23 09:03:35 +08:00
|
|
|
State = record.State
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
}
|