2026-04-21 20:59:52 +08:00
|
|
|
using System.Diagnostics;
|
|
|
|
|
using Avalonia.Threading;
|
|
|
|
|
using LanMountainDesktop.Launcher.Models;
|
2026-05-18 12:26:23 +08:00
|
|
|
using LanMountainDesktop.Launcher.Resources;
|
2026-04-23 09:45:05 +08:00
|
|
|
using LanMountainDesktop.Launcher.Services.Ipc;
|
2026-05-28 10:27:33 +08:00
|
|
|
using LanMountainDesktop.Launcher.Startup;
|
2026-04-21 20:59:52 +08:00
|
|
|
using LanMountainDesktop.Launcher.Views;
|
|
|
|
|
using LanMountainDesktop.Shared.Contracts.Launcher;
|
2026-04-22 14:55:30 +08:00
|
|
|
using LanMountainDesktop.Shared.IPC;
|
2026-04-23 00:27:01 +08:00
|
|
|
using LanMountainDesktop.Shared.IPC.Abstractions.Services;
|
2026-04-21 20:59:52 +08:00
|
|
|
|
|
|
|
|
namespace LanMountainDesktop.Launcher.Services;
|
|
|
|
|
|
2026-05-28 10:27:33 +08:00
|
|
|
internal sealed partial class LauncherFlowCoordinator
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
2026-05-18 12:26:23 +08:00
|
|
|
private static readonly string SoftTimeoutStatusMessage = Strings.Coordinator_SlowDeviceMessage;
|
|
|
|
|
private static readonly string SoftTimeoutDetailsMessage = Strings.Coordinator_RunningHostMessage;
|
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-21 20:59:52 +08:00
|
|
|
private readonly CommandContext _context;
|
|
|
|
|
private readonly DeploymentLocator _deploymentLocator;
|
|
|
|
|
private readonly OobeStateService _oobeStateService;
|
|
|
|
|
private readonly UpdateEngineService _updateEngine;
|
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 StartupAttemptRegistry _startupAttemptRegistry;
|
2026-04-23 09:45:05 +08:00
|
|
|
private readonly LauncherCoordinatorIpcServer? _coordinatorIpcServer;
|
Launcher fix (#6)
* fix.hy3试图修复中
* Resolve dev paths and fix splash UI thread
Compute a solutionRoot and expand development search paths (LanMountainDesktop and dev-test) in DeploymentLocator, add logging when scanning/finding hosts, and return distinct full paths. Ensure backward-compatible path checks. Fix cross-thread UI calls: invoke splashWindow.DismissAsync on the UI thread in LauncherFlowCoordinator, and make SplashWindow.DismissAsync ensure it runs on the UI thread before closing (simplified Close call). These changes improve development host discovery and prevent UI-thread access issues during shutdown.
* 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.
* Add dev/debug startup flow and launch profiles
Handle design-time initialization and add a developer debug startup path: App now skips normal startup when in design mode and shows a DevDebugWindow when running in debug (unless a preview or apply-update command). CommandContext.IsDebugMode is extended to include DOTNET_ENVIRONMENT=Development via a new IsDevelopmentEnvironment helper. Program.Main and BuildAvaloniaApp are made public to aid tooling. Added multiple launchSettings profiles for debug and preview commands that set DOTNET_ENVIRONMENT=Development to simplify IDE debugging and UI previewing.
* Simplify splash to fade; add themed about banners
Simplify splash startup visuals by removing the multi-mode/slide behavior and always using a fade animation. Update App to create SplashWindow without a StartupVisualMode parameter and remove related fields, layout configuration, slide animation, and easing helpers from SplashWindow. Clean up unused using. Replace the single about_banner asset with theme-aware variants (about_banner_dark.png and about_banner_light.png), delete the old about_banner.png, and update AboutSettingsPage to use a DynamicResource ImageBrush (AboutBannerBrush) that selects the appropriate banner per theme.
* Use AppJsonContext for startup state serialization
Switch serialization to the source-generated System.Text.Json context: add JsonSerializable(typeof(StartupAttemptRecord)) to AppJsonContext and replace the previous JsonSerializerOptions-based Serialize/Deserialize calls with AppJsonContext.Default.StartupAttemptRecord. Also remove the now-unused SerializerOptions field. Additionally, update .gitignore to exclude /test-aot-publish.
* Add OOBE redesign, theme & data location support
Introduce a redesigned OOBE flow and data-location/theme support across the launcher. Adds a new ThemeService for applying light/dark and accent colors; integrates FluentIcons.Avalonia package for icons. Overhauls OobeWindow (UX animations, typing effect, multi-step theme and data-location pages, Monet options, and final welcome step) and its code-behind to handle step navigation, accent selection, and data-location resolution. Adds DataLocation UI and handlers (DataLocationPromptWindow changes, DataLocation resolver usage) and wires a DevDebug UI for toggling/opening the data-location page. UpdateEngineService now resolves the launcher root via DataLocationResolver. Misc: update various view models, localization entries and remove TrimmerRoots.xml.
* 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:41:26 +08:00
|
|
|
private readonly DataLocationResolver _dataLocationResolver;
|
2026-04-21 20:59:52 +08:00
|
|
|
private readonly IReadOnlyList<IOobeStep> _oobeSteps;
|
|
|
|
|
|
|
|
|
|
public LauncherFlowCoordinator(
|
|
|
|
|
CommandContext context,
|
|
|
|
|
DeploymentLocator deploymentLocator,
|
|
|
|
|
OobeStateService oobeStateService,
|
|
|
|
|
UpdateEngineService updateEngine,
|
2026-04-23 09:45:05 +08:00
|
|
|
StartupAttemptRegistry? startupAttemptRegistry = null,
|
|
|
|
|
LauncherCoordinatorIpcServer? coordinatorIpcServer = null)
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
|
|
|
|
_context = context;
|
|
|
|
|
_deploymentLocator = deploymentLocator;
|
|
|
|
|
_oobeStateService = oobeStateService;
|
|
|
|
|
_updateEngine = updateEngine;
|
2026-04-23 09:45:05 +08:00
|
|
|
_startupAttemptRegistry = startupAttemptRegistry ?? new StartupAttemptRegistry();
|
|
|
|
|
_coordinatorIpcServer = coordinatorIpcServer;
|
Launcher fix (#6)
* fix.hy3试图修复中
* Resolve dev paths and fix splash UI thread
Compute a solutionRoot and expand development search paths (LanMountainDesktop and dev-test) in DeploymentLocator, add logging when scanning/finding hosts, and return distinct full paths. Ensure backward-compatible path checks. Fix cross-thread UI calls: invoke splashWindow.DismissAsync on the UI thread in LauncherFlowCoordinator, and make SplashWindow.DismissAsync ensure it runs on the UI thread before closing (simplified Close call). These changes improve development host discovery and prevent UI-thread access issues during shutdown.
* 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.
* Add dev/debug startup flow and launch profiles
Handle design-time initialization and add a developer debug startup path: App now skips normal startup when in design mode and shows a DevDebugWindow when running in debug (unless a preview or apply-update command). CommandContext.IsDebugMode is extended to include DOTNET_ENVIRONMENT=Development via a new IsDevelopmentEnvironment helper. Program.Main and BuildAvaloniaApp are made public to aid tooling. Added multiple launchSettings profiles for debug and preview commands that set DOTNET_ENVIRONMENT=Development to simplify IDE debugging and UI previewing.
* Simplify splash to fade; add themed about banners
Simplify splash startup visuals by removing the multi-mode/slide behavior and always using a fade animation. Update App to create SplashWindow without a StartupVisualMode parameter and remove related fields, layout configuration, slide animation, and easing helpers from SplashWindow. Clean up unused using. Replace the single about_banner asset with theme-aware variants (about_banner_dark.png and about_banner_light.png), delete the old about_banner.png, and update AboutSettingsPage to use a DynamicResource ImageBrush (AboutBannerBrush) that selects the appropriate banner per theme.
* Use AppJsonContext for startup state serialization
Switch serialization to the source-generated System.Text.Json context: add JsonSerializable(typeof(StartupAttemptRecord)) to AppJsonContext and replace the previous JsonSerializerOptions-based Serialize/Deserialize calls with AppJsonContext.Default.StartupAttemptRecord. Also remove the now-unused SerializerOptions field. Additionally, update .gitignore to exclude /test-aot-publish.
* Add OOBE redesign, theme & data location support
Introduce a redesigned OOBE flow and data-location/theme support across the launcher. Adds a new ThemeService for applying light/dark and accent colors; integrates FluentIcons.Avalonia package for icons. Overhauls OobeWindow (UX animations, typing effect, multi-step theme and data-location pages, Monet options, and final welcome step) and its code-behind to handle step navigation, accent selection, and data-location resolution. Adds DataLocation UI and handlers (DataLocationPromptWindow changes, DataLocation resolver usage) and wires a DevDebug UI for toggling/opening the data-location page. UpdateEngineService now resolves the launcher root via DataLocationResolver. Misc: update various view models, localization entries and remove TrimmerRoots.xml.
* 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:41:26 +08:00
|
|
|
_dataLocationResolver = new DataLocationResolver(deploymentLocator.GetAppRoot());
|
|
|
|
|
_oobeSteps =
|
|
|
|
|
[
|
|
|
|
|
new WelcomeOobeStep(_oobeStateService, _context),
|
|
|
|
|
new DataLocationOobeStep(_dataLocationResolver)
|
|
|
|
|
];
|
2026-04-21 20:59:52 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-23 09:45:05 +08:00
|
|
|
public static string ResolveSuccessPolicyKey(CommandContext context)
|
|
|
|
|
{
|
|
|
|
|
return new StartupSuccessTracker(context).PolicyKey;
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-21 20:59:52 +08:00
|
|
|
public async Task<LauncherResult> RunAsync(SplashWindow? existingSplashWindow = null)
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_deploymentLocator.CleanupOldDeployments(minVersionsToKeep: 3);
|
Harden OOBE, launch-source and elevation flow
Introduce a per-user OOBE state model and hardened launch/elevation handling. Adds OobeStateFile/OobeLaunchDecision models, OobeStateService (persisting %LOCALAPPDATA%/.launcher/state/oobe-state.json), and LauncherExecutionContext to capture elevation and user SID. CommandContext now normalizes/infers launch-source values (normal, postinstall, apply-update, plugin-install, debug-preview) and exposes maintenance checks. LauncherFlowCoordinator propagates richer launcher context details for diagnostics and suppresses OOBE for elevated/maintenance contexts. PluginInstallerService avoids requesting elevation for user-scoped installs and returns a clear error when installation target is outside the current user's LocalAppData. LauncherClient maps and surfaces result codes, UpdateWorkflow and installer invocation now pass explicit --launch-source values, and WelcomeOobeStep persists OOBE completion via the new service. Adds unit tests (CommandContext, OobeStateService, PluginInstallerService), docs/specs/checklists for the contract, and makes internals visible to tests.
2026-04-22 09:25:22 +08:00
|
|
|
var oobeDecision = _oobeStateService.Evaluate(_context);
|
|
|
|
|
var launcherContextDetails = BuildLauncherContextDetails(_context, oobeDecision, _deploymentLocator.GetAppRoot());
|
2026-04-21 20:59:52 +08:00
|
|
|
|
Harden OOBE, launch-source and elevation flow
Introduce a per-user OOBE state model and hardened launch/elevation handling. Adds OobeStateFile/OobeLaunchDecision models, OobeStateService (persisting %LOCALAPPDATA%/.launcher/state/oobe-state.json), and LauncherExecutionContext to capture elevation and user SID. CommandContext now normalizes/infers launch-source values (normal, postinstall, apply-update, plugin-install, debug-preview) and exposes maintenance checks. LauncherFlowCoordinator propagates richer launcher context details for diagnostics and suppresses OOBE for elevated/maintenance contexts. PluginInstallerService avoids requesting elevation for user-scoped installs and returns a clear error when installation target is outside the current user's LocalAppData. LauncherClient maps and surfaces result codes, UpdateWorkflow and installer invocation now pass explicit --launch-source values, and WelcomeOobeStep persists OOBE completion via the new service. Adds unit tests (CommandContext, OobeStateService, PluginInstallerService), docs/specs/checklists for the contract, and makes internals visible to tests.
2026-04-22 09:25:22 +08:00
|
|
|
if (oobeDecision.ShouldShowOobe)
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
|
|
|
|
var legacyInfo = LegacyVersionDetector.DetectLegacyInstallation();
|
2026-04-22 07:31:54 +08:00
|
|
|
if (legacyInfo is not null)
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
2026-04-22 07:31:54 +08:00
|
|
|
var migrationResult = await ShowMigrationPromptAsync(legacyInfo).ConfigureAwait(false);
|
|
|
|
|
Logger.Info($"Migration prompt completed. Result='{migrationResult}'.");
|
2026-04-21 20:59:52 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var splashWindow = existingSplashWindow ?? await Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
|
|
{
|
|
|
|
|
var window = new SplashWindow();
|
|
|
|
|
window.Show();
|
|
|
|
|
return window;
|
|
|
|
|
});
|
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
|
|
|
var windowsClosingByCoordinator = false;
|
2026-04-23 00:27:01 +08:00
|
|
|
var versionInfo = _deploymentLocator.GetVersionInfo();
|
|
|
|
|
splashWindow.SetVersionInfo(versionInfo.Version, versionInfo.Codename);
|
2026-04-21 20:59:52 +08:00
|
|
|
var reporter = (ISplashStageReporter)splashWindow;
|
2026-04-22 07:31:54 +08:00
|
|
|
|
2026-04-21 20:59:52 +08:00
|
|
|
LoadingDetailsWindow? loadingDetailsWindow = null;
|
|
|
|
|
if (_context.IsDebugMode || _context.GetOption("show-loading-details") == "true")
|
|
|
|
|
{
|
|
|
|
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
|
|
|
|
{
|
|
|
|
|
loadingDetailsWindow = new LoadingDetailsWindow();
|
|
|
|
|
loadingDetailsWindow.Show();
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-04-22 07:31:54 +08:00
|
|
|
|
2026-04-23 00:27:01 +08:00
|
|
|
var successTcs = new TaskCompletionSource<StartupSuccessState>(TaskCreationOptions.RunContinuationsAsynchronously);
|
2026-04-22 07:31:54 +08:00
|
|
|
var activationFailedTcs = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
|
|
|
var lastStage = StartupStage.Initializing;
|
|
|
|
|
var lastStageMessage = "launcher-started";
|
2026-04-23 00:27:01 +08:00
|
|
|
var startupSuccessTracker = new StartupSuccessTracker(_context);
|
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
|
|
|
var activationFailureReason = string.Empty;
|
|
|
|
|
var ipcConnected = false;
|
|
|
|
|
var softTimeoutShown = false;
|
|
|
|
|
var attachedToExistingAttempt = false;
|
|
|
|
|
StartupAttemptRecord? trackedAttempt = null;
|
2026-04-23 09:45:05 +08:00
|
|
|
PublicShellStatus? shellStatus = null;
|
|
|
|
|
|
|
|
|
|
void PublishCoordinatorStatus(bool? hostProcessAliveOverride = null, bool completed = false, bool succeeded = false)
|
|
|
|
|
{
|
|
|
|
|
if (_coordinatorIpcServer is null)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trackedAttempt = _startupAttemptRegistry.GetOwnedAttempt() ?? trackedAttempt;
|
|
|
|
|
var hostPid = trackedAttempt?.HostPid ?? 0;
|
|
|
|
|
var hostProcessAlive = hostProcessAliveOverride ??
|
|
|
|
|
(hostPid > 0 && TryGetLiveProcess(hostPid, out _));
|
|
|
|
|
var status = new LauncherCoordinatorStatus
|
|
|
|
|
{
|
|
|
|
|
AttemptId = trackedAttempt?.AttemptId ?? string.Empty,
|
|
|
|
|
CoordinatorPid = Environment.ProcessId,
|
|
|
|
|
HostPid = hostPid,
|
|
|
|
|
HostProcessAlive = hostProcessAlive,
|
|
|
|
|
LaunchSource = trackedAttempt?.LaunchSource ?? _context.LaunchSource,
|
|
|
|
|
SuccessPolicy = trackedAttempt?.SuccessPolicy ?? startupSuccessTracker.PolicyKey,
|
|
|
|
|
LastObservedStage = lastStage,
|
|
|
|
|
LastObservedMessage = lastStageMessage,
|
|
|
|
|
PublicIpcConnected = ipcConnected,
|
|
|
|
|
State = trackedAttempt?.State.ToString() ?? StartupAttemptState.Pending.ToString(),
|
|
|
|
|
SoftTimeoutShown = softTimeoutShown,
|
|
|
|
|
Completed = completed,
|
|
|
|
|
Succeeded = succeeded,
|
|
|
|
|
ShellStatus = shellStatus,
|
|
|
|
|
UpdatedAtUtc = DateTimeOffset.UtcNow
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
_coordinatorIpcServer.UpdateStatus(status);
|
|
|
|
|
_startupAttemptRegistry.UpdateOwnedCoordinatorHeartbeat(status);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
trackedAttempt = _startupAttemptRegistry.GetOwnedAttempt();
|
|
|
|
|
PublishCoordinatorStatus();
|
2026-04-22 07:31:54 +08:00
|
|
|
|
2026-04-21 20:59:52 +08:00
|
|
|
var loadingState = new LoadingStateMessage();
|
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
|
|
|
EventHandler? splashClosedHandler = null;
|
|
|
|
|
splashClosedHandler = (_, _) =>
|
|
|
|
|
{
|
|
|
|
|
if (windowsClosingByCoordinator)
|
|
|
|
|
{
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
_startupAttemptRegistry.MarkOwnedDetachedWaiting();
|
|
|
|
|
Logger.Warn("Splash window was closed manually. Launcher will continue monitoring the current startup attempt.");
|
|
|
|
|
};
|
|
|
|
|
splashWindow.Closed += splashClosedHandler;
|
2026-04-22 14:55:30 +08:00
|
|
|
using var ipcClient = new LanMountainDesktopIpcClient();
|
|
|
|
|
ipcClient.RegisterNotifyHandler<StartupProgressMessage>(IpcRoutedNotifyIds.LauncherStartupProgress, message =>
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
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
|
|
|
ipcConnected = true;
|
2026-04-22 07:31:54 +08:00
|
|
|
lastStage = message.Stage;
|
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
|
|
|
lastStageMessage = message.Message ?? message.Stage.ToString();
|
2026-04-22 07:31:54 +08:00
|
|
|
Logger.Info($"IPC stage received. Stage='{message.Stage}'; Message='{message.Message ?? string.Empty}'.");
|
|
|
|
|
|
2026-04-21 20:59:52 +08:00
|
|
|
loadingState = loadingState with
|
|
|
|
|
{
|
2026-04-22 07:31:54 +08:00
|
|
|
Stage = message.Stage,
|
|
|
|
|
OverallProgressPercent = message.ProgressPercent,
|
|
|
|
|
Message = message.Message,
|
2026-04-21 20:59:52 +08:00
|
|
|
Timestamp = DateTimeOffset.UtcNow
|
|
|
|
|
};
|
2026-04-22 07:31:54 +08:00
|
|
|
|
|
|
|
|
reporter.Report(MapStartupStageToSplashStage(message.Stage), message.Message ?? message.Stage.ToString());
|
2026-04-21 20:59:52 +08:00
|
|
|
loadingDetailsWindow?.UpdateLoadingState(loadingState);
|
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
|
|
|
_startupAttemptRegistry.UpdateOwnedStage(message.Stage, message.Message, ipcConnected: true);
|
2026-04-23 09:45:05 +08:00
|
|
|
PublishCoordinatorStatus();
|
2026-04-22 07:31:54 +08:00
|
|
|
|
2026-04-23 00:27:01 +08:00
|
|
|
if (startupSuccessTracker.TryResolve(message.Stage, out var successState))
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
2026-04-23 00:27:01 +08:00
|
|
|
successTcs.TrySetResult(successState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (message.Stage == StartupStage.ActivationFailed)
|
|
|
|
|
{
|
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
|
|
|
activationFailureReason = message.Message ?? "activation_failed";
|
2026-04-23 00:27:01 +08:00
|
|
|
activationFailedTcs.TrySetResult(message.Message ?? "activation_failed");
|
2026-04-21 20:59:52 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2026-04-22 07:31:54 +08:00
|
|
|
Logger.Error("IPC progress callback failed.", ex);
|
2026-04-21 20:59:52 +08:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
2026-04-22 14:55:30 +08:00
|
|
|
ipcClient.RegisterNotifyHandler<LoadingStateMessage>(IpcRoutedNotifyIds.LauncherLoadingState, message =>
|
|
|
|
|
{
|
|
|
|
|
Dispatcher.UIThread.Post(() =>
|
|
|
|
|
{
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
loadingState = message;
|
|
|
|
|
loadingDetailsWindow?.UpdateLoadingState(loadingState);
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Logger.Error("IPC loading-state callback failed.", ex);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
});
|
2026-04-21 20:59:52 +08:00
|
|
|
|
|
|
|
|
try
|
|
|
|
|
{
|
2026-05-28 10:27:33 +08:00
|
|
|
if (HostActivationPolicy.ShouldProbeExistingHostBeforeLaunch(_context))
|
2026-04-23 09:45:05 +08:00
|
|
|
{
|
2026-05-12 16:46:49 +08:00
|
|
|
var multiInstanceBehavior = LoadMultiInstanceLaunchBehavior();
|
2026-05-28 10:27:33 +08:00
|
|
|
var existingShellStatus = await TryGetExistingHostStatusAsync(ipcClient, StartupTimeoutPolicy.ExistingHostProbeTimeout)
|
2026-04-23 09:45:05 +08:00
|
|
|
.ConfigureAwait(false);
|
2026-05-28 10:27:33 +08:00
|
|
|
if (HostActivationPolicy.IsExistingHostReadyForLauncherDecision(existingShellStatus))
|
2026-04-23 09:45:05 +08:00
|
|
|
{
|
|
|
|
|
ipcConnected = true;
|
2026-05-12 16:46:49 +08:00
|
|
|
shellStatus = existingShellStatus;
|
|
|
|
|
var decisionResult = await ApplyExistingHostBehaviorAsync(
|
|
|
|
|
ipcClient,
|
|
|
|
|
multiInstanceBehavior,
|
|
|
|
|
existingShellStatus!)
|
|
|
|
|
.ConfigureAwait(false);
|
|
|
|
|
shellStatus = decisionResult.ActivationResult?.Status ?? existingShellStatus;
|
|
|
|
|
var recoverableActivationFailure = decisionResult.ActivationResult is not null &&
|
2026-05-28 10:27:33 +08:00
|
|
|
HostActivationPolicy.IsRecoverableActivationFailure(decisionResult.ActivationResult);
|
2026-05-12 16:46:49 +08:00
|
|
|
lastStage = decisionResult.Success || recoverableActivationFailure
|
2026-04-23 09:45:05 +08:00
|
|
|
? StartupStage.ActivationRedirected
|
|
|
|
|
: StartupStage.ActivationFailed;
|
2026-05-12 16:46:49 +08:00
|
|
|
lastStageMessage = decisionResult.Message;
|
|
|
|
|
if (decisionResult.Success || recoverableActivationFailure)
|
2026-04-23 09:45:05 +08:00
|
|
|
{
|
|
|
|
|
_startupAttemptRegistry.MarkOwnedSucceeded(lastStage, lastStageMessage);
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
_startupAttemptRegistry.MarkOwnedFailed(lastStage, lastStageMessage);
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-12 16:46:49 +08:00
|
|
|
PublishCoordinatorStatus(hostProcessAliveOverride: true, completed: true, succeeded: decisionResult.Success);
|
2026-04-23 09:45:05 +08:00
|
|
|
windowsClosingByCoordinator = true;
|
|
|
|
|
await CloseWindowsAsync(splashWindow, loadingDetailsWindow).ConfigureAwait(false);
|
|
|
|
|
return BuildResult(
|
2026-05-12 16:46:49 +08:00
|
|
|
success: decisionResult.Success,
|
2026-04-23 09:45:05 +08:00
|
|
|
stage: "launch",
|
2026-05-12 16:46:49 +08:00
|
|
|
code: decisionResult.Code,
|
|
|
|
|
message: decisionResult.Message,
|
2026-04-23 09:45:05 +08:00
|
|
|
details: MergeDetails(
|
|
|
|
|
launcherContextDetails,
|
|
|
|
|
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
|
|
|
|
{
|
|
|
|
|
["publicIpcConnected"] = "true",
|
2026-05-12 16:46:49 +08:00
|
|
|
["multiInstanceBehavior"] = multiInstanceBehavior.ToString(),
|
|
|
|
|
["existingHostPid"] = shellStatus?.ProcessId.ToString() ?? string.Empty,
|
|
|
|
|
["existingShellState"] = shellStatus?.ShellState ?? string.Empty,
|
|
|
|
|
["existingTrayState"] = shellStatus?.Tray.State ?? string.Empty,
|
|
|
|
|
["existingTaskbarUsable"] = shellStatus?.Taskbar.IsUsable.ToString() ?? string.Empty,
|
|
|
|
|
["activationAccepted"] = decisionResult.ActivationResult?.Accepted.ToString() ?? string.Empty
|
2026-04-23 09:45:05 +08:00
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 07:31:54 +08:00
|
|
|
reporter.Report("update", "Checking updates...");
|
2026-04-21 20:59:52 +08:00
|
|
|
var updateResult = await _updateEngine.ApplyPendingUpdateAsync().ConfigureAwait(false);
|
|
|
|
|
if (!updateResult.Success)
|
|
|
|
|
{
|
Launcher fix (#6)
* fix.hy3试图修复中
* Resolve dev paths and fix splash UI thread
Compute a solutionRoot and expand development search paths (LanMountainDesktop and dev-test) in DeploymentLocator, add logging when scanning/finding hosts, and return distinct full paths. Ensure backward-compatible path checks. Fix cross-thread UI calls: invoke splashWindow.DismissAsync on the UI thread in LauncherFlowCoordinator, and make SplashWindow.DismissAsync ensure it runs on the UI thread before closing (simplified Close call). These changes improve development host discovery and prevent UI-thread access issues during shutdown.
* 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.
* Add dev/debug startup flow and launch profiles
Handle design-time initialization and add a developer debug startup path: App now skips normal startup when in design mode and shows a DevDebugWindow when running in debug (unless a preview or apply-update command). CommandContext.IsDebugMode is extended to include DOTNET_ENVIRONMENT=Development via a new IsDevelopmentEnvironment helper. Program.Main and BuildAvaloniaApp are made public to aid tooling. Added multiple launchSettings profiles for debug and preview commands that set DOTNET_ENVIRONMENT=Development to simplify IDE debugging and UI previewing.
* Simplify splash to fade; add themed about banners
Simplify splash startup visuals by removing the multi-mode/slide behavior and always using a fade animation. Update App to create SplashWindow without a StartupVisualMode parameter and remove related fields, layout configuration, slide animation, and easing helpers from SplashWindow. Clean up unused using. Replace the single about_banner asset with theme-aware variants (about_banner_dark.png and about_banner_light.png), delete the old about_banner.png, and update AboutSettingsPage to use a DynamicResource ImageBrush (AboutBannerBrush) that selects the appropriate banner per theme.
* Use AppJsonContext for startup state serialization
Switch serialization to the source-generated System.Text.Json context: add JsonSerializable(typeof(StartupAttemptRecord)) to AppJsonContext and replace the previous JsonSerializerOptions-based Serialize/Deserialize calls with AppJsonContext.Default.StartupAttemptRecord. Also remove the now-unused SerializerOptions field. Additionally, update .gitignore to exclude /test-aot-publish.
* Add OOBE redesign, theme & data location support
Introduce a redesigned OOBE flow and data-location/theme support across the launcher. Adds a new ThemeService for applying light/dark and accent colors; integrates FluentIcons.Avalonia package for icons. Overhauls OobeWindow (UX animations, typing effect, multi-step theme and data-location pages, Monet options, and final welcome step) and its code-behind to handle step navigation, accent selection, and data-location resolution. Adds DataLocation UI and handlers (DataLocationPromptWindow changes, DataLocation resolver usage) and wires a DevDebug UI for toggling/opening the data-location page. UpdateEngineService now resolves the launcher root via DataLocationResolver. Misc: update various view models, localization entries and remove TrimmerRoots.xml.
* 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:41:26 +08:00
|
|
|
Logger.Warn($"Update apply failed, will try to launch existing version. Error='{updateResult.Message}'.");
|
|
|
|
|
reporter.Report("update", "Update failed, launching existing version...");
|
|
|
|
|
// Clean up corrupted update files to prevent repeated failures
|
|
|
|
|
try
|
|
|
|
|
{
|
|
|
|
|
_updateEngine.CleanupIncomingArtifacts();
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Logger.Warn($"Failed to cleanup update artifacts after failed update: {ex.Message}");
|
|
|
|
|
}
|
|
|
|
|
// Continue to launch existing version instead of aborting
|
2026-04-21 20:59:52 +08:00
|
|
|
}
|
|
|
|
|
|
Harden OOBE, launch-source and elevation flow
Introduce a per-user OOBE state model and hardened launch/elevation handling. Adds OobeStateFile/OobeLaunchDecision models, OobeStateService (persisting %LOCALAPPDATA%/.launcher/state/oobe-state.json), and LauncherExecutionContext to capture elevation and user SID. CommandContext now normalizes/infers launch-source values (normal, postinstall, apply-update, plugin-install, debug-preview) and exposes maintenance checks. LauncherFlowCoordinator propagates richer launcher context details for diagnostics and suppresses OOBE for elevated/maintenance contexts. PluginInstallerService avoids requesting elevation for user-scoped installs and returns a clear error when installation target is outside the current user's LocalAppData. LauncherClient maps and surfaces result codes, UpdateWorkflow and installer invocation now pass explicit --launch-source values, and WelcomeOobeStep persists OOBE completion via the new service. Adds unit tests (CommandContext, OobeStateService, PluginInstallerService), docs/specs/checklists for the contract, and makes internals visible to tests.
2026-04-22 09:25:22 +08:00
|
|
|
if (oobeDecision.ShouldShowOobe)
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
|
|
|
|
await Dispatcher.UIThread.InvokeAsync(() => splashWindow.Hide());
|
|
|
|
|
foreach (var step in _oobeSteps)
|
|
|
|
|
{
|
|
|
|
|
await step.RunAsync(CancellationToken.None).ConfigureAwait(false);
|
|
|
|
|
}
|
2026-04-22 07:31:54 +08:00
|
|
|
|
2026-04-21 20:59:52 +08:00
|
|
|
await Dispatcher.UIThread.InvokeAsync(() => splashWindow.Show());
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 07:31:54 +08:00
|
|
|
reporter.Report("launch", "Launching desktop...");
|
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
|
|
|
var launchOutcome = default(HostLaunchOutcome);
|
|
|
|
|
var attachableAttempt = _startupAttemptRegistry.TryGetAttachableAttempt(_context.LaunchSource, startupSuccessTracker.PolicyKey);
|
|
|
|
|
if (attachableAttempt is not null &&
|
|
|
|
|
_startupAttemptRegistry.AdoptAttempt(attachableAttempt.AttemptId) &&
|
|
|
|
|
TryGetLiveProcess(attachableAttempt.HostPid, out var attachedProcess))
|
|
|
|
|
{
|
|
|
|
|
trackedAttempt = attachableAttempt;
|
|
|
|
|
attachedToExistingAttempt = true;
|
|
|
|
|
ipcConnected = attachableAttempt.IpcConnected;
|
|
|
|
|
lastStage = attachableAttempt.LastObservedStage;
|
|
|
|
|
lastStageMessage = string.IsNullOrWhiteSpace(attachableAttempt.LastObservedMessage)
|
|
|
|
|
? "Attached to the existing startup attempt."
|
|
|
|
|
: attachableAttempt.LastObservedMessage;
|
|
|
|
|
reporter.Report(MapStartupStageToSplashStage(lastStage), lastStageMessage);
|
2026-04-23 09:45:05 +08:00
|
|
|
PublishCoordinatorStatus(hostProcessAliveOverride: 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
|
|
|
|
|
|
|
|
if (startupSuccessTracker.TryResolve(lastStage, out var attachedSuccessState))
|
|
|
|
|
{
|
|
|
|
|
windowsClosingByCoordinator = true;
|
|
|
|
|
_startupAttemptRegistry.MarkOwnedSucceeded(attachedSuccessState.Stage, attachedSuccessState.Message);
|
|
|
|
|
await CloseWindowsAsync(splashWindow, loadingDetailsWindow).ConfigureAwait(false);
|
|
|
|
|
return BuildResult(
|
|
|
|
|
success: true,
|
|
|
|
|
stage: "launch",
|
|
|
|
|
code: attachedSuccessState.Code,
|
|
|
|
|
message: attachedSuccessState.Message,
|
|
|
|
|
details: MergeDetails(
|
|
|
|
|
launcherContextDetails,
|
|
|
|
|
BuildAttemptDetails(
|
|
|
|
|
trackedAttempt,
|
|
|
|
|
attachedToExistingAttempt,
|
|
|
|
|
ipcConnected,
|
|
|
|
|
hostProcessAlive: true,
|
|
|
|
|
lastStage,
|
|
|
|
|
lastStageMessage,
|
|
|
|
|
activationFailureReason,
|
|
|
|
|
softTimeoutShown: false,
|
|
|
|
|
recoveryActivationAttempted: false)));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (attachableAttempt.State is StartupAttemptState.SoftTimeout or StartupAttemptState.DetachedWaiting)
|
|
|
|
|
{
|
|
|
|
|
softTimeoutShown = true;
|
|
|
|
|
reporter.Report("delayed", SoftTimeoutStatusMessage);
|
|
|
|
|
loadingState = BuildDelayedLoadingState(
|
|
|
|
|
loadingState,
|
|
|
|
|
SoftTimeoutStatusMessage,
|
|
|
|
|
SoftTimeoutDetailsMessage,
|
|
|
|
|
trackedAttempt.StartedAtUtc);
|
|
|
|
|
loadingDetailsWindow?.UpdateLoadingState(loadingState);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
launchOutcome = HostLaunchOutcome.FromProcess(
|
|
|
|
|
attachedProcess!,
|
|
|
|
|
BuildResult(
|
|
|
|
|
true,
|
|
|
|
|
"launchHost",
|
|
|
|
|
"attached_attempt",
|
|
|
|
|
"Attached to an existing startup attempt.",
|
|
|
|
|
BuildAttemptDetails(
|
|
|
|
|
trackedAttempt,
|
|
|
|
|
attachedToExistingAttempt,
|
|
|
|
|
ipcConnected,
|
|
|
|
|
hostProcessAlive: true,
|
|
|
|
|
lastStage,
|
|
|
|
|
lastStageMessage,
|
|
|
|
|
activationFailureReason,
|
|
|
|
|
softTimeoutShown,
|
|
|
|
|
recoveryActivationAttempted: false)),
|
|
|
|
|
BuildAttemptDetails(
|
|
|
|
|
trackedAttempt,
|
|
|
|
|
attachedToExistingAttempt,
|
|
|
|
|
ipcConnected,
|
|
|
|
|
hostProcessAlive: true,
|
|
|
|
|
lastStage,
|
|
|
|
|
lastStageMessage,
|
|
|
|
|
activationFailureReason,
|
|
|
|
|
softTimeoutShown,
|
|
|
|
|
recoveryActivationAttempted: false));
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
launchOutcome = await LaunchHostWithIpcAsync().ConfigureAwait(false);
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 07:31:54 +08:00
|
|
|
if (!launchOutcome.Result.Success)
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
Harden OOBE, launch-source and elevation flow
Introduce a per-user OOBE state model and hardened launch/elevation handling. Adds OobeStateFile/OobeLaunchDecision models, OobeStateService (persisting %LOCALAPPDATA%/.launcher/state/oobe-state.json), and LauncherExecutionContext to capture elevation and user SID. CommandContext now normalizes/infers launch-source values (normal, postinstall, apply-update, plugin-install, debug-preview) and exposes maintenance checks. LauncherFlowCoordinator propagates richer launcher context details for diagnostics and suppresses OOBE for elevated/maintenance contexts. PluginInstallerService avoids requesting elevation for user-scoped installs and returns a clear error when installation target is outside the current user's LocalAppData. LauncherClient maps and surfaces result codes, UpdateWorkflow and installer invocation now pass explicit --launch-source values, and WelcomeOobeStep persists OOBE completion via the new service. Adds unit tests (CommandContext, OobeStateService, PluginInstallerService), docs/specs/checklists for the contract, and makes internals visible to tests.
2026-04-22 09:25:22 +08:00
|
|
|
return WithAdditionalDetails(launchOutcome.Result, launcherContextDetails);
|
2026-04-21 20:59:52 +08:00
|
|
|
}
|
|
|
|
|
|
2026-04-22 07:31:54 +08:00
|
|
|
if (launchOutcome.ImmediateResult is not null)
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
2026-04-22 07:31:54 +08:00
|
|
|
await CloseWindowsAsync(splashWindow, loadingDetailsWindow).ConfigureAwait(false);
|
Harden OOBE, launch-source and elevation flow
Introduce a per-user OOBE state model and hardened launch/elevation handling. Adds OobeStateFile/OobeLaunchDecision models, OobeStateService (persisting %LOCALAPPDATA%/.launcher/state/oobe-state.json), and LauncherExecutionContext to capture elevation and user SID. CommandContext now normalizes/infers launch-source values (normal, postinstall, apply-update, plugin-install, debug-preview) and exposes maintenance checks. LauncherFlowCoordinator propagates richer launcher context details for diagnostics and suppresses OOBE for elevated/maintenance contexts. PluginInstallerService avoids requesting elevation for user-scoped installs and returns a clear error when installation target is outside the current user's LocalAppData. LauncherClient maps and surfaces result codes, UpdateWorkflow and installer invocation now pass explicit --launch-source values, and WelcomeOobeStep persists OOBE completion via the new service. Adds unit tests (CommandContext, OobeStateService, PluginInstallerService), docs/specs/checklists for the contract, and makes internals visible to tests.
2026-04-22 09:25:22 +08:00
|
|
|
return WithAdditionalDetails(launchOutcome.ImmediateResult, launcherContextDetails);
|
2026-04-22 07:31:54 +08:00
|
|
|
}
|
2026-04-21 20:59:52 +08:00
|
|
|
|
2026-04-22 07:31:54 +08:00
|
|
|
if (launchOutcome.Process is null)
|
|
|
|
|
{
|
|
|
|
|
return BuildResult(
|
|
|
|
|
success: false,
|
|
|
|
|
stage: "launch",
|
|
|
|
|
code: "host_start_failed",
|
|
|
|
|
message: "Host launch did not create a process.",
|
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
|
|
|
details: MergeDetails(
|
|
|
|
|
launcherContextDetails,
|
|
|
|
|
MergeDetails(
|
|
|
|
|
launchOutcome.Details,
|
|
|
|
|
BuildAttemptDetails(
|
|
|
|
|
trackedAttempt,
|
|
|
|
|
attachedToExistingAttempt,
|
|
|
|
|
ipcConnected,
|
|
|
|
|
hostProcessAlive: false,
|
|
|
|
|
lastStage,
|
|
|
|
|
lastStageMessage,
|
|
|
|
|
activationFailureReason,
|
|
|
|
|
softTimeoutShown,
|
|
|
|
|
recoveryActivationAttempted: false))));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!attachedToExistingAttempt)
|
|
|
|
|
{
|
2026-04-23 09:45:05 +08:00
|
|
|
var reservedAttempt = _startupAttemptRegistry.GetOwnedAttempt();
|
|
|
|
|
trackedAttempt = reservedAttempt is { ReservedBeforeHostStart: true }
|
|
|
|
|
? _startupAttemptRegistry.AssignOwnedHostProcess(
|
|
|
|
|
launchOutcome.Process.Id,
|
|
|
|
|
lastStage,
|
|
|
|
|
lastStageMessage)
|
|
|
|
|
: _startupAttemptRegistry.StartOwnedAttempt(
|
|
|
|
|
launchOutcome.Process.Id,
|
|
|
|
|
_context.LaunchSource,
|
|
|
|
|
startupSuccessTracker.PolicyKey,
|
|
|
|
|
lastStage,
|
|
|
|
|
lastStageMessage);
|
|
|
|
|
PublishCoordinatorStatus(hostProcessAliveOverride: true);
|
2026-04-22 07:31:54 +08:00
|
|
|
}
|
|
|
|
|
|
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
|
|
|
Dictionary<string, string> ComposeLaunchDetails(bool hostProcessAlive, bool recoveryActivationAttempted = false)
|
2026-04-22 07:31:54 +08:00
|
|
|
{
|
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 MergeDetails(
|
|
|
|
|
launcherContextDetails,
|
|
|
|
|
MergeDetails(
|
|
|
|
|
launchOutcome.Details,
|
|
|
|
|
BuildAttemptDetails(
|
|
|
|
|
trackedAttempt,
|
|
|
|
|
attachedToExistingAttempt,
|
|
|
|
|
ipcConnected,
|
|
|
|
|
hostProcessAlive,
|
|
|
|
|
lastStage,
|
|
|
|
|
lastStageMessage,
|
|
|
|
|
activationFailureReason,
|
|
|
|
|
softTimeoutShown,
|
|
|
|
|
recoveryActivationAttempted)));
|
2026-04-22 07:31:54 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-28 10:27:33 +08:00
|
|
|
var monitor = new HostStartupMonitor();
|
|
|
|
|
var monitorOutcome = await monitor.MonitorUntilCompleteAsync(new HostStartupMonitor.Request(
|
|
|
|
|
launchOutcome.Process,
|
|
|
|
|
ipcClient,
|
|
|
|
|
startupSuccessTracker,
|
|
|
|
|
_startupAttemptRegistry,
|
|
|
|
|
trackedAttempt,
|
|
|
|
|
attachedToExistingAttempt,
|
|
|
|
|
launcherContextDetails,
|
|
|
|
|
successTcs,
|
|
|
|
|
activationFailedTcs,
|
|
|
|
|
reporter,
|
|
|
|
|
loadingDetailsWindow,
|
|
|
|
|
loadingState,
|
|
|
|
|
lastStage,
|
|
|
|
|
lastStageMessage,
|
|
|
|
|
ipcConnected,
|
|
|
|
|
activationFailureReason,
|
|
|
|
|
softTimeoutShown,
|
|
|
|
|
(hostProcessAliveOverride, completed, succeeded) =>
|
|
|
|
|
PublishCoordinatorStatus(hostProcessAliveOverride, completed, succeeded),
|
|
|
|
|
ComposeLaunchDetails)).ConfigureAwait(false);
|
2026-04-23 19:04:39 +08:00
|
|
|
|
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
|
|
|
windowsClosingByCoordinator = true;
|
2026-04-22 07:31:54 +08:00
|
|
|
await CloseWindowsAsync(splashWindow, loadingDetailsWindow).ConfigureAwait(false);
|
|
|
|
|
return BuildResult(
|
2026-05-28 10:27:33 +08:00
|
|
|
success: monitorOutcome.Success,
|
2026-04-22 07:31:54 +08:00
|
|
|
stage: "launch",
|
2026-05-28 10:27:33 +08:00
|
|
|
code: monitorOutcome.Code,
|
|
|
|
|
message: monitorOutcome.Message,
|
|
|
|
|
details: monitorOutcome.Details);
|
2026-04-21 20:59:52 +08:00
|
|
|
}
|
|
|
|
|
finally
|
|
|
|
|
{
|
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
|
|
|
if (splashClosedHandler is not null)
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
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
|
|
|
splashWindow.Closed -= splashClosedHandler;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!windowsClosingByCoordinator)
|
|
|
|
|
{
|
|
|
|
|
await Dispatcher.UIThread.InvokeAsync(() =>
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
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
|
|
|
try
|
2026-04-21 20:59:52 +08:00
|
|
|
{
|
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
|
|
|
if (splashWindow.IsVisible && splashWindow.IsLoaded)
|
|
|
|
|
{
|
|
|
|
|
splashWindow.Close();
|
|
|
|
|
Logger.Info("Splash window closed in coordinator cleanup.");
|
|
|
|
|
}
|
2026-04-21 20:59:52 +08:00
|
|
|
}
|
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 (Exception ex)
|
|
|
|
|
{
|
|
|
|
|
Logger.Error("Failed to close splash window during coordinator cleanup.", ex);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
2026-04-21 20:59:52 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
{
|
2026-04-22 07:31:54 +08:00
|
|
|
Logger.Error("Launcher coordinator failed.", ex);
|
|
|
|
|
return BuildResult(
|
|
|
|
|
success: false,
|
|
|
|
|
stage: "launch",
|
|
|
|
|
code: "exception",
|
|
|
|
|
message: ex.Message,
|
Harden OOBE, launch-source and elevation flow
Introduce a per-user OOBE state model and hardened launch/elevation handling. Adds OobeStateFile/OobeLaunchDecision models, OobeStateService (persisting %LOCALAPPDATA%/.launcher/state/oobe-state.json), and LauncherExecutionContext to capture elevation and user SID. CommandContext now normalizes/infers launch-source values (normal, postinstall, apply-update, plugin-install, debug-preview) and exposes maintenance checks. LauncherFlowCoordinator propagates richer launcher context details for diagnostics and suppresses OOBE for elevated/maintenance contexts. PluginInstallerService avoids requesting elevation for user-scoped installs and returns a clear error when installation target is outside the current user's LocalAppData. LauncherClient maps and surfaces result codes, UpdateWorkflow and installer invocation now pass explicit --launch-source values, and WelcomeOobeStep persists OOBE completion via the new service. Adds unit tests (CommandContext, OobeStateService, PluginInstallerService), docs/specs/checklists for the contract, and makes internals visible to tests.
2026-04-22 09:25:22 +08:00
|
|
|
details: BuildLauncherContextDetails(_context, _oobeStateService.Evaluate(_context), _deploymentLocator.GetAppRoot()),
|
2026-04-22 07:31:54 +08:00
|
|
|
errorMessage: ex.ToString());
|
2026-04-21 20:59:52 +08:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-22 07:31:54 +08:00
|
|
|
|
|
|
|
|
|
2026-04-21 20:59:52 +08:00
|
|
|
|
2026-04-22 07:31:54 +08:00
|
|
|
|
2026-05-28 10:27:33 +08:00
|
|
|
private static LauncherResult BuildResult(
|
|
|
|
|
bool success,
|
|
|
|
|
string stage,
|
|
|
|
|
string code,
|
|
|
|
|
string message,
|
|
|
|
|
Dictionary<string, string>? details = null,
|
|
|
|
|
string? errorMessage = null)
|
2026-04-22 07:31:54 +08:00
|
|
|
{
|
2026-05-28 10:27:33 +08:00
|
|
|
Logger.Info($"Launcher result prepared. Success={success}; Stage='{stage}'; Code='{code}'.");
|
|
|
|
|
return new LauncherResult
|
2026-04-22 07:31:54 +08:00
|
|
|
{
|
2026-05-28 10:27:33 +08:00
|
|
|
Success = success,
|
|
|
|
|
Stage = stage,
|
|
|
|
|
Code = code,
|
|
|
|
|
Message = message,
|
|
|
|
|
ErrorMessage = errorMessage,
|
|
|
|
|
Details = details ?? []
|
2026-04-22 07:31:54 +08:00
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2026-05-28 10:27:33 +08:00
|
|
|
private static LauncherResult WithAdditionalDetails(LauncherResult result, Dictionary<string, string> details)
|
2026-05-24 23:12:48 +08:00
|
|
|
{
|
2026-05-28 10:27:33 +08:00
|
|
|
return new LauncherResult
|
2026-05-24 23:12:48 +08:00
|
|
|
{
|
2026-05-28 10:27:33 +08:00
|
|
|
Success = result.Success,
|
|
|
|
|
Stage = result.Stage,
|
|
|
|
|
Code = result.Code,
|
|
|
|
|
Message = result.Message,
|
|
|
|
|
CurrentVersion = result.CurrentVersion,
|
|
|
|
|
TargetVersion = result.TargetVersion,
|
|
|
|
|
RolledBackTo = result.RolledBackTo,
|
|
|
|
|
Details = MergeDetails(details, result.Details),
|
|
|
|
|
InstalledPackagePath = result.InstalledPackagePath,
|
|
|
|
|
ManifestId = result.ManifestId,
|
|
|
|
|
ManifestName = result.ManifestName,
|
|
|
|
|
ErrorMessage = result.ErrorMessage
|
|
|
|
|
};
|
2026-05-24 23:12:48 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-28 10:27:33 +08:00
|
|
|
private static Dictionary<string, string> BuildLauncherContextDetails(
|
|
|
|
|
CommandContext context,
|
|
|
|
|
OobeLaunchDecision oobeDecision,
|
|
|
|
|
string appRoot)
|
2026-04-22 07:31:54 +08:00
|
|
|
{
|
2026-05-28 10:27:33 +08:00
|
|
|
return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase)
|
2026-04-22 07:31:54 +08:00
|
|
|
{
|
2026-05-28 10:27:33 +08:00
|
|
|
["command"] = context.Command,
|
|
|
|
|
["launchSource"] = context.LaunchSource,
|
|
|
|
|
["isGuiMode"] = context.IsGuiCommand.ToString(),
|
|
|
|
|
["isDebugMode"] = context.IsDebugMode.ToString(),
|
|
|
|
|
["isElevated"] = oobeDecision.IsElevated.ToString(),
|
|
|
|
|
["resolvedAppRoot"] = appRoot,
|
|
|
|
|
["oobeStatePath"] = oobeDecision.StatePath,
|
|
|
|
|
["oobeStateStatus"] = oobeDecision.Status.ToString(),
|
|
|
|
|
["oobeDecision"] = oobeDecision.ShouldShowOobe ? "show" : "skip",
|
|
|
|
|
["oobeSuppressionReason"] = oobeDecision.SuppressionReason,
|
|
|
|
|
["oobeResultCode"] = oobeDecision.ResultCode,
|
|
|
|
|
["userSid"] = oobeDecision.UserSid ?? string.Empty,
|
|
|
|
|
["usedLegacyOobeMarker"] = oobeDecision.UsedLegacyMarker.ToString(),
|
|
|
|
|
["migratedLegacyOobeMarker"] = oobeDecision.MigratedLegacyMarker.ToString(),
|
|
|
|
|
["oobeStateError"] = oobeDecision.ErrorMessage
|
|
|
|
|
};
|
|
|
|
|
}
|
2026-04-22 07:31:54 +08:00
|
|
|
|
|
|
|
|
|
2026-05-28 10:27:33 +08:00
|
|
|
private static Dictionary<string, string> MergeDetails(
|
|
|
|
|
Dictionary<string, string> left,
|
|
|
|
|
Dictionary<string, string> right)
|
|
|
|
|
{
|
|
|
|
|
var merged = new Dictionary<string, string>(left, StringComparer.OrdinalIgnoreCase);
|
|
|
|
|
foreach (var pair in right)
|
2026-04-22 07:31:54 +08:00
|
|
|
{
|
2026-05-28 10:27:33 +08:00
|
|
|
merged[pair.Key] = pair.Value;
|
2026-04-22 07:31:54 +08:00
|
|
|
}
|
|
|
|
|
|
2026-05-28 10:27:33 +08:00
|
|
|
return merged;
|
2026-04-22 07:31:54 +08:00
|
|
|
}
|
2026-05-28 10:27:33 +08:00
|
|
|
}
|
2026-04-21 20:59:52 +08:00
|
|
|
|