Commit Graph

108 Commits

Author SHA1 Message Date
ljy-1351 bf6548bc8e fix(plugins): filter .d.mts and .d.cts declaration files in extension discovery (#107994)
* fix(plugins): filter .d.mts and .d.cts declaration files in extension discovery

isExtensionFile only checked for .d.ts but not .d.mts or .d.cts.
path.extname('.d.mts') returns '.mts' so these declaration files
passed through all checks and were treated as valid plugin entries.

Replaced the single .d.ts check with a regex covering all TS
declaration file variants.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix: update d.mts test to use distinct filenames so it fails on old code

---------

Co-authored-by: Claude <noreply@anthropic.com>
2026-07-31 21:48:12 -07:00
Peter Steinberger 3972c41513 fix(plugins): canonical discovery diagnostics and deterministic lifecycles (#117025)
* fix(plugins): preserve canonical diagnostic ownership

* fix(plugins): make discovery owner ordering deterministic

* test(plugins): keep discovery owner regressions lint and type safe

---------

Co-authored-by: Peter Steinberger <steipete@macos.shared>
2026-07-31 15:10:10 -07:00
Peter Steinberger 5bfc65d7f4 refactor: remove 3,543 lines of redundant runtime and tests (#115961)
* refactor: remove 3,543 lines of redundant runtime and tests

* refactor: ratchet production environment variable budget
2026-07-29 11:43:41 -04:00
Peter Steinberger 3fb201c5a7 fix(plugins): reuse gateway lifecycle metadata and isolate plugin state (#114476)
* fix(plugins): reuse gateway metadata and preserve reload isolation

* test(plugins): split lifecycle regressions and simplify gateway dispatch

* perf(plugins): reuse proven immutable snapshot graphs

* test(plugins): isolate snapshot lifecycle regression mocks

* perf(plugins): retain one gateway metadata cache

* test(plugins): preserve frozen proxy descriptor invariants

* test(plugins): isolate lifecycle metadata mock ownership

* test(plugins): bind shared worker mocks before module evaluation
2026-07-27 07:48:13 -04:00
Peter Steinberger cb628df199 fix: recognize configured plugin dependencies by manifest id (#113691)
Co-authored-by: Peter Steinberger <steipete@golden-gate.local>
2026-07-25 07:05:44 -07:00
evan-YM e90a21fbac fix(plugins): enhance plugin API compatibility diagnostic messages with troubleshooting guidance (#94019)
* fix(plugins): enhance plugin API compatibility diagnostic messages with troubleshooting guidance

Improve the diagnostic messages in shouldSkipIncompatiblePackagePluginApi to
include actionable troubleshooting hints when plugin discovery is skipped due
to version mismatches or invalid metadata.

Related to #93908

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(plugins): align compatibility diagnostics

Extend the discovery guidance to the manifest-registry load path and assert the exact remediation text.\n\nCo-authored-by: 杨爱文 <yang.aiwen@xydigit.com>

---------

Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-21 00:25:25 -07:00
Peter Steinberger c7e7ac2728 refactor: remove expired plugin compatibility surfaces (#111451)
* docs(secrets): remove retired web credential paths

* refactor(web): remove retired provider compatibility paths

* refactor(providers): delete retired compatibility routes

* refactor(secrets): remove retired credential aliases

* refactor(plugin-sdk): delete retired compatibility surfaces

* docs(plugin-sdk): remove retired migration guidance

* chore(plugin-sdk): refresh rebased surface budgets

* chore(plugin-sdk): refresh API removal baseline

* refactor(compat): migrate retired internal callers

* chore(plugin-sdk): refresh current-main baselines

* test(config): migrate plugin-owned secret assertions

* test(gateway): narrow plugin secret refs

* fix(plugin-sdk): preserve private boundary type identity

* chore(compat): remove stale sweep references

* chore(lint): lower max-lines budget

* refactor(secrets): remove unused web helper

* build(plugin-sdk): drop removed compat entries

* chore(plugin-sdk): refresh rebased API baseline

* chore(plugin-sdk): use Linux API baseline hash

* fix(plugin-sdk): preserve private bundled build entries

* fix(plugin-sdk): package private runtime facades

* fix(plugins): preserve external credential contracts
2026-07-19 11:04:48 -07:00
Peter Steinberger a876451d8b fix(qa): avoid touching operator state during private QA runs (#111446)
* fix(qa): isolate commands from operator state

* fix(qa): isolate runner discovery from operator state

* fix(qa): keep discovery scope internal
2026-07-19 09:52:21 -07:00
Peter Steinberger f81f9d8570 chore: enforce max-lines suppression ratchet (#107315)
* ci: enforce max-lines suppression ratchet

* chore: prune stale max-lines suppression

* fix: close max-lines ratchet enforcement gaps

* fix: harden max-lines ratchet checks

* fix(ci): satisfy max-lines ratchet checks

* style: format max-lines declarations

* fix(ci): match oxlint suppression grammar

* test: isolate max-lines git fixtures

* chore: prune resolved max-lines debt

* test: skip newline path fixture on Windows

* fix: harden max-lines suppression ratchet

* chore: refresh max-lines baseline

* fix: close max-lines ratchet bypasses

* fix: derive ratchet base from PR merge tree

* fix: support older Git in staged ratchet

* fix: align max-lines declarations and baseline

* chore: refresh max-lines baseline for current main

* fix: exclude generated wizard locales from max-lines

* chore: prune resolved max-lines debt
2026-07-14 09:27:02 -07:00
Peter Steinberger d05391b9b3 fix: prevent Slack startup from loading stale builds (#104831) 2026-07-11 17:51:19 -07:00
Vincent Koc fdc7892a6e refactor: localize internal implementation types (#101731) 2026-07-07 07:41:04 -07:00
Leonidas Lux 0f01930c8e fix(plugins): discover config load.paths plugins when index has entries (#99196)
* fix(plugins): discover config load.paths plugins when index has entries

Installing a managed npm plugin populates the persisted plugin index.
Once the index has entries (index.plugins.length > 0),
loadPluginManifestRegistryForInstalledIndex is used instead of the
full discovery path. This path only processes plugins that already
exist in the index — if workspace plugins from config plugins.load.paths
are missing from the index, they are silently dropped from the manifest
registry, producing 'plugin not found (stale config entry ignored)'
warnings on gateway startup and in CLI commands.

Fix: after converting index records to plugin candidates, also
discover plugins from config plugins.load.paths and merge any
candidates that are not already represented in the index-based
list. This ensures config-origin workspace plugins are always
included in the manifest registry regardless of index completeness.

Fixes #99185

* fix(plugins): address clawsweeper review — config-only scope, pluginId scoping, regression tests

- P1: Limit extra discovery scope to config-origin candidates only
  (filter by origin === 'config') instead of full discoverOpenClawPlugins
- P2: Preserve pluginId scoping for load-path candidates
  (filter extra candidates by pluginIdSet when present)
- Add 3 regression tests: load.paths discovery, empty-load no-op,
  pluginId scoping preservation
- Add L2 real behavior proof with direct function call evidence

🦞 diamond lobster: L2 evidence (real function call + objects)

Ref. https://github.com/openclaw/openclaw/pull/99196

* fix(plugins): replace discoverOpenClawPlugins with config-only discoverFromConfigPaths

P1: The installed-index fallback previously called discoverOpenClawPlugins,
which scans bundled/global/workspace roots and returns their diagnostics.
This allowed unrelated shared-root diagnostics to leak into the
installed-index config validation surface.

Replace it with discoverFromConfigPaths — a new helper that scans only
the user-configured plugins.load.paths entries via discoverFromPath
directly, producing zero bundled/global candidates or diagnostics.

P2: pluginId scoping is preserved for load-path candidates.

Added discoverFromConfigPaths to src/plugins/discovery.ts to keep the
config-only discovery path reusable and explicit.

🦞 diamond lobster: L2 evidence (terminal output from real dev build)

Ref. https://github.com/openclaw/openclaw/pull/99196

* fix(plugins): preserve bundled-load-path alias filtering in discoverFromConfigPaths

P2: The new discoverFromConfigPaths helper (added in Round 1 to fix the
P1 full-discovery issue) bypassed normal discovery's bundled-load-path
alias guard. If a user had a bundled plugin directory in their
plugins.load.paths, the installed-index fallback would treat it as a
valid config-origin candidate and potentially override/duplicate bundled
plugins.

Add resolvePackagedBundledLoadPathAlias check in discoverFromConfigPaths
before calling discoverFromPath, so bundled paths are skipped with a
warning diagnostic matching normal discovery behavior.

Add P2 regression test verifying bundled plugin load paths are ignored
on the installed-index path while real workspace load paths still work.

Ref. https://github.com/openclaw/openclaw/pull/99196

* fix(plugins): forward load-path diagnostics unconditionally in installed-index path

Previously, diagnostics from discoverFromConfigPaths were only forwarded
when at least one extra candidate survived filtering. Configs whose
plugins.load.paths contained only bundled aliases, missing paths, duplicates
already in the index, or scoped-out entries silently lost the normal
discovery warning or doctor hint.

Now extraDiagnostics is assigned outside the extraCandidates.length > 0
guard, so every config load-path diagnostic reaches the caller regardless
of whether a new candidate is merged.

Adds a focused regression test verifying that a bundled-only load path
still surfaces the expected alias-guard warning diagnostic.

🦞 diamond lobster: P2 fix, no new L2 evidence needed (regression test
covers the diagnostic-forwarding contract directly)

Ref. https://github.com/openclaw/openclaw/pull/99196

* fix(plugins): preserve requiresPlugins diagnostics for load paths in installed-index path

- [P2] Export addMissingRequiredPluginDiagnostics from discovery.ts for reuse
- [P2] Call addMissingRequiredPluginDiagnostics on combined index + load-path candidates before passing to loadPluginManifestRegistry
- [P2] Add regression tests: warns when load-path plugin requires missing plugin, does not false-warn when required plugin is already in index

🦞 diamond lobster: L2 + test coverage

Ref. https://github.com/openclaw/openclaw/pull/99196

* fix(plugins): add missing discovery.js mock exports for addMissingRequiredPluginDiagnostics

plugin-install.test.ts mocks discovery.js but the mock didn't include the
newly exported addMissingRequiredPluginDiagnostics, causing CI failures in
tests that exercise loadPluginManifestRegistryForInstalledIndex.

Also add discoverFromConfigPaths to the mock (already imported but was
only latent because the mock's code path wasn't triggered with load paths).

Ref. https://github.com/openclaw/openclaw/pull/99196

* fix(plugins): rebuild stale configured plugin indexes

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
2026-07-06 02:01:00 -07:00
Vincent Koc 0ab7942956 refactor(plugins): narrow manifest stat cache reads (#87695)
Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-07-05 05:55:39 -07:00
ml12580 1585ec54f1 perf(plugins): cache existence probes within bundle manifest scan [AI-assisted] (#93919)
* perf(plugins): cache existence probes within bundle manifest scan

Bundle plugin discovery re-probes the same marker paths (skills/, commands/,
agents/, .mcp.json, .lsp.json, settings.json, hooks/hooks.json) once in
detectBundleManifestFormat and again in loadBundleManifest's capability
builders. Across the bundled plugin tree this is thousands of redundant
synchronous fs.existsSync calls; #76209 reports 25.4s of self-time on a
Windows cold start.

Add a scan-scoped existence cache (plugin-scan-existence-cache.ts) entered
only around discoverBundleInRoot. pluginScanExistsSync memoizes inside the
active scan and falls back to plain fs.existsSync outside it, so install,
hooks, and doctor flows stay uncached. The cache is push/pop per
discoverBundleInRoot call (try/finally), so a later install/repair pass
re-reads the filesystem — no process-global staleness.

Measured on Windows over a 25-plugin fixture: 550 -> 325 fs.existsSync
calls (41% fewer), 294.75ms -> 208.49ms. Discovery results unchanged.

Closes #76209

* fix(plugins): drop unused test reset helper and satisfy oxlint

Remove __resetPluginScanExistenceCacheForTest: the scan cache is push/pop
balanced by try/finally in withPluginScanExistenceCache, so the stack never
leaks between tests and the helper was dead code. It also tripped oxlint
no-underscore-dangle. Refactor the integration test to count existsSync calls
via a const-returning helper so there is no useless assignment.
2026-06-22 18:27:36 +00:00
Vincent Koc 34be976c6d refactor(plugins): remove status dependency barrel 2026-06-18 01:49:46 +08:00
Peter Steinberger f91fab8b18 docs: document plugin manifest helpers 2026-06-04 04:46:27 -04:00
Peter Steinberger b653d94918 chore(lint): enable no-useless-assignment 2026-05-31 22:40:48 +01:00
Peter Steinberger b9fe0894a6 chore(lint): enable additional cleanup rules 2026-05-31 08:16:11 +01:00
Peter Steinberger 00d8d7ead0 refactor: extract normalization core package
Extract shared normalization/coercion helpers into private @openclaw/normalization-core workspace package while preserving existing plugin SDK helper subpaths.\n\nAlso keeps direct normalization-core imports internal, wires UI/build/loader resolution, and replaces the slow PR network CodeQL lane with a fast added-line boundary scan while retaining full CodeQL for scheduled/manual runs.\n\nVerification: local moved tests, plugin SDK boundary tests, extension loader tests, agents-support shard, UI build/test, build artifacts, lint, workflow guards, autoreview, and GitHub CI passed on PR head 963d893715.
2026-05-31 01:33:00 +01:00
mushuiyu_xydt f93a558892 fix(plugins): ignore helper files in extension roots
Fixes #88198.

Ignore top-level helper scripts in auto-discovered global/workspace extension roots so they do not become manifestless plugin candidates during config validation. Standalone plugin files remain supported when explicitly configured through `plugins.load.paths`, and docs now call out the supported path.

Verification:
- `node scripts/run-vitest.mjs src/plugins/discovery.test.ts src/config/config.plugin-validation.test.ts`
- `node scripts/run-oxlint.mjs src/plugins/discovery.ts src/plugins/discovery.test.ts src/config/config.plugin-validation.test.ts`
- `git diff --check`
- GitHub CI green at `93073bfa85ee294e644c623881ba59ba71d90975`
- `.agents/skills/autoreview/scripts/autoreview --mode branch --base origin/main`

Thanks @mushuiyu886 for the fix and @mmhzlrj for the report.
2026-05-30 17:31:53 +01:00
Rohit 376b03f8ea fix(plugins): reject incompatible package plugin API installs (#87477)
* fix(plugins): enforce package plugin API compatibility

* fix(plugins): preserve plugin API prerelease floors

* fix(plugins): gate persisted plugin api compatibility

* fix(plugins): skip incompatible package discovery

* fix(plugins): check api compatibility before package shape

* fix(plugins): gate bundle package api compatibility

* docs(plugins): clarify plugin API release sync

* test(agents): keep dynamic live model unit test runtime-free

* fix(plugins): normalize correction plugin api floors

* test(agents): align dynamic normalizer expectation

* fix(plugins): reject malformed plugin api metadata

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
2026-05-28 20:25:30 +01:00
Peter Steinberger 68e6f03fd9 perf: reduce gateway runtime discovery overhead 2026-05-28 15:47:50 +01:00
Peter Steinberger b58786ce9f perf: reduce agent turn CPU overhead 2026-05-28 14:59:09 +01:00
Dallin Romney d638611684 feat: split diffs language pack
Split the diffs viewer Shiki language pack into an external publishable plugin.

The diffs plugin keeps the default curated syntax set, while the new @openclaw/diffs-language-pack package carries the extended Shiki languages for npm and ClawHub distribution. The install metadata includes the external ClawHub spec, and the curated C# alias set keeps both c# and cs supported without the language pack.

Co-authored-by: Dallin Romney <dallinromney@gmail.com>
2026-05-27 18:08:40 +01:00
Vincent Koc 793e300cc5 fix(plugins): support linked source checkouts on Windows 2026-05-25 03:36:06 +02:00
Peter Steinberger 12f82270cf perf: cache stable gateway metadata 2026-05-24 02:54:28 +01:00
Julyan 6f933656e5 fix: strip -plugin suffix in deriveIdHint to match manifest ids (#85170)
The deriveIdHint function already strips -provider from unscoped
package names (@openclaw/anthropic-provider -> anthropic) but does
not strip -plugin (@openclaw/xai-plugin -> xai-plugin instead of
xai). This causes ~30 spurious 'plugin id mismatch' warnings on
gateway startup for built-in plugins whose package names end in
-plugin.

Closes #85048
2026-05-22 09:58:56 +01:00
Dallin Romney 9a6744baba perf(plugins): scan-scoped package.json cache in discovery (#84302)
* perf(plugins): extend discovery threading to loader, manifest registry, installed-index, and config contracts

Follow-up to #75451. Threads optional discovery?: PluginDiscoveryResult
through the remaining helpers that still call discoverOpenClawPlugins
internally during startup:

- loadOpenClawPlugins / loadOpenClawPluginCliRegistry (src/plugins/loader.ts):
  add discovery? to PluginLoadOptions and consult it before falling back to
  an internal scan at both call sites.

- loadPluginManifestRegistry (src/plugins/manifest-registry.ts): accept
  discovery? as a more ergonomic alternative to the existing candidates? /
  diagnostics? pair; candidates? still wins when both are supplied.

- resolveInstalledPluginIndexRegistry (src/plugins/installed-plugin-index-registry.ts):
  add discovery? to LoadInstalledPluginIndexParams and use it when
  candidates aren't supplied.

- resolvePluginConfigContractsById (src/plugins/config-contracts.ts): add
  discovery? and thread it into the bundled-fallback discovery call.

Add discovery-threading.test.ts asserting each entry point skips its
internal discoverOpenClawPlugins call when discovery is supplied, calls it
when nothing is supplied, and prefers explicit candidates over discovery
when both are present (6 tests, all pass).

discoverOpenClawPlugins remains stateless; sharing is function-scoped per
src/plugins/CLAUDE.md guidance. Backward compatible: every change is
additive (new optional param).

* perf(plugins): drop verbose JSDoc from discovery? params

* perf(plugins): scan-scoped package.json cache in discovery

Adds a per-scan Map<string, PackageManifest | null> threaded through
discoverFromPath/discoverInDirectory/readCandidatePackageManifest, keyed
by the directory's resolved real path. Within one discovery scan, a
plugin's package.json is now read from disk once and reused across the
overlapping discovery code paths (bundled overlay scan, stock-root scan,
source-checkout extensions scan, installed-path scan, global-root scan)
that previously each fired their own read.

The cache lifetime is one scan (created in runPluginDiscovery alongside
the existing realpathCache and seen Set, dies when the scan returns).
discoverOpenClawPlugins remains stateless externally; no persistent
metadata cache.

* perf(plugins): expose raw parsed package.json on PluginCandidate

Discovery already reads each plugin's package.json once and produces a
parsed PackageManifest object before distilling it into metadata via
getPackageManifestMetadata. Currently only the distilled metadata is
kept on the candidate; the full parsed manifest is discarded.

Store the full parsed manifest on rawPackageManifest so downstream
consumers iterating candidates can use it instead of re-reading from
disk. This is the candidate-side groundwork for the scenario-C followup
that routes consumers (bundled-plugin-metadata, bundle-* helpers, etc.)
through the cached field; those consumers currently do their own
directory scans and would need to be refactored to iterate
PluginCandidate arrays before they can benefit.

The field is a frozen-at-discovery-time snapshot, same lifetime semantics
as the existing packageManifest / packageName / packageVersion fields on
PluginCandidate. No new staleness window introduced.

* perf(plugins): make package-manifest cache key trust-aware
2026-05-20 04:57:45 -07:00
Vincent Koc d2e0a8231f fix(plugins): reject malformed package entries 2026-05-16 03:44:33 +08:00
Vincent Koc 8f7a3cbff3 fix(plugins): repair legacy npm declaration stubs 2026-05-14 19:17:05 +08:00
Kaspre 5375281974 fix(plugins): load untracked TS source plugins 2026-05-12 07:08:38 +01:00
the sun gif man 954d20ece2 fix: allow Nix store plugin hardlinks (#79344)
Merged via squash.

Prepared head SHA: bf533f8654

Co-authored-by: Codex <noreply@openai.com>
Reviewed-by: @joshp123
2026-05-08 16:59:53 +02:00
Peter Steinberger 8f3a34e2a1 refactor: share fs-safe JSON helpers 2026-05-06 07:40:10 +01:00
Peter Steinberger b85b1c68d1 Refactor file access to use fs-safe primitives (#78255)
* refactor: use fs-safe primitives across file access

* fix: preserve invalid managed npm manifests

* fix: keep fs seams for startup metadata
2026-05-06 05:03:11 +01:00
Peter Steinberger 538605ff44 [codex] Extract filesystem safety primitives (#77918)
* refactor: extract filesystem safety primitives

* refactor: use fs-safe for file access helpers

* refactor: reuse fs-safe for media reads

* refactor: use fs-safe for image reads

* refactor: reuse fs-safe in qqbot media opener

* refactor: reuse fs-safe for local media checks

* refactor: consume cleaner fs-safe api

* refactor: align fs-safe json option names

* fix: preserve fs-safe migration contracts

* refactor: use fs-safe primitive subpaths

* refactor: use grouped fs-safe subpaths

* refactor: align fs-safe api usage

* refactor: adapt private state store api

* chore: refresh proof gate

* refactor: follow fs-safe json api split

* refactor: follow reduced fs-safe surface

* build: default fs-safe python helper off

* fix: preserve fs-safe plugin sdk aliases

* refactor: consolidate fs-safe usage

* refactor: unify fs-safe store usage

* refactor: trim fs-safe temp workspace usage

* refactor: hide low-level fs-safe primitives

* build: use published fs-safe package

* fix: preserve outbound recovery durability after rebase

* chore: refresh pr checks
2026-05-06 02:15:17 +01:00
Vincent Koc 89a15fddaf fix(plugins): ignore invalid managed runtime shadows 2026-05-04 03:17:57 -07:00
Peter Steinberger 2949171fcc perf: reduce gateway startup readiness latency 2026-05-04 05:20:39 +01:00
Peter Steinberger f74e901794 fix: clarify blocked plugin validation 2026-05-03 19:39:30 +01:00
Peter Steinberger 4a3ad3963b feat(plugins): report dependency install state 2026-05-02 19:58:19 +01:00
Peter Steinberger 5551d9fad4 fix: discover source-only plugins in checkouts 2026-05-02 17:33:15 +01:00
pashpashpash 563dca82f4 Add Codex happy path prompt snapshots (#75807)
* Add Codex prompt snapshots

* Fix prompt snapshot scenario catalogs

* Harden prompt snapshot drift check

* Fix CLI compat build export

* fix: keep codex snapshots out of core plugin surface

* fix: harden prompt snapshot ci checks

* fix: accept readonly web search onboarding scopes

* fix: repair plugin sdk package boundary types

* fix: clear prompt snapshot ci regressions

* fix: clear latest main ci checks

* fix: resolve latest main discord helper overlap

* fix: refresh codex dynamic tool snapshots

* fix: align prompt snapshot branch with latest ci

* fix: isolate plugin auto enable tests

* test: refresh prompt dynamic tool snapshots

* fix: stabilize bundled channel auto enable

* fix: clean stale prompt snapshots
2026-05-03 00:59:55 +09:00
Peter Steinberger 04f1fd4d1f fix: harden source checkout plugin dependency handling 2026-05-02 02:43:15 +01:00
Peter Steinberger ed8f50f240 refactor: simplify plugin dependency handling
Simplify plugin installation and runtime loading around package-manager-owned dependencies, with Jiti reserved for local/TS fallback paths.

Also scans npm plugin install roots so hoisted transitive dependencies are covered by dependency denylist and node_modules symlink checks.
2026-05-01 21:32:22 +01:00
Peter Steinberger e27fe55aa8 refactor: simplify plugin cache boundaries 2026-04-29 04:33:15 +01:00
Peter Steinberger 7a5b419843 refactor(plugins): simplify plugin cache boundaries 2026-04-29 03:52:22 +01:00
Shakker a48ffda7f7 chore: trace plugin lifecycle phases 2026-04-28 18:03:01 +01:00
Peter Steinberger 28f264034b fix: discover symlinked plugin directories 2026-04-27 14:17:32 +01:00
Peter Steinberger 6a0dc3a9bc fix: cache plugin discovery realpaths 2026-04-27 14:09:15 +01:00
Peter Steinberger 74a4ff1adc fix: prefer mounted bundled plugin sources 2026-04-26 11:28:41 +01:00
Peter Steinberger f337c9019c refactor: share plugin package entry resolution 2026-04-26 11:11:58 +01:00