feat(pluginhost): introduce browser-navigable plugin resources in Management API

- Added `resources` field in `management.register` for defining browser-accessible resources.
- Updated examples and documentation to reflect resource-based paths under `/v0/resource/plugins/<pluginID>/...`.
- Replaced legacy `GET` menu routes with resource-based implementations for consistent plugin behavior.
- Enhanced request handling for resource paths, including proper response headers and streamlined test coverage.
This commit is contained in:
Luis Pater
2026-06-09 22:46:27 +08:00
parent 2aeb41cecf
commit 44ea9abced
22 changed files with 342 additions and 70 deletions

View File

@@ -109,7 +109,7 @@ type Capabilities struct {
UsagePlugin UsagePlugin
// CommandLinePlugin declares and handles plugin-owned command-line flags.
CommandLinePlugin CommandLinePlugin
// ManagementAPI declares plugin-owned diagnostic Management API routes.
// ManagementAPI declares plugin-owned diagnostic Management API and resource routes.
ManagementAPI ManagementAPI
}
@@ -921,7 +921,7 @@ type CommandLineExecutionResponse struct {
ExitCode int
}
// ManagementAPI declares plugin-owned Management API routes.
// ManagementAPI declares plugin-owned Management API and resource routes.
type ManagementAPI interface {
RegisterManagement(context.Context, ManagementRegistrationRequest) (ManagementRegistrationResponse, error)
}
@@ -932,12 +932,16 @@ type ManagementRegistrationRequest struct {
Plugin Metadata
// BasePath is the only Management API prefix plugins may register under.
BasePath string
// ResourceBasePath is the plugin resource prefix for browser-navigable resources.
ResourceBasePath string
}
// ManagementRegistrationResponse lists plugin-owned Management API routes.
// ManagementRegistrationResponse lists plugin-owned Management API and resource routes.
type ManagementRegistrationResponse struct {
// Routes contains the exact Management API routes to expose.
Routes []ManagementRoute
// Resources contains browser-navigable plugin resources exposed under /v0/resource/plugins/<pluginID>/.
Resources []ResourceRoute
}
// ManagementRoute describes one plugin-owned Management API route.
@@ -946,15 +950,27 @@ type ManagementRoute struct {
Method string
// Path is an exact path under /v0/management/. Relative paths are resolved under that prefix.
Path string
// Menu is the optional management UI menu label for GET routes.
// Menu is a legacy resource menu label. GET routes with Menu are registered under /v0/resource/plugins/<pluginID>/.
Menu string
// Description explains the management route for UI display.
// Description explains the legacy resource menu entry for UI display.
Description string
// Handler processes matching Management API requests.
Handler ManagementHandler
}
// ManagementHandler handles one plugin-owned Management API route.
// ResourceRoute describes one plugin-owned browser-navigable resource route.
type ResourceRoute struct {
// Path is an exact path under /v0/resource/plugins/<pluginID>/. Relative paths are resolved under that prefix.
Path string
// Menu is the management UI menu label for this GET resource.
Menu string
// Description explains the resource route for UI display.
Description string
// Handler processes matching resource requests. Resource requests are not management-authenticated.
Handler ManagementHandler
}
// ManagementHandler handles one plugin-owned Management API or resource route.
type ManagementHandler interface {
HandleManagement(context.Context, ManagementRequest) (ManagementResponse, error)
}

View File

@@ -48,16 +48,15 @@ func TestMetadataConfigFieldsExposePluginSchema(t *testing.T) {
}
}
func TestManagementRouteMenuFieldsExposeManagementUIHints(t *testing.T) {
route := ManagementRoute{
Method: "GET",
Path: "/plugins/example/status",
func TestResourceRouteMenuFieldsExposeManagementUIHints(t *testing.T) {
route := ResourceRoute{
Path: "/status",
Menu: "Example Status",
Description: "Shows example plugin status.",
Handler: compileTimePlugin{},
}
if route.Menu == "" || route.Description == "" {
t.Fatalf("management route missing menu fields: %#v", route)
t.Fatalf("resource route missing menu fields: %#v", route)
}
}