Commit Graph
25 Commits
Author SHA1 Message Date
_Kerman 9005026ea6 fix(plugin-manager): stream the install diagnostics a failed run already holds
A failed installation could show the generic sentence while its details
panel stayed empty. A pnpm run that printed nothing had its diagnostic
written to the complete log and the result but never streamed, and the
GitHub connection check that stops an installation before pnpm ran kept
its captured output only in the result. Both now reach the install-log
stream the dialog renders: the no-output diagnostic streams as the run's
output, and a network or timeout check failure streams the exact
git ls-remote command line and its output as the installation's only
run under the caller's request id, with no exit code when the check
timed out. A check failure pnpm may resolve streams nothing.
2026-10-28 14:08:30 +08:00
creatixchu 82bbf85894 fix(plugins): report unsupported bundle activation and address review 2026-10-23 13:15:05 +08:00
creatixchu 2e0cc478e8 Merge master into bundle Host platform support 2026-10-23 13:12:09 +08:00
Turtle e0e37bfd3d refactor(boot): move official bundle lists out of app-boot
Profile templates and the default, optional, on-demand, retired, and
installation-owned bundle lists move to the new data-only package
@deepseek-ai/dsh-official-bundles, so editing a bundle list no longer
touches app-boot.
2026-10-22 11:56:47 +08:00
creatixchu c7b6fba276 feat(plugins): enforce bundle Host platform support 2026-10-20 15:28:55 +08:00
turtle1999 b10bd84dfe feat(plugins): check and install newer registry versions of third-party bundles 2026-10-19 15:40:51 +08:00
turtle1999 3de3a977e0 feat(plugin-market): add the Custom plugin markets bundle and Discover tab
Read configured plugin market indexes on the Host, search them with fuzzy
multilingual matching, and install a chosen version from the Plugins page.

- dsh-plugin-market-index validates format-1 indexes and builds install
  arguments; inline icons are capped at 32 KiB.
- The pluginMarket service fetches configured http(s) URLs, parses bodies in
  a Worker Thread, caches documents, and pages filtered, ordered results with
  installation status and engines.dsh compatibility.
- Discover is an opt-in experimental bundle; the Plugins page gains a
  plugins.tab slot and confirmed installs.
- The plugin_market tool lists indexes, plugins, and versions for Creator mode.
- Plugin compatibility reads engines.dsh before DSH peer dependencies.
2026-10-15 05:37:44 +08:00
fz 634d7e96dc fix(plugin-manager): close pnpm logs when preparation fails 2026-10-12 13:05:37 +08:00
Turtle b173f3e2a2 revert(plugin): drop preset-scoped profile patches; ship optional tools as global Host rows
Remove the `{ preset, insert }` / `{ preset, id, ...override }` profile patch
operation added in #5773, together with the app-boot compiler and reload drain,
config-editor filtering, plugin-manager preset row inventory and retained-module
removal checks, the read-only preset rows on the Plugins page, and the
agent-preset-registry row identities that served them.

Session search, Ralph, persistent terminals, Codex, and Claude Code bundles now
insert ordinary Host rows whose tools register in the global tool layer, like the
earlier optional bundles. Every preset, including minimal, sees them, and toggling
a bundle updates live Agents without new composition generations.
2026-10-07 20:46:37 +08:00
Tianyi Cui 15ae753dcd fix(plugin): inspect preset contributions before profile activation 2026-10-06 23:48:59 +08:00
Turtle 1bd3df926d fix(plugin-manager): wait for a pnpm run its exited operation left behind
Review found three ways the takeover could let two writers overlap.

- A claimant that read an exited PID could remove the lock of a new
  holder that reused that PID before the claim. The claimant now probes
  the PID again under the claim, after re-reading the record.
- The browser Worker's process shim reports its own PID as absent, so a
  second writer in the Worker took over the first one's live lock. A
  record naming the contender's own process is never taken over.
