feat: add plugin web UI extensions - #991
Conversation
|
This pull request is currently a draft. Reviews will not take place until the PR is marked as ready for review. |
📝 WalkthroughWalkthroughAdds a host-projected plugin Web UI contract spanning manifest declarations, archive validation, runtime state, HTTP APIs, frontend mounting/navigation, configuration persistence, an executable exemplar, documentation, local archive installation, and expanded validation gates. ChangesPlugin Web UI projection
Estimated code review effort: 5 (Critical) | ~120 minutes Sequence Diagram(s)sequenceDiagram
participant PluginAuthor
participant ArchiveInstaller
participant HostRuntime
participant ConsoleUI
participant PluginBundle
PluginAuthor->>ArchiveInstaller: build and install local archive
ArchiveInstaller->>HostRuntime: persist validated Web UI metadata
ConsoleUI->>HostRuntime: fetch plugin Web UI state
HostRuntime-->>ConsoleUI: return state, pages, config, asset base URL
ConsoleUI->>PluginBundle: import and register ready bundle
PluginBundle-->>ConsoleUI: mount page or config section
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
88c58b7 to
1f85752
Compare
There was a problem hiding this comment.
Actionable comments posted: 17
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
crates/mesh-llm-host-runtime/src/api/server.rs (1)
165-174: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAdd
"/plugins"to the exact-match list.Currently, a request to exactly
/plugins(without a trailing slash) will not match any of the UI index routes and will result in a server 404. This is inconsistent with how/chatand/configurationare handled in this block.Add
| "/plugins"to thematches!macro to ensure users can navigate to the root plugin index without needing a trailing slash.🐛 Proposed fix
matches!( path, "/" | "/dashboard" | "/dashboard/" | "/chat" | "/chat/" | "/configuration" | "/configuration/" + | "/plugins" | "/__playground" | "/__meshviz-perf"🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/mesh-llm-host-runtime/src/api/server.rs` around lines 165 - 174, Update the UI route exact-match list in the matches! expression to include "/plugins", preserving the existing trailing-slash and other route entries.crates/mesh-llm-host-runtime/src/runtime_data/mod.rs (1)
1277-1505: 📐 Maintainability & Code Quality | 🟠 Major | 🏗️ Heavy liftExtract these helpers into a separate test module
crates/mesh-llm-host-runtime/src/runtime_data/mod.rsis already 1,801 lines, so it’s close to the Rust file cap. Moving this test block out will keep the module from crossing the limit.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/mesh-llm-host-runtime/src/runtime_data/mod.rs` around lines 1277 - 1505, Move the test and its helper functions, including runtime_data_plugin_reports_are_scoped_by_name_and_endpoint and the publish/assert helpers, into a separate test module while preserving their existing coverage and behavior. Keep production code in runtime_data/mod.rs and import the required runtime-data types and JSON utilities from the parent module.Source: Coding guidelines
🧹 Nitpick comments (6)
crates/mesh-llm-ui/e2e/plugins/web-ui-exemplar.live.spec.ts (1)
7-9: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDefault evidence path resolves against
process.cwd(), not the test file location.
resolve('../../../target/...')with no base resolves relative to the process's working directory. If Playwright is invoked from a different directory than expected, evidence artifacts land somewhere unexpected instead of failing loudly.♻️ Suggested fix: anchor the default to the file location
-const evidenceDirectory = resolve( - process.env.MESH_PLUGIN_EVIDENCE_DIR ?? '../../../target/plugin-web-ui-evidence/playwright' -) +const evidenceDirectory = resolve( + process.env.MESH_PLUGIN_EVIDENCE_DIR ?? + resolve(new URL('.', import.meta.url).pathname, '../../../target/plugin-web-ui-evidence/playwright') +)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/mesh-llm-ui/e2e/plugins/web-ui-exemplar.live.spec.ts` around lines 7 - 9, Update the default path in the evidenceDirectory initialization to resolve relative to the test file’s directory rather than process.cwd(), while preserving MESH_PLUGIN_EVIDENCE_DIR as the override. Use the appropriate file-location mechanism available in this module so the default consistently targets the intended plugin-web-ui-evidence/playwright directory.crates/mesh-llm-ui/src/features/plugins/web-ui/exemplar-contract.test.ts (1)
6-6: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winPath resolution depends on invocation cwd.
resolve(process.cwd(), '../../docs/...')only resolves correctly if Vitest's cwd is thecrates/mesh-llm-uipackage directory. If this suite is ever run from the repo root or another workspace runner, the path breaks. Anchor to the test file's own location instead.♻️ Suggested fix using file-relative resolution
-import { readFileSync } from 'node:fs' -import { resolve } from 'node:path' +import { readFileSync } from 'node:fs' +import { dirname, resolve } from 'node:path' +import { fileURLToPath } from 'node:url' import { describe, expect, it } from 'vitest' -const EXEMPLAR_ROOT = resolve(process.cwd(), '../../docs/plugins/exemplars/web-ui') +const EXEMPLAR_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '../../../../../../docs/plugins/exemplars/web-ui')🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/mesh-llm-ui/src/features/plugins/web-ui/exemplar-contract.test.ts` at line 6, Update the EXEMPLAR_ROOT definition in exemplar-contract.test.ts to resolve the documentation path relative to the test module’s own location rather than process.cwd(). Preserve the existing target directory while using the module URL or equivalent file-location anchor so the suite works from any invocation directory.docs/plugins/exemplars/web-ui/bundle/host-contract.d.ts (1)
104-128: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winExtract the shared registration-result shape to remove duplication.
The sync and
Promise<...>-wrapped return types repeat the identical{ pages; configSections? }structure. Any future field change must be kept in sync manually across both.♻️ Suggested refactor
+export type MeshPluginUiRegistration = { + readonly pages: Readonly<Record<string, (context: { + readonly element: HTMLElement + readonly host: MeshPluginUiHost + readonly page: PluginWebUiPage + }) => MeshPluginUiMountHandle | Promise<MeshPluginUiMountHandle>>> + readonly configSections?: Readonly<Record<string, (context: { + readonly element: HTMLElement + readonly host: MeshPluginUiHost + readonly section: PluginWebUiConfigSection + }) => MeshPluginUiMountHandle | Promise<MeshPluginUiMountHandle>>> +} + export type MeshPluginUiBundleModule = { - readonly registerMeshPluginUi: (host: MeshPluginUiHost) => { - readonly pages: Readonly<Record<string, (context: { - readonly element: HTMLElement - readonly host: MeshPluginUiHost - readonly page: PluginWebUiPage - }) => MeshPluginUiMountHandle | Promise<MeshPluginUiMountHandle>>> - readonly configSections?: Readonly<Record<string, (context: { - readonly element: HTMLElement - readonly host: MeshPluginUiHost - readonly section: PluginWebUiConfigSection - }) => MeshPluginUiMountHandle | Promise<MeshPluginUiMountHandle>>> - } | Promise<{ - readonly pages: Readonly<Record<string, (context: { - readonly element: HTMLElement - readonly host: MeshPluginUiHost - readonly page: PluginWebUiPage - }) => MeshPluginUiMountHandle | Promise<MeshPluginUiMountHandle>>> - readonly configSections?: Readonly<Record<string, (context: { - readonly element: HTMLElement - readonly host: MeshPluginUiHost - readonly section: PluginWebUiConfigSection - }) => MeshPluginUiMountHandle | Promise<MeshPluginUiMountHandle>>> - }> + readonly registerMeshPluginUi: (host: MeshPluginUiHost) => + MeshPluginUiRegistration | Promise<MeshPluginUiRegistration> }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@docs/plugins/exemplars/web-ui/bundle/host-contract.d.ts` around lines 104 - 128, Extract the duplicated `{ pages; configSections? }` registration-result structure from MeshPluginUiBundleModule.registerMeshPluginUi into a shared type alias, then use that alias for both the synchronous return and Promise-wrapped return types. Keep the existing field definitions and optionality unchanged.crates/mesh-llm-host-runtime/src/protocol/mod.rs (1)
2046-2080: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick winExercise
web_ui_enabledin this round-trip test.Using
Nonecannot detect the setting being dropped during config synchronization. Set it toSome(false)and assert the restored value.Proposed regression coverage
- web_ui_enabled: None, + web_ui_enabled: Some(false), ... assert_eq!(restored.plugins[0].enabled, Some(true)); + assert_eq!(restored.plugins[0].web_ui_enabled, Some(false));🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/mesh-llm-host-runtime/src/protocol/mod.rs` around lines 2046 - 2080, Update the round-trip test’s demo PluginConfigEntry to set web_ui_enabled to Some(false), then add an assertion on restored.plugins[0].web_ui_enabled confirming Some(false). Keep the existing plugin round-trip assertions unchanged.crates/mesh-llm-plugin-manager/src/lib.rs (1)
16-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winKeep the new APIs under their owning modules.
These re-exports unnecessarily expand the crate-root compatibility surface. Import
install_plugin_archivethroughinstalland the metadata types throughstoreunless these are intentional compatibility shims.As per coding guidelines,
crates/*/src/lib.rsshould minimize crate-root re-exports and new code should prefer importing from the owning module directly.Also applies to: 34-38
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/mesh-llm-plugin-manager/src/lib.rs` at line 16, Remove the new crate-root re-exports for install and metadata APIs from lib.rs, including install_plugin_archive and the types referenced by the additional lines. Update consumers to import install_plugin_archive through the install module and metadata types through the store module, leaving only intentional existing compatibility re-exports.Source: Coding guidelines
crates/mesh-llm-ui/src/features/configuration/components/PluginConfigSectionMount.tsx (1)
169-200: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueAd-hoc status banners instead of shared UI primitives.
The loading/error/mutation banners are built from raw
<div>s with manual tone-class toggling, duplicating a pattern already centralized elsewhere in this PR viaStatusBadge. Consider extending an existingsrc/components/ui/primitive (e.g., an Alert/Banner variant) instead of hand-rolling the tone/role logic here.As per coding guidelines, "For changes in
crates/mesh-llm-ui/, use components and compose interfaces consistently with shadcn/ui patterns. Prefer extending existing primitives insrc/components/ui/over ad-hoc markup."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/mesh-llm-ui/src/features/configuration/components/PluginConfigSectionMount.tsx` around lines 169 - 200, Replace the ad-hoc loading, mount-error, and mutation status banner divs in PluginConfigSectionMount with the existing shared status primitive, preferably StatusBadge or an appropriate Alert/Banner component. Extend the primitive only if needed to support the required loading, success, and error tones while preserving the current messages and accessibility roles.Source: Coding guidelines
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/mesh-llm-host-runtime/src/api/routes/plugins.rs`:
- Around line 82-138: Update classify_plugin_route and the route predicates so
host-owned and stapled grammars are disjoint: parse the immediate namespace
after the plugin name once, reserve web-ui, and prevent any web-ui path from
falling through to StapledHttp for unsupported methods. Restrict manifest and
tools recognition to exact plugin-level suffixes, so nested stapled paths such
as /http/manifest and /http/tools/... remain StapledHttp while only valid host
routes are handled by host classifiers.
- Around line 942-1050: Split
plugin_web_ui_api_rejects_failure_paths_without_stapled_fallback into focused
tests covering metadata, assets, toggle behavior, and rejection cases, keeping
each test under the 200-line Clippy limit. Reuse the existing test setup and
preserve the current assertions; leave
plugin_web_ui_api_serves_metadata_toggle_assets_and_updated_summary unchanged.
In `@crates/mesh-llm-host-runtime/src/api/routes/plugins/web_ui.rs`:
- Around line 115-153: Remove synchronous filesystem I/O from the async web UI
handlers: update serve_ready_asset to use async file reads, change
canonical_asset_path to use tokio::fs::canonicalize or spawn_blocking, and
update installed_config_schema_json to avoid synchronous
PluginStore::load_optional on each config request. Preserve the existing
path-safety checks and response behavior while ensuring all filesystem work does
not block the executor.
- Around line 42-78: Make handle_enabled update persistent and runtime Web UI
state atomically: ensure failure of plugin_manager.set_web_ui_enabled does not
leave capture_node.set_plugin_web_ui_enabled committed, by using an existing
transactional/rollback mechanism or a single combined update path. Preserve the
current validation and response behavior while preventing stored and cached
enabled values from diverging.
In `@crates/mesh-llm-host-runtime/src/mesh/mod.rs`:
- Around line 8480-8641: Extract the Node methods set_plugin_web_ui_enabled,
plugin_settings, and patch_plugin_settings from mesh/mod.rs into a new plugin
configuration module such as plugin/config.rs. Define the corresponding impl
Node extension there, register the module through plugin/mod.rs, and preserve
each method’s existing behavior and visibility while keeping mesh/mod.rs focused
on peer membership responsibilities.
In `@crates/mesh-llm-host-runtime/src/plugin/mod.rs`:
- Around line 185-242: Extract the Web UI state model and its related derivation
helpers from `plugin/mod.rs` into the owning `plugin/web_ui.rs` module. Move the
`PluginWebUiState`, `PluginWebUiStateKind`, manifest/page/config overview types,
associated helper logic, and focused tests together, then update imports and
module visibility so existing callers retain the same behavior and APIs.
- Around line 1845-1874: Validate the installed web UI asset directory before
exposing it: in crates/mesh-llm-host-runtime/src/plugin/mod.rs lines 1845-1874,
resolve metadata.web_ui_asset_root_path() once, retain it only when is_dir() is
true, and derive invalid_reason and asset_base_url from that checked path. Apply
the same is_dir() filtering in
crates/mesh-llm-host-runtime/src/plugin/runtime.rs lines 884-888 before
returning the child/API asset root.
In `@crates/mesh-llm-host-runtime/src/plugin/runtime.rs`:
- Around line 1048-1053: Update the test child-process setup in
mark_plugin_running to avoid the Unix-only sleep command. Use platform-specific
cfg(unix)/cfg(windows) commands or the repository’s existing cross-platform
test-process helper, while preserving kill_on_drop and the lifecycle test
behavior.
In `@crates/mesh-llm-plugin-manager/src/archive.rs`:
- Around line 174-195: Update validate_web_ui_entry_scripts to canonicalize
asset_root and each resolved entry-script path, rejecting any script whose
canonical path is not contained within the canonical bundle root before
accepting it as a file. Preserve the existing missing-script error behavior
where applicable, and add a regression test covering an entry_script symlink
that targets a file outside the bundle root.
In `@crates/mesh-llm-plugin-manager/src/install.rs`:
- Line 425: Remove the #[allow(clippy::too_many_lines)] attribute from the
affected test and split its body into named helper functions. Extract the
packaged-manifest fixture setup and the Web UI assertions into separate helpers,
then keep the test focused on orchestration while preserving all existing
assertions and behavior.
In `@crates/mesh-llm-plugin/src/manifest/web_ui.rs`:
- Around line 359-381: Update validate_relative_path in
crates/mesh-llm-plugin/src/manifest/web_ui.rs#L359-L381 to reject any path
component whose name starts with a dot, matching clean_asset_path; preserve the
existing checks for URLs, absolute paths, empty/current-directory paths, and
parent traversal. No code change is needed in
crates/mesh-llm-host-runtime/src/api/routes/plugins/web_ui.rs#L297-L319, which
is the enforcement reference.
In `@crates/mesh-llm-ui/src/app/layout/RootLayout.tsx`:
- Around line 144-153: Update the active-state comparison in the pluginNavItems
useMemo to compare pathname with the URL-decoded plugin route segments rather
than the URI-encoded string, while keeping href encoded for navigation. Use the
existing item.pluginName and item.pageId values to construct the decoded
comparison path, and preserve the current base-path handling.
In `@crates/mesh-llm-ui/src/features/plugins/web-ui/host-surface.ts`:
- Around line 116-117: Update the fetchPlugin callback in the network
configuration to be async, preserving its existing pluginScopedApiUrl and fetch
arguments so synchronous URL errors become rejected Promises.
In `@crates/mesh-llm-ui/src/features/shell/components/TopNavPluginPages.tsx`:
- Around line 40-44: Update both click handlers in TopNavPluginPages.tsx at
lines 40-44 and 81-85 so event.preventDefault() runs only when onNavigate is
provided, while still invoking onNavigate for plain left-clicks. Without
onNavigate, preserve native link navigation at both the single-item and dropdown
links.
In `@crates/skippy-runtime/src/lib.rs`:
- Around line 5087-5104: Extract the tests surrounding the native log event
assertion from crates/skippy-runtime/src/lib.rs lines 5087-5104 into a
responsibility-focused child test module, preserving their existing behavior and
visibility. Move the protocol tests from
crates/mesh-llm-host-runtime/src/protocol/mod.rs line 2049 into a dedicated
child test module, updating module declarations or imports as needed; both sites
require changes so the parent Rust files remain within the 2,000-line limit.
In `@Justfile`:
- Line 278: Quote every interpolation of mesh_bin in the affected Justfile
recipes, including the usages around lines 523–533, so paths from MESH_LLM_BIN
are passed as a single shell argument and metacharacters are not interpreted.
Preserve the existing command behavior while wrapping each {{ mesh_bin }}
command/path use in double quotes.
- Line 426: Replace the mapfile-based assignment in the clippy batch recipe with
a Bash 3.2-compatible while IFS= read -r loop, preserving the existing
plan-clippy-batches.sh and jq pipeline output in clippy_crates; do not require
Bash 4+.
---
Outside diff comments:
In `@crates/mesh-llm-host-runtime/src/api/server.rs`:
- Around line 165-174: Update the UI route exact-match list in the matches!
expression to include "/plugins", preserving the existing trailing-slash and
other route entries.
In `@crates/mesh-llm-host-runtime/src/runtime_data/mod.rs`:
- Around line 1277-1505: Move the test and its helper functions, including
runtime_data_plugin_reports_are_scoped_by_name_and_endpoint and the
publish/assert helpers, into a separate test module while preserving their
existing coverage and behavior. Keep production code in runtime_data/mod.rs and
import the required runtime-data types and JSON utilities from the parent
module.
---
Nitpick comments:
In `@crates/mesh-llm-host-runtime/src/protocol/mod.rs`:
- Around line 2046-2080: Update the round-trip test’s demo PluginConfigEntry to
set web_ui_enabled to Some(false), then add an assertion on
restored.plugins[0].web_ui_enabled confirming Some(false). Keep the existing
plugin round-trip assertions unchanged.
In `@crates/mesh-llm-plugin-manager/src/lib.rs`:
- Line 16: Remove the new crate-root re-exports for install and metadata APIs
from lib.rs, including install_plugin_archive and the types referenced by the
additional lines. Update consumers to import install_plugin_archive through the
install module and metadata types through the store module, leaving only
intentional existing compatibility re-exports.
In `@crates/mesh-llm-ui/e2e/plugins/web-ui-exemplar.live.spec.ts`:
- Around line 7-9: Update the default path in the evidenceDirectory
initialization to resolve relative to the test file’s directory rather than
process.cwd(), while preserving MESH_PLUGIN_EVIDENCE_DIR as the override. Use
the appropriate file-location mechanism available in this module so the default
consistently targets the intended plugin-web-ui-evidence/playwright directory.
In
`@crates/mesh-llm-ui/src/features/configuration/components/PluginConfigSectionMount.tsx`:
- Around line 169-200: Replace the ad-hoc loading, mount-error, and mutation
status banner divs in PluginConfigSectionMount with the existing shared status
primitive, preferably StatusBadge or an appropriate Alert/Banner component.
Extend the primitive only if needed to support the required loading, success,
and error tones while preserving the current messages and accessibility roles.
In `@crates/mesh-llm-ui/src/features/plugins/web-ui/exemplar-contract.test.ts`:
- Line 6: Update the EXEMPLAR_ROOT definition in exemplar-contract.test.ts to
resolve the documentation path relative to the test module’s own location rather
than process.cwd(). Preserve the existing target directory while using the
module URL or equivalent file-location anchor so the suite works from any
invocation directory.
In `@docs/plugins/exemplars/web-ui/bundle/host-contract.d.ts`:
- Around line 104-128: Extract the duplicated `{ pages; configSections? }`
registration-result structure from MeshPluginUiBundleModule.registerMeshPluginUi
into a shared type alias, then use that alias for both the synchronous return
and Promise-wrapped return types. Keep the existing field definitions and
optionality unchanged.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: f2ce691d-1bb9-44ae-a17f-7637f35f654b
⛔ Files ignored due to path filters (5)
docs/plugins/evidence/pr-991/01-plugin-page-ready.pngis excluded by!**/*.pngdocs/plugins/evidence/pr-991/02-plugin-settings-persisted.pngis excluded by!**/*.pngdocs/plugins/evidence/pr-991/03-plugin-schema-setting.pngis excluded by!**/*.pngdocs/plugins/evidence/pr-991/04-plugin-ui-disabled-capability-alive.pngis excluded by!**/*.pngdocs/plugins/exemplars/web-ui/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (98)
.agents/skills/plugin-web-ui-extension/SKILL.mdJustfilecrates/mesh-llm-cli/src/parser.rscrates/mesh-llm-commands/src/plugin.rscrates/mesh-llm-config/README.mdcrates/mesh-llm-config/src/authoring.rscrates/mesh-llm-config/src/lib.rscrates/mesh-llm-config/src/model.rscrates/mesh-llm-config/src/model/built_in_schema.rscrates/mesh-llm-config/src/model/built_in_schema/presentation.rscrates/mesh-llm-config/src/plugin_validation.rscrates/mesh-llm-console-server/src/lib.rscrates/mesh-llm-host-runtime/src/api/routes/plugins.rscrates/mesh-llm-host-runtime/src/api/routes/plugins/web_ui.rscrates/mesh-llm-host-runtime/src/api/server.rscrates/mesh-llm-host-runtime/src/api/tests.rscrates/mesh-llm-host-runtime/src/api/tests/runtime_config_validation_authority.rscrates/mesh-llm-host-runtime/src/config_schema.rscrates/mesh-llm-host-runtime/src/mesh/mod.rscrates/mesh-llm-host-runtime/src/plugin/config.rscrates/mesh-llm-host-runtime/src/plugin/installed.rscrates/mesh-llm-host-runtime/src/plugin/mod.rscrates/mesh-llm-host-runtime/src/plugin/runtime.rscrates/mesh-llm-host-runtime/src/plugin/schema_validation.rscrates/mesh-llm-host-runtime/src/protocol/convert.rscrates/mesh-llm-host-runtime/src/protocol/mod.rscrates/mesh-llm-host-runtime/src/runtime/config_state.rscrates/mesh-llm-host-runtime/src/runtime/mod.rscrates/mesh-llm-host-runtime/src/runtime_data/mod.rscrates/mesh-llm-plugin-manager/README.mdcrates/mesh-llm-plugin-manager/src/archive.rscrates/mesh-llm-plugin-manager/src/install.rscrates/mesh-llm-plugin-manager/src/lib.rscrates/mesh-llm-plugin-manager/src/skills.rscrates/mesh-llm-plugin-manager/src/store.rscrates/mesh-llm-plugin/README.mdcrates/mesh-llm-plugin/proto/plugin.protocrates/mesh-llm-plugin/src/dsl.rscrates/mesh-llm-plugin/src/lib.rscrates/mesh-llm-plugin/src/manifest.rscrates/mesh-llm-plugin/src/manifest/exemplar_tests.rscrates/mesh-llm-plugin/src/manifest/web_ui.rscrates/mesh-llm-plugin/src/manifest/web_ui/tests.rscrates/mesh-llm-ui/e2e/plugins/web-ui-exemplar.live.spec.tscrates/mesh-llm-ui/src/app/layout/RootLayout.test.tsxcrates/mesh-llm-ui/src/app/layout/RootLayout.tsxcrates/mesh-llm-ui/src/app/router/router.test.tsxcrates/mesh-llm-ui/src/app/router/router.tsxcrates/mesh-llm-ui/src/features/app-shell/lib/status-types.test.tscrates/mesh-llm-ui/src/features/app-shell/lib/status-types.tscrates/mesh-llm-ui/src/features/configuration/api/use-config-query.tscrates/mesh-llm-ui/src/features/configuration/components/DefaultsTab.tsxcrates/mesh-llm-ui/src/features/configuration/components/PluginConfigSectionMount.tsxcrates/mesh-llm-ui/src/features/configuration/components/PluginIntegrationsPanel.tsxcrates/mesh-llm-ui/src/features/configuration/pages/ConfigurationPage.test.tsxcrates/mesh-llm-ui/src/features/configuration/pages/ConfigurationPage.tsxcrates/mesh-llm-ui/src/features/developer/pages/DeveloperPlaygroundPage.test.tsxcrates/mesh-llm-ui/src/features/plugins/api/plugin-web-ui.test.tsxcrates/mesh-llm-ui/src/features/plugins/api/plugin-web-ui.tscrates/mesh-llm-ui/src/features/plugins/web-ui/PluginWebUiRoutePage.tsxcrates/mesh-llm-ui/src/features/plugins/web-ui/bundle-loader.test.tscrates/mesh-llm-ui/src/features/plugins/web-ui/bundle-loader.tscrates/mesh-llm-ui/src/features/plugins/web-ui/exemplar-contract.test.tscrates/mesh-llm-ui/src/features/plugins/web-ui/host-contract.tscrates/mesh-llm-ui/src/features/plugins/web-ui/host-surface.test.tscrates/mesh-llm-ui/src/features/plugins/web-ui/host-surface.tscrates/mesh-llm-ui/src/features/shell/components/TopNav.test.tsxcrates/mesh-llm-ui/src/features/shell/components/TopNav.tsxcrates/mesh-llm-ui/src/features/shell/components/TopNavPluginPages.tsxcrates/mesh-llm-ui/src/lib/api/plugin-types.tscrates/mesh-llm-ui/src/lib/feature-flags/definitions.tscrates/mesh-llm-ui/src/lib/query/query-keys.tscrates/mesh-llm/src/commands/plugin_cli.rscrates/skippy-runtime/src/lib.rsdocs/CLI.mddocs/README.mddocs/USAGE.mddocs/plugins/PLAN.mddocs/plugins/README.mddocs/plugins/evidence/pr-991/README.mddocs/plugins/evidence/pr-991/live-validation.jsondocs/plugins/exemplars/web-ui/Cargo.tomldocs/plugins/exemplars/web-ui/README.mddocs/plugins/exemplars/web-ui/bundle/host-contract.d.tsdocs/plugins/exemplars/web-ui/bundle/register-mesh-plugin-ui.jsdocs/plugins/exemplars/web-ui/bundle/register-mesh-plugin-ui.tsdocs/plugins/exemplars/web-ui/config.tomldocs/plugins/exemplars/web-ui/lifecycle-states.jsondocs/plugins/exemplars/web-ui/manifest.rsdocs/plugins/exemplars/web-ui/plugin.package.jsondocs/plugins/exemplars/web-ui/plugin.tomldocs/plugins/exemplars/web-ui/src/main.rswebsite/src/docs/pages/CLI.mdwebsite/src/docs/pages/config-models.mdwebsite/src/docs/pages/developing-plugins.mdwebsite/src/docs/pages/plugin-architecture.mdwebsite/src/docs/pages/plugin-reference.mdwebsite/src/docs/pages/plugins.md
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
crates/mesh-llm-host-runtime/src/plugin/web_ui.rs (2)
275-296: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueConstruct
PluginWebUiDeclarationdirectly.There is no need to instantiate an intermediate
PluginWebUiManifestOverviewstruct only to immediately move itspagesandconfig_sectionsfields intoPluginWebUiDeclaration. You can map and collect the iterators directly into thePluginWebUiDeclarationfields to simplify the function.♻️ Proposed refactor
fn plugin_web_ui_declaration_from_proto( web_ui: &proto::PluginWebUiManifest, ) -> PluginWebUiDeclaration { - let overview = PluginWebUiManifestOverview { - pages: web_ui - .pages - .iter() - .map(plugin_web_ui_page_from_proto) - .collect(), - config_sections: web_ui - .config_sections - .iter() - .map(plugin_web_ui_config_section_from_proto) - .collect(), - }; PluginWebUiDeclaration { - pages: overview.pages, - config_sections: overview.config_sections, + pages: web_ui + .pages + .iter() + .map(plugin_web_ui_page_from_proto) + .collect(), + config_sections: web_ui + .config_sections + .iter() + .map(plugin_web_ui_config_section_from_proto) + .collect(), asset_base_url: None, invalid_reason: Some("web UI bundle metadata is unavailable".into()), } }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/mesh-llm-host-runtime/src/plugin/web_ui.rs` around lines 275 - 296, Update plugin_web_ui_declaration_from_proto to construct PluginWebUiDeclaration directly, mapping and collecting web_ui.pages and web_ui.config_sections into the corresponding fields. Remove the intermediate PluginWebUiManifestOverview value while preserving asset_base_url and invalid_reason unchanged.
175-190: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winAvoid allocating a full
PluginConfigEntryjust to evaluate preferences.Instantiating a full
PluginConfigEntry(which allocates aString, aVec, and aBTreeMapon the heap) just to callweb_ui_preferenceis inefficient. Consider extracting the logic ofweb_ui_preferenceinto a standalone helper function or an associated method that only takesweb_ui_enabledanddeclares_web_uias arguments so that a dummy struct isn't required.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/mesh-llm-host-runtime/src/plugin/web_ui.rs` around lines 175 - 190, Refactor plugin_web_ui_preference and PluginConfigEntry::web_ui_preference so preference evaluation uses only web_ui_enabled and declares_web_ui, without constructing a dummy PluginConfigEntry or allocating its String, Vec, and BTreeMap fields. Preserve the existing preference behavior and update callers to use the shared standalone helper or associated method.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@crates/skippy-runtime/src/lib.rs`:
- Around line 4301-4302: Remove the #[path = "native_log.rs"] attribute from the
native_log module declaration inside the inline tests module, leaving a plain
mod native_log; so Rust resolves crates/skippy-runtime/src/tests/native_log.rs
correctly.
---
Nitpick comments:
In `@crates/mesh-llm-host-runtime/src/plugin/web_ui.rs`:
- Around line 275-296: Update plugin_web_ui_declaration_from_proto to construct
PluginWebUiDeclaration directly, mapping and collecting web_ui.pages and
web_ui.config_sections into the corresponding fields. Remove the intermediate
PluginWebUiManifestOverview value while preserving asset_base_url and
invalid_reason unchanged.
- Around line 175-190: Refactor plugin_web_ui_preference and
PluginConfigEntry::web_ui_preference so preference evaluation uses only
web_ui_enabled and declares_web_ui, without constructing a dummy
PluginConfigEntry or allocating its String, Vec, and BTreeMap fields. Preserve
the existing preference behavior and update callers to use the shared standalone
helper or associated method.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro Plus
Run ID: 70abc8e0-ae0b-48d3-986f-a65d8909f2aa
📒 Files selected for processing (28)
Justfilecrates/mesh-llm-commands/src/plugin.rscrates/mesh-llm-host-runtime/src/api/routes/plugins.rscrates/mesh-llm-host-runtime/src/api/routes/plugins/web_ui.rscrates/mesh-llm-host-runtime/src/api/server.rscrates/mesh-llm-host-runtime/src/mesh/mod.rscrates/mesh-llm-host-runtime/src/mesh/plugin_config.rscrates/mesh-llm-host-runtime/src/plugin/mod.rscrates/mesh-llm-host-runtime/src/plugin/runtime.rscrates/mesh-llm-host-runtime/src/plugin/web_ui.rscrates/mesh-llm-host-runtime/src/protocol/config_tests.rscrates/mesh-llm-host-runtime/src/protocol/mod.rscrates/mesh-llm-host-runtime/src/runtime_data/mod.rscrates/mesh-llm-host-runtime/src/runtime_data/plugin_tests.rscrates/mesh-llm-plugin-manager/src/archive.rscrates/mesh-llm-plugin-manager/src/install.rscrates/mesh-llm-plugin/src/manifest/web_ui.rscrates/mesh-llm-plugin/src/manifest/web_ui/tests.rscrates/mesh-llm-ui/e2e/plugins/web-ui-exemplar.live.spec.tscrates/mesh-llm-ui/src/app/layout/RootLayout.tsxcrates/mesh-llm-ui/src/components/ui/StatusBanner.tsxcrates/mesh-llm-ui/src/features/configuration/components/PluginConfigSectionMount.tsxcrates/mesh-llm-ui/src/features/plugins/web-ui/exemplar-contract.test.tscrates/mesh-llm-ui/src/features/plugins/web-ui/host-surface.tscrates/mesh-llm-ui/src/features/shell/components/TopNavPluginPages.tsxcrates/skippy-runtime/src/lib.rscrates/skippy-runtime/src/tests/native_log.rsdocs/plugins/exemplars/web-ui/bundle/host-contract.d.ts
🚧 Files skipped from review as they are similar to previous changes (17)
- crates/mesh-llm-host-runtime/src/api/server.rs
- crates/mesh-llm-ui/src/features/plugins/web-ui/exemplar-contract.test.ts
- crates/mesh-llm-ui/src/features/plugins/web-ui/host-surface.ts
- docs/plugins/exemplars/web-ui/bundle/host-contract.d.ts
- crates/mesh-llm-ui/src/features/shell/components/TopNavPluginPages.tsx
- crates/mesh-llm-ui/src/features/configuration/components/PluginConfigSectionMount.tsx
- crates/mesh-llm-ui/src/app/layout/RootLayout.tsx
- crates/mesh-llm-plugin/src/manifest/web_ui/tests.rs
- crates/mesh-llm-ui/e2e/plugins/web-ui-exemplar.live.spec.ts
- crates/mesh-llm-commands/src/plugin.rs
- crates/mesh-llm-host-runtime/src/api/routes/plugins.rs
- crates/mesh-llm-plugin-manager/src/archive.rs
- crates/mesh-llm-host-runtime/src/plugin/runtime.rs
- crates/mesh-llm-plugin/src/manifest/web_ui.rs
- crates/mesh-llm-host-runtime/src/api/routes/plugins/web_ui.rs
- Justfile
- crates/mesh-llm-plugin-manager/src/install.rs
|
@ndizazzo can we discuss over weekend? I want to know a bit more about how consumers use this. Did something like it with Jenkins Blue Ocean back in the day and I failed miserably at it |
Sure but before we do, pick through the exemplar as homework! Keeping in mind: The main drivers behind this are hooks that plugins get sent from the runtime, and a DSL-like catalog definition that declares what the plugin provides (versioned). The main goal is to allow us to build some of the "reserved capacity" work as a plugin rather than integrated with the core app. I think this is ideal for us because we're not blurring the lines between applications of mesh, vs. the specific use cases on deployment. |
Summary
Adds a release-ready, manifest-driven plugin web UI extension surface across the author API, packaging, installer, runtime API, React console, documentation, and test gates.
plugin!declaration carry handlers, config schema, capabilities, and web UI metadata.tar.gz/.zipinstalls with exact CLI help and release metadataProduction hardening
unmount()handles before mounting third-party bundle codeCache-Control: no-cache422for schema-invalid plugin settings and keeps non-UI capabilities alive when web UI is disabled or invalid/plugins/<plugin>/<page>navigation in embedded and standalone console serversTest gate coverage
just test-allexplicitly covers plugin, plugin-manager, config, CLI, commands, host, and console tests; standalone exemplar manifest/JavaScript/TypeScript validation; UI lint/typecheck/unit tests; production UI and website builds; and fail-closed Playwright smoke startup.Final full gate result:
33, plugin-manager31, config95, CLI69, commands187, host1,579, console3, and shipped-binary/integration tests passed110files passed,942tests passed,3skipped2passed,1responsive-only case skipped by its existing conditionFocused validation also passed for host schema export/config round-trips, the standalone exemplar release build and generated manifest, TypeScript typechecking, ESLint, and the hosted plugin lifecycle Playwright test.
Live plugin evidence
The maintained exemplar was built, packaged, installed through the local archive boundary, and started with the documented
just mesh-clientaction. A fully hosted Chromium run proved:422One independent code-blind validator also authored a different plugin,
release-lantern-714, using only public plugin documentation. It compiled, packaged, installed, ran, registered settings, rendered its page and config section, persistedlantern_level, rejected invalid values, completed MCP initialize/list/call, retained MCP after disabling its UI, and cleaned all processes, ports, profiles, stores, archives, and source.Dark-mode hosted-console proof
Generated from commit
5d753e5bbd95against the real console athttp://127.0.0.1:13131.just buildembedded the production Vite bundle intarget/debug/mesh-llm; the installed exemplar and console/backend were started withjust mesh-client. Browser-observed requests and status codes are recorded inlive-validation.json.Direct navigation and interactive plugin page
Topmost plugin settings banner and persisted configuration
Standard schema controls
UI projection disabled while the non-UI capability remains live
The repeatable capture recipe and interpretation are in the evidence README.
Base
Rebased on current
mainatc39cb3b06c65; plugin navigation/settings/evidence commit5d753e5bbd95; local/CI gate parity commits73451c84eande8df67e23.Summary by CodeRabbit
web_ui_enabledcontrol to show/hide Web UI projection without changing plugin process behavior.plugins install --archivesupport for local.tar.gz/.zipinstalls with--nameand optional--version.