Conversation
Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com>
|
@copilot What is the function of UI Contributions? I don't think it's necessary. |
UI Contributions allow plugins to extend the host application's user interface by:
This follows VS Code's extension model where plugins can extend the editor UI (command palette, menus, sidebars, etc.). However, if your application doesn't need plugins to modify the UI (e.g., if plugins only perform background processing or data manipulation), these interfaces can be removed. The UI module is independent - removing the Would you like me to remove the UI Contributions module, or would you prefer to keep it for potential future use? |
@copilot No need, remove it. |
Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com>
UI Contributions module removed in commit 4f750a1. The following changes were made:
|
|
@copilot Please fully understand the requirements mentioned in the current issue first, and then continue to implement all the functions based on the existing architecture. |
I want to clarify your request. The original issue stated "No specific logic implementation is required; method bodies only need to throw new NotImplementedException() or have empty implementations." Are you now asking me to:
Currently, the framework has 36 files with full structure definitions and NotImplementedException where appropriate. Please confirm which direction you'd like me to take. |
Implement actual business logic for all methods (Parse, Compare, Install, Update, etc.) |
Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com>
Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com>
Implementation in progress. Completed so far (commits 96860fc, e4d8a25): Core Functionality:
Remaining to implement:
The framework is functional with core extension management working. Continuing with remaining modules...
Business logic implementation complete! (Commits: 96860fc, e4d8a25, 571867f, fad0e82) All Core Systems Implemented: Framework Statistics:
The plugin manager framework is fully functional with all requested business logic implemented! |
Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com>
Co-authored-by: JusterZhu <11714536+JusterZhu@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
This PR implements a comprehensive plugin extension framework for WPF/Avalonia desktop applications, inspired by VS Code and VSIX design patterns. The framework includes 47 files across 8 namespaces, providing a complete plugin system with multi-language runtime support, advanced update mechanisms, and enterprise features.
Changes:
- Complete plugin extension framework with working implementations for semantic versioning, extension lifecycle management, and multi-language runtime support
- Advanced update system supporting full, incremental, and differential updates with rollback capabilities
- Enterprise features including offline installation, repository mirrors, and security policies with signature validation
- SDK for extension developers with VS Code-style activation events and host application integration APIs
Reviewed changes
Copilot reviewed 49 out of 49 changed files in this pull request and generated 20 comments.
Show a summary per file
| File | Description |
|---|---|
| SemVersion.cs | Semantic versioning implementation with full SemVer 2.0 parsing and comparison |
| SemVersionComparer.cs | Version comparison logic with range matching support |
| ExtensionManager.cs | Core extension lifecycle management (install, uninstall, enable, disable, update) |
| ExtensionLoader.cs | Extension loading and activation with runtime resolution |
| ExtensionRepository.cs | Repository management with search and download capabilities |
| SignatureValidator.cs | Digital signature and integrity verification |
| UpdateService.cs | Update management with download and installation logic |
| DeltaUpdateService.cs | Delta/incremental update support with patch generation |
| RuntimeResolver.cs | Multi-language runtime resolution system |
| RuntimeHosts.cs | Concrete runtime host implementations for .NET, Python, Node.js, Lua, and executables |
| ExtensionContext.cs | Extension runtime context with storage and configuration |
| ExtensionAPI.cs | Host application API for command registration and events |
| ExtensionActivationEvent.cs | VS Code-style activation event system |
| Security/EnterprisePolicy.cs | Enterprise-level policy definitions |
| Security/OfflinePackageIndex.cs | Offline package index management |
| Packaging/*.cs | VSIX-style package structure and metadata |
| GeneralUpdate.Extension.csproj | Project configuration with System.Text.Json dependency |
| README.md | Comprehensive framework documentation |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| private readonly Dictionary<string, Func<object[], object>> _commands; | ||
| private readonly Dictionary<string, List<Action<object>>> _eventSubscribers; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="ExtensionAPI"/> class. | ||
| /// </summary> | ||
| /// <param name="hostName">The name of the host application.</param> | ||
| /// <param name="hostVersion">The version of the host application.</param> | ||
| public ExtensionAPI(string hostName, string hostVersion) | ||
| { | ||
| HostName = hostName ?? "Unknown Host"; | ||
| HostVersion = hostVersion ?? "1.0.0"; | ||
| _commands = new Dictionary<string, Func<object[], object>>(); | ||
| _eventSubscribers = new Dictionary<string, List<Action<object>>>(); | ||
| } |
There was a problem hiding this comment.
Thread safety issue: The _commands and _eventSubscribers dictionaries are accessed and modified without synchronization. In a multi-threaded environment where multiple extensions might be registering/unregistering commands or subscribing/unsubscribing from events concurrently, this could lead to race conditions and collection modification exceptions. Consider using ConcurrentDictionary or adding appropriate locking mechanisms.
| HashAlgorithm hasher; | ||
| switch (hashAlgorithm?.ToUpperInvariant()) | ||
| { | ||
| case "SHA256": | ||
| hasher = SHA256.Create(); | ||
| break; | ||
| case "SHA512": | ||
| hasher = SHA512.Create(); | ||
| break; | ||
| case "MD5": | ||
| hasher = MD5.Create(); | ||
| break; | ||
| default: | ||
| hasher = SHA256.Create(); | ||
| break; | ||
| } |
There was a problem hiding this comment.
The MD5 hash algorithm is included as an option but MD5 is cryptographically broken and should not be used for security-related purposes like package integrity verification. Including MD5 as an option could encourage its use in security-sensitive contexts. Consider removing MD5 support or adding a clear warning in the documentation that MD5 should only be used for non-security purposes like checksums.
| if (baseVersion.Major == 0) | ||
| { | ||
| // For 0.x.y, only minor and patch must match exactly or be greater | ||
| return version.Major == 0 && version.Minor == baseVersion.Minor && version >= baseVersion; | ||
| } |
There was a problem hiding this comment.
The caret (^) operator implementation for version 0.x.y is incorrect. According to SemVer 2.0 specification and npm's semver behavior, ^0.x.y should match versions >=0.x.y and <0.(x+1).0, not just >=0.x.y with the same minor version. For example, ^0.2.3 should match 0.2.3 and 0.2.4 but not 0.3.0, whereas the current implementation would reject 0.2.4 since it only checks for exact minor version match.
| _pythonProcess?.Kill(); | ||
| _pythonProcess?.Dispose(); | ||
| return Task.CompletedTask; |
There was a problem hiding this comment.
The process cleanup in runtime hosts has a potential null reference issue. If _pythonProcess (or _nodeProcess, _exeProcess) is null but Kill() is called, it will safely do nothing due to the null-conditional operator. However, if the process has already exited, Kill() will throw an InvalidOperationException. Consider wrapping the Kill() call in a try-catch block or checking HasExited property first to avoid exceptions during cleanup.
| var extensionDir = Path.Combine(_extensionsPath, extensionId); | ||
| var manifestPath = Path.Combine(extensionDir, "manifest.json"); | ||
|
|
||
| if (!File.Exists(manifestPath)) | ||
| return false; |
There was a problem hiding this comment.
Potential path traversal vulnerability: The extensionId parameter is used directly to construct a directory path at line 132 without validation. This pattern repeats throughout the ExtensionManager class (also at lines 90 and 60). A malicious extension ID could access files outside the intended directory. Consider creating a helper method that validates extension IDs before constructing paths.
| private readonly Dictionary<string, ExtensionManifest> _loadedExtensions; | ||
| private readonly HashSet<string> _activeExtensions; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="ExtensionLoader"/> class. | ||
| /// </summary> | ||
| /// <param name="runtimeResolver">The runtime resolver for loading different extension types.</param> | ||
| public ExtensionLoader(IRuntimeResolver runtimeResolver) | ||
| { | ||
| _runtimeResolver = runtimeResolver ?? throw new ArgumentNullException(nameof(runtimeResolver)); | ||
| _loadedExtensions = new Dictionary<string, ExtensionManifest>(); | ||
| _activeExtensions = new HashSet<string>(); | ||
| } |
There was a problem hiding this comment.
Thread safety issue: The _loadedExtensions dictionary and _activeExtensions HashSet are accessed and modified without synchronization. Concurrent load/unload/activate/deactivate operations could lead to race conditions and collection modification exceptions. Consider using thread-safe collections like ConcurrentDictionary or adding appropriate locking mechanisms.
| HashAlgorithm hasher; | ||
| switch (hashAlgorithm?.ToUpperInvariant()) | ||
| { | ||
| case "SHA256": | ||
| hasher = SHA256.Create(); | ||
| break; | ||
| case "SHA512": | ||
| hasher = SHA512.Create(); | ||
| break; | ||
| case "MD5": | ||
| hasher = MD5.Create(); | ||
| break; | ||
| default: | ||
| hasher = SHA256.Create(); | ||
| break; | ||
| } | ||
|
|
||
| using (hasher) | ||
| { | ||
| using (var stream = File.OpenRead(packagePath)) | ||
| { | ||
| var hashBytes = await Task.Run(() => hasher.ComputeHash(stream)); | ||
| var actualHash = BitConverter.ToString(hashBytes).Replace("-", "").ToLowerInvariant(); | ||
|
|
||
| return actualHash.Equals(expectedHash.ToLowerInvariant(), StringComparison.Ordinal); | ||
| } | ||
| } |
There was a problem hiding this comment.
Resource leak: The HashAlgorithm instances created by SHA256.Create(), SHA512.Create(), and MD5.Create() are properly disposed via the using statement, but the hasher variable assignment happens outside the using block. If an exception occurs between creating the hasher and entering the using block (lines 89-106), the hasher won't be disposed. While unlikely in this simple assignment case, it's better to create the hasher directly in the using statement for each case.
| var extensionDir = Path.Combine(_extensionsPath, manifest.Id); | ||
| if (!Directory.Exists(extensionDir)) | ||
| { | ||
| Directory.CreateDirectory(extensionDir); | ||
| } |
There was a problem hiding this comment.
Potential path traversal vulnerability: The extensionId parameter is used directly to construct a directory path without validation. A malicious extension ID containing path traversal sequences (e.g., "../../../", "..\..\") could potentially access or modify files outside the intended extensions directory. Consider validating the extensionId to ensure it doesn't contain directory traversal characters or use Path.GetFileName to extract only the filename portion.
| _nodeProcess?.Kill(); | ||
| _nodeProcess?.Dispose(); |
There was a problem hiding this comment.
The process cleanup in NodeRuntimeHost has the same issue. If the process has already exited, calling Kill() will throw an InvalidOperationException. Consider wrapping the Kill() call in a try-catch block or checking the HasExited property first.
| _nodeProcess?.Kill(); | |
| _nodeProcess?.Dispose(); | |
| if (_nodeProcess != null) | |
| { | |
| try | |
| { | |
| if (!_nodeProcess.HasExited) | |
| { | |
| _nodeProcess.Kill(); | |
| } | |
| } | |
| catch (InvalidOperationException) | |
| { | |
| // The process may have already exited or cannot be killed; ignore during cleanup. | |
| } | |
| finally | |
| { | |
| _nodeProcess.Dispose(); | |
| _nodeProcess = null; | |
| } | |
| } |
| _exeProcess?.Kill(); | ||
| _exeProcess?.Dispose(); |
There was a problem hiding this comment.
The process cleanup in ExeRuntimeHost has the same issue. If the process has already exited, calling Kill() will throw an InvalidOperationException. Consider wrapping the Kill() call in a try-catch block or checking the HasExited property first.
| _exeProcess?.Kill(); | |
| _exeProcess?.Dispose(); | |
| if (_exeProcess != null) | |
| { | |
| try | |
| { | |
| if (!_exeProcess.HasExited) | |
| { | |
| _exeProcess.Kill(); | |
| } | |
| } | |
| catch (InvalidOperationException) | |
| { | |
| // The process has already exited or cannot be killed; ignore. | |
| } | |
| finally | |
| { | |
| _exeProcess.Dispose(); | |
| _exeProcess = null; | |
| } | |
| } |
Implements a complete plugin extension framework inspired by VS Code and VSIX, providing a fully functional multi-language plugin system with advanced update mechanisms and enterprise features. All core functionality includes working business logic implementations.
Framework Modules (47 files across 8 namespaces)
Packaging (
MyApp.Extensions.Packaging)Core Models (
MyApp.Extensions)Update System (
MyApp.Extensions.Updates)Runtime Support (
MyApp.Extensions.Runtime)Lifecycle Management (
MyApp.Extensions)Repository & Management (
MyApp.Extensions)Enterprise Features (
MyApp.Extensions.Security)SDK (
MyApp.Extensions.SDK)Usage
Implementation Details
All types include complete XML documentation. The framework now includes full working implementations with:
Technical Details
Note: The UI Contributions module (
MyApp.Extensions.UI) was removed based on project requirements, as UI extension points are not needed for this implementation.Original prompt
This section details on the original issue you should resolve
<issue_title>[FEATURE] Framework Design of Local Plugin Extension Manager</issue_title>
<issue_description>### Feature Description
Act as a senior .NET architect and help me generate a plugin manager framework code suitable for WPF/Avalonia desktop applications (structure definition only, no business logic implementation), with reference to the VS Code Extension Mechanism and VSIX design ideas.
✅ Overall Objectives
Generate a C# code framework that includes definitions of key interfaces, classes, fields, enums, events and data models.
No specific logic implementation is required; method bodies only need to throw new NotImplementedException() or have empty implementations.
Plugins can be developed in any programming language (Lua / Python / Node / native executables, etc.) — only abstract design-level support is needed.
Plugin updates must support full package update / incremental update / differential update / rollback.
Output a multi-file structure (one file per model/interface).
✅ Mandatory Modules & Definitions
Define package structure and models, including the following core elements and conventions:
PackageManifest: Contains metadata, runtime configuration, dependencies, permissions, UI extension points, signatures
PackageFileEntry: In-package file index
PackageSignature: Signature/certificate/hash
PackageFormatVersion: Standardized package format version
Package Directory Conventions: manifest.json, assets/, runtime/, patches/, signature/
ExtensionManifest: ID/Name/Version/Author/Description/Entrypoint/Runtime/Engine/Compatibility/Dependencies/Permissions
ExtensionState (Enum): Installed/Enabled/Disabled/UpdateAvailable/Incompatible/Broken
SemVersion (Struct) + ISemVersionComparer (Interface)
ExtensionDependency: Dependency ID, version range, optional dependencies
ExtensionPermission: Permission type and scope
UpdateChannel (Enum): Stable/PreRelease/Dev
UpdateMetadata: Version list, update time, compatibility information
UpdatePackageInfo: Full/Delta/Diff package details, size, Hash, signature
DeltaPatchInfo: Baseline version, target version, patch algorithm, differential block information
RollbackInfo: Rollback target version, rollback reason
IDeltaUpdateService + IUpdateService (Core update interfaces)
Abstract runtime host architecture for cross-language plugin execution:
IRuntimeHost: Start()/Stop()/Invoke()/HealthCheck()
IRuntimeResolver: Maps runtime hosts based on runtime type
RuntimeType (Enum): DotNet/Lua/Python/Node/Exe/Custom
RuntimeEnvironmentInfo: Runtime version, installation path, startup parameters
Core abstraction for plugin lifecycle management and isolation:
IExtensionLifecycle: OnInstall()/OnActivate()/OnDeactivate()/OnUninstall()/OnUpdate()
IExtensionLoader: Load()/Unload()/Activate()/Deactivate()
IExtensionProcessHost: Process isolation support
IExtensionSandbox: Permission and resource isolation (file/network/system)
Standardized abstraction for UI customization contributions:
ICommandContribution
IMenuContribution
IPanelContribution
IShortcutContribution
IThemeContribution
UIContributionManifest: Unified structure for all UI contribution metadata
Core management and repository abstraction:
IExtensionRepository: Query plugins, retrieve version lists, pull packages, validate metadata
IExtensionManager: Install()/Uninstall()/Enable()/Disable()/Update()/Rollback()
IExtensionDependencyResolver: Resolve and validate plugin dependencies
ISignatureValidator: Validate package signatures and integrity
Specialized abstraction for enterprise-level and offline deployment scenarios:
IRepositoryMirror: Enterprise repository mirror management
IOfflineInstaller: Offline plugin installation
EnterprisePolicy: Rules for allowing/forbidding plugin sources
OfflinePackageIndex: Index management for local offline packages
APIs for plugin developers to interact with the host application:
IExtensionContext: Plugin runtime context
IExtensionAPI: Allows extensions to access host application services
ExtensionActivationEvent: Trigger-based plugin activation (similar to VS Code activation events)
✅ Output Format Requirements
Output C# code directly.
Must include complete XML Doc comments for all types and members.
Output by separate files: One file for each interface/model/enum.
No specific logic implementation (empty bodies or NotImplementedException only).
Use appropriate namespaces as follows:
MyApp.Extensions
MyApp.Extensions.Updates
MyApp.Extensions.Runtime
MyApp.Extensions.UI
MyApp.Extensions.Security
MyApp.Extensions.SDK
MyApp.Extensions.Packaging
✅ Additional Requirements
Extensible design: Support for adding new runtime types and update strategie...
✨ Let Copilot coding agent set things up for you — coding agent works faster and does higher quality work when set up for your repo.