- Takeover proves only that the holder exited, while a pnpm run it
  started can still be writing the profile. Each profile operation now
  records its pnpm run in .plugin-manager/run.json and removes the record
  when the run ends; an operation that finds a record waits for the run
  within the existing tree bound and otherwise refuses, naming the
  process and the record, without running pnpm.

A failed removal of the exited holder's lock now waits instead of
failing the acquisition, the probe range is stated as int32, and the
tests inject exited PIDs instead of reusing real ones, which Windows
reissues at once.
2026-09-24 19:55:41 +08:00
turtle1999 51d70c5f5c feat(plugins): report incompatible versions as typed refusals
Compatibility checks now yield the refused packages as data instead of a
profile-specific sentence. runProfilePnpm returns them as
PackageResult.incompatible, and the plugin manager refuses with the
incompatible-version code carrying the same records. Each surface renders
them: the CLI prints the exact allow-version command for its profile, the
Web plugin manager words them through its locale dictionary, and app-boot
keeps one neutral English diagnostic for logs and startup stderr.
2026-09-23 19:51:29 +08:00
turtle1999 747c98b0db fix(plugins): deny incompatible bundles and clarify compatibility refusals
A bundle package is not a plugin row, so startup admission never read its
own DSH peers: a bundle installed outside DSH or before a DSH upgrade still
applied its patch layer. loadProfileDirectory now skips such a bundle like an
unreadable one unless the profile exempts that exact pair.

A post-install refusal in a profile without a lockfile reinstalls from the
restored manifest instead of failing a frozen install and leaving the
rejected packages in node_modules.

