description: "Plugin management over the booted profile: the pluginManager service and the plugins Remote that install, enable, disable, and retry bundles, edit user-layer rows, and report every package's state."
English | 中文
dsh-host-plugin-manager is the one place that changes what a running profile is composed of. The profile launcher composes the host tree from bundle layers and user patch files and, through profileRuntime, can recompose it while it runs; this service drives that: it runs pnpm in the profile directory to install or remove a package, moves a bundle in and out of the profile's layer list and recomposes the tree with the outcome, composes a bundle again when its rows failed at boot, adds and removes rows in the profile's global user layer or one agent preset's, and folds the manifest, the probe record, and the live tree into one view per package. The plugins Remote exposes each operation; every change is followed by a plugins/changed event and an install run streams pnpm's output as plugins/install-log. Client packages consume the Remote through the api-remotes assembly.
Mount the row in a host composition beside the plugin inventory; the web bundle does. The row injects only the Loader and resolves the profile runtime per call, so a composition booted without the profile launcher still starts and answers every call with plugins/unavailable.
plugins/list returns one view per package the profile knows: its template and installed bundles and every other installed dependency. A view carries the manifest facts (name, version, title, description, engines.dsh), what the package is (bundle, plugin, or library), who supplied it (builtin or external), when its rows mount (boot or runtime), whether it is installed and enabled, and a folded status: running, partial, or failed for an enabled bundle by how many of its rows are active; disabled for an installed bundle outside the layer list; not-enableable when the probe refused it, with the reason; restart-required when the manifest and the live tree disagree on a profile that applies changes at its next start; plain for a library or plugin module, which is added to a composition rather than enabled. Rows come from the live tree while the bundle is composed — phase, disabled-by, and the recorded failure of an isolated row — and from the probe record otherwise, under the ids their patches declare. addable lists the modules the package declares in dsh.plugins, each with its default config and the probe's verdict.
plugins/install takes a pnpm spec — a registry name, a github: or git URL, a tarball, an absolute path — runs pnpm add in the profile directory, records what pnpm wrote to dependencies, probes every new package in a child process, and leaves new bundles disabled unless enable was asked for. pnpm's output arrives as plugins/install-log chunks carrying the run's jobId; the last chunk carries the exit code. A non-zero exit, a spawn failure, or the timeout fails the call with plugins/install-failed and the tail of the log, and the profile manifest is restored to what it was before the run. A successful pnpm add is not yet an installed plugin: a new package that declares neither a bundle nor a plugin module, or a bundle whose row id a composed layer already owns, is removed again with pnpm remove and listed under removed with the reason; a package the probe refused stays installed and the view reports why it cannot be enabled. The manager runs one mutation at a time — a second call while one runs fails with plugins/busy naming the operation in flight — and refuses to change node_modules while a session is running, with plugins/agents-running.
plugins/enable puts an installed bundle into the layer list and, on a live profile, recomposes the tree with it through the profile runtime. The recomposition is the Loader's own transaction: a bundle the tree rejects — a boot-stage bundle whose row throws — rolls back, the layer list is restored, and the call fails with plugins/enable-failed naming the reason, while the tree that was running keeps running. A runtime-stage bundle whose row fails is isolated instead: the call succeeds, the view reports the row's failure, and plugins/retry composes the bundle again from scratch. plugins/disable is the reverse; a template bundle, which is not a dependency, cannot be disabled. On a profile whose patchReload is startup, both write the manifest and report effect: 'restart'.
plugins/uninstall disables the bundle when enabled, drops every user-layer row that names one of the package's modules, runs pnpm remove, and forgets the probe record. Like install, it waits for running sessions: plugins/agents-running while any agent is running.
plugins/addRow inserts a row naming one of the package's modules — its main export for a plugin package, or a dsh.plugins entry — into the profile's global cordis.patch.yml (target: { kind: 'global' }) or an agent preset's user layer ({ kind: 'preset', preset }, through the roster's overlayPathFor). The row id derives from the package name and subpath unless given; a taken id fails with plugins/row-conflict. plugins/removeRow removes an inserted row and plugins/setRowDisabled writes or removes a disabled: true for any row — deny-only, so a bundle's own !!js gate is restored rather than overridden. The global layer is recomposed live on the spot; a preset's layer reaches its next standing generation.
plugins/dependents says what disabling or removing a package would strand: services its rows provide that rows outside it inject, and user-layer rows naming its modules.
| Field | Default | Meaning |
|---|---|---|
pnpmCommand |
pnpm |
The executable, resolved through PATH like the dsh plugin command. |
installTimeoutMs |
600000 |
Bound on one install or remove run. |
probeTimeoutMs |
20000 |
Bound on one package probe. |
installLogTailBytes |
16384 |
How much trailing output an install failure reports. |
Read these when the manager's contract is not enough: the runtime it drives, the files it edits, and the surface that renders it.
None, as the host-side plugin manager registers nothing model-facing; the rows it composes own every registration they make.
None; this package neither assembles nor sends a provider request.
These limits define what the manager will not do for a client. They are current package constraints, not a task backlog.
pnpm update through install rewrites the files but the running tree keeps the old modules until the process restarts.inject edge, so dependents cannot name a row that only reads what the package registered.engines.dsh check yet — the range is reported, not enforced against the running harness version.