refactor(launcher): converge plugin pending to Host via PluginPackaging

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
lincube
2026-05-28 10:28:31 +08:00
parent 545dee85a7
commit 1ee6e68f33
32 changed files with 888 additions and 1924 deletions

View File

@@ -27,9 +27,8 @@
3. 首次启动显示 OOBE 引导 (`OobeWindow`)
4. 显示 Splash 启动动画 (`SplashWindow`)
5. 检查并应用待处理的更新 (`UpdateEngineService.ApplyPendingUpdate`)
6. 处理插件升级队列 (`PluginUpgradeQueueService`)
7. 启动主程序 `app-{version}/LanMountainDesktop.exe`
8. 清理标记为 `.destroy` 的旧版本
6. 启动主程序 `app-{version}/LanMountainDesktop.exe`(待处理插件安装/升级由 Host 在 `PluginRuntimeService.ApplyPendingPluginOperations()` 中应用,而非 Launcher 启动流程)
7. 清理标记为 `.destroy` 的旧版本
**主程序启动流程 (LanMountainDesktop.exe):**
@@ -98,12 +97,11 @@
| 服务 | 职责 |
|------|------|
| `DeploymentLocator` | 扫描和定位 `app-*` 版本目录,选择最佳版本 |
| `UpdateCheckService` | 调用 GitHub Release API 检查更新,支持 Stable/Preview 频道 |
| `UpdateEngineService` | 下载、验证、应用增量更新,支持原子化更新和回滚 |
| `LauncherFlowCoordinator` | 协调 OOBE → Splash → 更新 → 插件 → 启动主程序的完整流程 |
| `LauncherFlowCoordinator` | 协调 OOBE → Splash → 更新 → 启动主程序的完整流程 |
| `OobeStateService` | 管理首次运行状态 |
| `PluginInstallerService` | 处理 `.laapp` 插件包安装 |
| `PluginUpgradeQueueService` | 批量处理插件升级队列 |
| `PluginInstallerService` | CLI 维护:`plugin install` 直接安装 `.laapp` |
| `PluginUpgradeQueueService` | CLI 维护:`plugin update` 应用待处理队列(正常市场安装/升级由 Host 处理) |
#### 版本管理机制
@@ -191,7 +189,7 @@ GitHub Release Assets:
This repository is organized around a desktop host app plus a host-side plugin ecosystem. `LanMountainDesktop/` contains the application entry points, UI, services, component system, and plugin runtime integration. The surrounding projects provide the public SDK, shared contracts, appearance infrastructure, settings primitives, host abstractions, runtime support, and tests.
**Launcher Architecture**: `LanMountainDesktop.Launcher/` serves as the single entry point, managing OOBE, splash screen, multi-version deployment, incremental updates, and plugin installation. It uses a version directory structure (`app-{version}/`) with marker files (`.current`, `.partial`, `.destroy`) to enable atomic updates and rollback capabilities. See the Chinese section above for detailed architecture documentation.
**Launcher Architecture**: `LanMountainDesktop.Launcher/` serves as the single entry point, managing OOBE, splash screen, multi-version deployment, and incremental updates. In-app plugin market installation is Host-owned: packages are downloaded into the current user's pending plugin queue and applied by the Host before plugin discovery on the next startup. The Launcher still keeps plugin CLI commands as maintenance compatibility entry points. It uses a version directory structure (`app-{version}/`) with marker files (`.current`, `.partial`, `.destroy`) to enable atomic updates and rollback capabilities. See the Chinese section above for detailed architecture documentation.
The runtime flow starts with the Launcher selecting the best version, then proceeds into `Program.cs`, into `App.axaml.cs`, initializes settings/theme/localization services, then boots the desktop shell, tray, windows, and plugin runtime. The most important behavior boundaries are component registration, plugin activation, appearance resources, and settings persistence.
@@ -272,3 +270,4 @@ See `docs/EXTERNAL_IPC_ARCHITECTURE.md` for the detailed contract and migration
- `apply-update`, `plugin-install`, and `debug-preview` must not auto-open OOBE.
- Elevation is allowed only for the installer, full installer update application, and user-confirmed legacy uninstall.
- Default plugin install should stay inside the user's LocalAppData scope and should not ask for UAC.
- Marketplace plugin installs are queued under the user's data root and take effect after restart; they do not use Launcher elevation.

View File