Compatibility warnings now name the `dsh plugin allow-version` command for
CLI-managed profiles, startup denials name the row id instead of a resolved
module URL, and the CLI no longer reports "pnpm failed" when pnpm never ran.
2026-09-23 19:14:19 +08:00
turtle1999 2c67633990 feat(plugins): enforce DSH peer compatibility with exact exemptions 2026-09-23 18:46:08 +08:00
Turtle ccaa0dc11c fix(plugin-manager): bound pnpm runs so a silent child cannot hold the profile lock (#4982)
* fix(plugin-manager): bound pnpm runs so a silent child cannot hold the profile lock

A pnpm child that prints its completion line and then never exits holds the
profile write lock until the host is restarted, so every other session and host
on that profile fails after its own lock timeout (issue #4981). execa resolves
its promise only once the piped stdio has ended, so a run now completes on the
process's own exit and drains the pipes under a bound instead of awaiting their
end.

A service run whose captured output stays silent for idleTimeoutMs is
terminated, reports timedOut alongside its exit status, and is not retried on
the next registry; a run with inherited descriptors captures nothing and is
left unbounded for its operator.

* test(plugin-manager): type the mocked child and await the drain descendants

The inherited-descriptor fixture no longer needs its legacy `as unknown` cast,
because the mocked child is typed for the fields the operations read; the
retired baseline entry is pruned (verify-no-unknown-casts).

The drain specs wait for the killed descendant to exit before removing its
directory: Windows refuses to delete a directory a live process still holds.

* test(plugin-manager): stop the drain descendant deterministically

The descendant that holds the pipes now ends on a stop file the teardown
writes, and on its own after a minute, so the fixture leaves no process behind
when a kill cannot be delivered. Windows stops it through taskkill, and the
spec waits for the exit before removing the directory it ran in.

* test(plugin-manager): run the drain descendant from its own script file

Windows cannot carry a nested `-e` script through another spawn argument: the
descendant failed to start there, so the fixture asserted on a process that was
already gone. The descendant now runs from a script file the fixture writes.

* test(plugin-manager): cover the bounded drain everywhere and stage the real descendant on POSIX

Windows does not stage a descendant that keeps the inherited pipes open past
the direct child's exit, so the real-process cases run on POSIX only. The
mocked child now models execa's own completion rule — its promise settles only
once both pipes end — which covers the bounded drain on every platform and
still fails without the raw-exit race.

* test(plugin-manager): cover the drain failure path without a child process

An output consumer failure that the bounded drain cuts short is staged with the
mocked child as well, so the per-file coverage gate also holds where the
real-process cases are skipped.

* fix(plugin-manager): terminate the whole tree of a stalled run

A lifecycle script is a child of pnpm, so signalling pnpm alone left an
approved postinstall writing into the profile after the operation reported a
timeout and released the lock. A stalled run now stops its process group (or
Windows process tree) and waits for it to disappear before the caller restores
the files. The cut tail's close is told apart from a reading failure by where it
happened, so a failure from before the cut still surfaces.

* docs(plugin-manager): regenerate the config catalog after the rebase

The English catalog gains `idleTimeoutMs` from the regenerator, and the Chinese
counterpart carries the same interface line so the pair stays consistent.

* fix(plugin-manager): terminate a run's tree through execa, keeping exit cleanup

Spawning a service run with `detached` made execa skip the handler that kills
the child when the manager exits, so a crashed manager left the pnpm tree
running in the profile. `killDescendants` spawns the run as its own process
group while keeping that cleanup, and it addresses the tree on every kill path,
including cancellation. The module now only decides the probe target and waits
for quiescence.
2026-09-23 16:11:30 +08:00
Turtle 654caa4bbd feat(bundle): accept an ordered patch file list in dsh.bundle.patch (#4722)
* feat(bundle): accept an ordered patch file list in dsh.bundle.patch

`dsh.bundle.patch` now accepts `string | string[]`. A list names patch
files applied in order as one bundle layer; each file's relative plugin
paths still resolve beside that file. `bundlePatchPaths` in dsh-app-boot
is the one resolver from a declaration to absolute paths, and every
reader uses it: profile loading, plugin-manager reconcile and bundle
rows, the client-runtime roster, and the workspace, cordis-config, and
product-isolation gates. `ProfileLayer.patchPath` becomes `patchPaths`.

* fix(bundle): keep declared patch files package-relative in the workspace gate

path.join produced backslash entries on Windows, so the expected files
list did not match package.json files. bundlePatchFiles returns the
validated declaration as written; bundlePatchPaths joins it.
2026-09-20 18:20:24 +08:00
Yichen Jiang cb2750019a fix(plugin-manager): read pnpm's own registry, its stdout refusals, and lay failures where they belong
Review of #4539. A refusal pnpm view prints as JSON on stdout was read
as unknown, so no registry was ever asked after a dead one; stderr and
stdout are now read alike, and a lookup killed at its bound is a
network refusal. pnpm's own registry joins the fallback plan only once
pnpm config get registry names npm's own or a configured fallback, so
a company registry in .npmrc is never followed by a public one, and a
registry pnpm already names is asked once. The Host lays each failure
at the registry or the spec's own host, reading the host off error
lines only, and reports it as ChangeResult.failedAt; a stop that lands
between attempts is cancelled, not failed. The package publishes its
registry rule through ./registry so the dialog plans with the Host's
own function, starts from the registry the Host asks first until one
is remembered, tags that one as the default, labels pnpm's own by the
host it names, and words and offers a change of registry from the
Host's attribution. ERR_PNPM_NO_OFFLINE_META is no longer a network
failure.
2026-09-18 17:03:04 +08:00
Yichen Jiang 716e2e2683 feat(plugin-manager): ask configured registries in turn while one is unreachable
Add registry and fallbackRegistries to the manager Config, ask them in
order from inspect and installBundle with --registry, and move on only
for failures another registry can change: unreachable, timed out, or a
copy the registry lacks. A git or tarball spec's own host is never
retried through a registry, and a registry outside the configured set
is asked alone. The lookup runs without pnpm's retries so a dead
registry is reported within inspectTimeoutMs; the install keeps pnpm's
defaults. Results carry the registries asked, the install-state event
names each attempt, and the plugin_manager tool takes a registry.
2026-09-18 15:21:44 +08:00
turtle1999 8c5ae870c2 fix(creator): address plugin workflow review findings 2026-09-16 17:22:38 +08:00
Yichen Jiang dc9256536b Merge remote-tracking branch 'origin/master' into feat/plugin-mgmt-4-web
Master now carries the final #4182 plugin manager and the live client
module graph of #4189, so the web management layer is re-ported onto
them:

- The manager keeps master's `ManagementError` codes, `ChangeResult`
  stages, `configure()` under `hmr.runExclusive`, `reload()` warnings
  and `writePluginEnabled` name matching, and adds back `inspect`,
  request-id installs with `plugin-manager/install-log` and
  `plugin-manager/install-state`, `cancelInstall`, `listBundles` titles,
  rows and overrides, `packageResult.kind`, `pnpmCommand` and
  `inspectTimeoutMs`, and `plugin-manager/changed` after each change and
  each `profile/reconciled`. A failed or cancelled installation restores
  the snapshotted manifest and lockfile instead of master's one-shot
  `pnpm remove` cleanup, so `cleanup` and `remainingDependencies` leave
  the result.
- App boot keeps master's reconciliation diagnostics and emits
  `profile/reconciled`; `watchUserPatches` goes with the launcher's
  watcher now that HMR owns profile reloads.
- The settings Plugin list tab stays read-only with #4189's page-local
  sync failures and retry; master's bundle form and row checkboxes are
  dropped again. The sidebar page words the Host's codes through its
  dictionary, and a client bundle change follows the live module graph
  without a page reload.
- The web e2e scaffold no longer chooses a reload policy: the base
  bundle's `hmr` row turns on with the profile context and application
  readiness, and `profile.hmr: false` disables it through an overlay for
  the startup-only scenario.
2026-09-15 17:28:08 +08:00
Yichen Jiang 4d1adb8541 feat(web): manage plugins over the #4182 manager from the sidebar page
The sidebar Plugins page now drives `ctx.pluginManager` from #4182 and the
settings Plugin list tab goes back to read-only: its bundle form and row
checkboxes are removed, and the page joins `listBundles` with
`listPlugins` into one view per bundle, decides availability from the
inventory's `managementAvailable`, switches bundles and rows through
`setBundleEnabled` and `setPluginEnabled`, and confirms uninstalls.

The manager gains what the guided install dialog needs: `inspect(spec)`
reads a spec through the registry or a directory before anything installs
and refuses specs without a bundle; `installBundle` takes a request id,
streams each pnpm run as `plugin-manager/install-log`, reports its phases
as `plugin-manager/install-state`, classifies a failed run into
`packageResult.kind`, names the bundle it added, and restores
`package.json` and `pnpm-lock.yaml` when the run fails, is cancelled, or
adds a package without a bundle patch; `cancelInstall(requestId)` answers
only after pnpm exited and the files are back; `listBundles` carries
titles, one-liners, declared rows with their live entries, and the
built-in rows a bundle overrides; every completed operation and every
patch generation applied outside the manager emits
`plugin-manager/changed`. The config gains `pnpmCommand` and
`inspectTimeoutMs`. App boot emits `profile/reconciled` after each applied
patch generation and finds a root Include mounted under its pinned id by
another copy of the module.

Row diagnostics, dependency checks, and the non-bundle dependency group
are not carried over; the web e2e scaffold mounts a launcher-style
managed profile instead of the removed profile runtime.
2026-09-15 16:36:31 +08:00
Turtle 311156b1c2 Merge master and preserve profile-managed HMR 2026-09-15 16:20:47 +08:00
Turtle 61665c3454 fix(plugin-manager): clean failed installs and address critical review findings 2026-09-15 15:15:49 +08:00
Turtle abd765a600 refactor(hmr): own profile reload lifecycle through YAML 2026-09-15 13:01:06 +08:00
Turtle 98b92b683c feat: add current-profile plugin manager service and Web controls 2026-09-14 23:09:05 +08:00