@@ -165,6 +165,8 @@ Use `LanMountainDesktop.slnx` as the workspace entry point. The standard loop is
For packaging, see `LanMountainDesktop/PACKAGING.md`. For plugin package generation or local feed workflows, use `scripts/Pack-PluginPackages.ps1`.
In-app marketplace plugin installs use a per-user pending plugin queue. The package is downloaded and verified immediately, then applied on the next Host startup before plugin discovery. `LanMountainDesktop.Launcher.exe plugin install` remains only as a maintenance compatibility command.
**Launcher Architecture**: LanMountainDesktop uses a Launcher as the single entry point, responsible for version management, updates, and launching the main application. See the Chinese section above for detailed architecture documentation.
## VeloPack Release Assets
@@ -172,4 +174,3 @@ For packaging, see `LanMountainDesktop/PACKAGING.md`. For plugin package generat
- Windows incremental release packaging now uses VeloPack native outputs (
eleases.win.json, *.nupkg).
- Launcher still performs update apply/rollback; VeloPack is used for package generation.
- Legacy delta script flow is retained behind a disabled fallback switch in CI.

View File

@@ -191,23 +191,20 @@ void MarkCompleted()
```
### PluginInstallerService
**职责**: 处理插件安装
**职责**: CLI 维护命令下的插件安装`plugin install`)。应用内插件市场安装由 Host 在启动时应用 pending 队列,不经过 Launcher 正常启动流程。
**关键方法**:
```csharp
// 安装插件包
Task<PluginInstallResult> InstallAsync(
string packagePath,
string targetDirectory,
CancellationToken cancellationToken = default)
// 安装插件包CLI 维护)
LauncherResult InstallPackage(string sourcePath, string pluginsDirectory)
```
### PluginUpgradeQueueService
**职责**: 批量处理插件升级队列
**职责**: CLI 维护命令下的待处理插件升级(`plugin update`。Launcher 正常 GUI 启动流程不再应用 pending 队列Host 在 `PluginRuntimeService.ApplyPendingPluginOperations()` 中统一处理。
**关键方法**:
```csharp
// 应用待处理的插件升级
// 应用待处理的插件升级CLI 维护)
LauncherResult ApplyPendingUpgrades(string pluginsDirectory)
```
@@ -381,19 +378,12 @@ public async Task<LauncherResult> RunAsync()
try
{
// 4. 应用更新
var updateResult = _updateEngine.ApplyPendingUpdate();
var updateResult = await _updateEngine.ApplyPendingUpdateAsync();
if (!updateResult.Success)
return updateResult;
// 5. 插件升级
var pluginsDir = Path.Combine(_deploymentLocator.GetAppRoot(), "plugins");
var queueResult = new PluginUpgradeQueueService(_pluginInstallerService)
.ApplyPendingUpgrades(pluginsDir);
if (!queueResult.Success)
return queueResult;
// 6. 启动主程序
var hostResult = LaunchHost();
Logger.Warn("Update apply failed, will try to launch existing version.");
// 5. 启动主程序(插件 pending 由 Host 应用,不在 Launcher 启动步骤处理)
var hostResult = await LaunchHostWithIpcAsync();
if (!hostResult.Success)
return hostResult;
@@ -454,7 +444,7 @@ LanMountainDesktop.Launcher.exe update rollback
LanMountainDesktop.Launcher.exe plugin install <path-to-plugin.laapp>
```
安装 `.laapp` 插件包。
维护兼容入口:直接把 `.laapp` 插件包写入指定插件目录。应用内插件市场不再使用 Launcher 做普通插件安装;市场安装会先把包下载到当前用户的 pending 队列,并在下一次 Host 启动、插件发现前应用
## 开发指南
@@ -561,6 +551,7 @@ var updateCheckService = new UpdateCheckService(
- `apply-update`, `plugin-install`, and `debug-preview` must not auto-enter OOBE.
- Allowed elevation paths are limited to the installer itself, full installer update application, and user-confirmed legacy uninstall.
- Default plugin installation targets the current user's LocalAppData scope and must not request elevation by default.
- In-app market installs are deferred Host-side operations: download and verify now, apply from the per-user pending queue on the next Host startup.
## Public IPC Baseline

View File

@@ -524,6 +524,8 @@ LanMountainDesktop.Launcher.exe plugin install MyAwesomePlugin-1.0.0.laapp
LanMountainDesktop.Launcher.exe launch
```
应用内插件市场不会调用 Launcher 安装插件。市场安装会把 `.laapp` 下载到当前用户的 pending 队列,并在下一次 Host 启动、插件发现前应用;上面的 Launcher 命令仅作为本地维护/兼容入口保留。
### 4. 发布插件
**选项 1: GitHub Release**