Commit Graph
338 Commits
Author SHA1 Message Date
Yichen Jiang d4b109f439 Merge pull request #4907 from deepseek-harness/worktree/plugin-install-country-registry
feat(plugin-manager): select the first responsive public registry
2026-09-22 19:51:49 +08:00
Yichen Jiang 3c50bf6b6c feat(plugin-manager): choose the first responsive public registry 2026-09-22 18:38:43 +08:00
Yichen Jiang 8cf07ea76c fix(plugin-manager): preserve remembered registries during initial lookup 2026-09-22 15:32:16 +08:00
winewill cafb9b93b4 fix: address adversarial review of the fatal-diagnostics branch
client-modules: a batch that already executed is never requested again,
even when its first importer was a row it did register — every executed
batch URL is remembered and a still-missing row goes straight to its
one-resource URL. The in-process module system test in test-support is
updated to the aggregated arrival message. describeError replaces three
inline non-Error ternaries and is covered.

subprocess-local: the spill path is published only after the exclusive
open succeeds, so a failed open (EEXIST on a planted entry included) can no
longer unlink a path this process did not create; a reporter that throws is
contained and written to stderr; logSpillFailure builds the owner reporter
for both the runtime and the SSH helper so the logging path is exercised
once instead of duplicated. Tests cover the runtime logger, the bare-caller
stderr default, EEXIST, and a throwing reporter.

desktop: a fatal failure during shutdown still writes its report; the
report phase follows the backend state so a failure during a restart is
'startup' again; writeReport is a required recovery operation and the
dialog tests await the write; the shell pid is labelled; the withheld
response-header list states why set-cookie is dropped.

app-boot, READMEs, Agent Note: "never resumes" is restated as control never
returning to the failed operation while the loop runs until release
settles; inspect output is bounded; incident inferences (who removed the
spill directory, which batch failure occurred) are written as inferences.
2026-09-22 14:41:10 +08:00
winewill 9f9a50e553 feat(app-boot): treat uncaught exceptions as fatal through installFailLoud
The Desktop Host and every other profile launcher installed a fail-loud
handler for unhandled rejections only. A synchronous throw from a stream
'data' listener fell through to Node's default uncaught-exception path: it
printed and exited without the surface's release hook, and the label and
format differed from every other fatal diagnostic.

installFailLoud now registers the same report for 'uncaughtException' with
the label `fatal uncaught exception`, shares the exit latch and release
timeout with rejections, and never resumes the process. The diagnostic is
util.inspect(err, { depth: 4 }) instead of err.stack so enumerable
properties (a node:fs error's code, syscall, and path) and the cause chain
reach stderr; the `fatal load failure` label for rejections is unchanged
because the Web profile expected-output e2e tests match it.
2026-09-22 12:46:37 +08:00
turtle1999 112ce776ac release(dsh): 0.1.7-alpha.1 2026-09-22 11:59:16 +08:00
Turtle 8c146d978f feat(tool-cordis): inspect live plugin Config schemas (#4834)
* feat(tool-cordis): inspect live plugin Config schemas

Replace the Host `Builtin` inspect provider, which described a dynamic Host
half the model can no longer define, with a `Config` provider that lists every
live Loader entry with its Config status and projects one entry's native
Schemastery Config into a self-contained JSON Schema document. app-boot exports
its projector, native-schema check, and the shared `loaderExpression`
definition for the live walk; the CLI dump keeps its profile-composition path.

* fix(tool-cordis): refresh config catalog pointer and presets e2e provider list

* fix(tool-cordis): resolve the Loader at Config query time

A Host composed programmatically without a Loader keeps registering the
inspect providers; `Config.listConfigs` reports the missing Loader when
queried. Refresh the advanced-toolchain recordings whose provider list now
carries `Config` in place of `Builtin`.

* docs: regenerate module graph for the tool-cordis app-boot dependency

* docs: regenerate event graph pointer for app-boot exports

* fix(tool-cordis): address review on the Config inspect provider

Report the Loader entry id (unique across include trees) beside the tree-local
patch id, classify group and include carriers as `tree`, treat disabled and
disposed fibers as `inactive`, and drop the unreachable boolean-schema branch
by narrowing the projector's root type. The presets e2e now queries
`Config.listConfigs` over the booted Web profile and projects the shipped
`tools` row; the advanced-toolchain recordings carry the updated manifest.

* fix(tool-cordis): page the Config directory and filter it by plugin name

A full directory of a shipped Web profile exceeds the 50000-byte spill cap
once rows carry more fields, and the spilled preview is no longer JSON.
`Config.listConfigs` now returns pages (`offset`, `limit` up to 100, default
25) with `total` and `nextOffset`, and an exact plugin `name` filter; the
presets e2e reads the shipped rows through the filter.
2026-09-21 20:39:29 +08:00
Turtle 601d6761e4 feat(settings): project volatile Config through profile-backed forms (#4587) 2026-09-21 18:26:03 +08:00
Turtle 4eb26f0e71 feat(cli): export JSON Schema for Cordis configuration (#4705)
* feat(cli): dump native Cordis config schemas

* test(cli): include schema dump in launcher help snapshot

* feat(cli): export JSON Schema for Cordis configuration

* fix(cli): preserve config schema discovery and annotations

* fix(cli): conservatively project effectful config validation

* refactor(boot): encapsulate profile config schema generation

* fix(cli): follow Loader disabling and native writeback in config schema

- Dormant group/include rows (disabled without group: true) may omit
  config and are recorded as childless trees; the entry schema skips
  their child declarations, matching the Loader, which never creates them.
- Lazy builders run once per document, and a wrapped null-only projection
  keeps a deduplicated type list so the document stays compilable.
- Union widening applies only when an earlier branch can write adapted
  values back through a container; +Infinity max and -Infinity min
  without a step are treated as the native no-ops they are.
- Projection limitations repeat at every entry sharing a Config, and the
  entry schema documents the boolean/expression domain of disabled.
- Record the schema-dump validation policy as an Agent Note.
2026-09-21 09:19:24 +00:00
Yichen Jiang 080bea0b85 Merge remote-tracking branch 'origin/master' into worktree/experimental-voice-input
# Conflicts:
#	scripts/gen-cordis-catalog.ts
2026-09-21 15:39:45 +08:00
imccyu 7252e5823b fix(boot): keep linked-root removal independent of relinking 2026-09-21 14:00:52 +08:00
imccyu 73e9efbbdf docs(boot): define linked-root removal across generations 2026-09-21 14:00:52 +08:00
imccyu cf41c158ba fix(boot): keep host lookups native and fix source smoke fixtures 2026-09-21 14:00:52 +08:00
imccyu c9d4b7561d fix(boot): apply linked peers at each native lookup position 2026-09-21 14:00:52 +08:00
imccyu 3e7af9cfbb feat(boot): resolve linked plugin peers from the running installation 2026-09-21 14:00:51 +08:00
imccyu a946008275 docs(boot): document linked plugin peer resolution 2026-09-21 14:00:51 +08:00
Yichen Jiang b3ff0f5dc7 Merge remote-tracking branch 'origin/master' into worktree/experimental-voice-input
# Conflicts:
#	packages/client/ui-plugin-manager/tests/components.client.spec.tsx
#	packages/client/ui-primitives/README.i18n.yaml
#	packages/client/ui-primitives/README.md
#	packages/client/ui-primitives/README.zh.md
#	packages/client/ui-primitives/tests/icons.client.spec.tsx
#	packages/client/ui-primitives/tests/state-dot.client.spec.tsx
2026-09-21 10:56:23 +08:00
Turtle 3f7016a422 feat(config): add volatile schemas and watcher-driven updates (#4579)
* feat(cordis): add volatile config references without remounting

* refactor(cordis): use internal prefix for volatile updates

* fix(schemastery): preserve metadata-free serialized schemas

* refactor(config): move volatile updates into an opt-in Loader plugin

* refactor(config): reuse Loader context patch for volatile updates

* refactor(config): let the volatile watcher commit equal candidates

* refactor(config): separate comparison from volatile reconciliation

* refactor(loader): extract pure entry option diff

* refactor(loader): compare volatile config through schema metadata

* refactor(loader): make volatile comparison policy explicit

* refactor: name config reload and volatile HMR events

* refactor(loader): compare volatile config in one schema walk; declare hmr/volatile-update in volatile-hmr

Replace the mask/project pair in vendor/loader/src/config/diff.ts with one recursive schema-guided comparison. Absent objects now compare as their declared schema default at every object node, not only under volatile ancestors; both inputs resolve to the same validated config, so the one changed assertion (undefined vs {} under a non-volatile object schema) becomes unchanged. The isJsExpr special case is dropped: expression keys fall through to raw comparison.

Move the hmr/volatile-update Events declaration from vendored Cordis core to its producer, packages/boot/volatile-hmr, matching how dsh-hmr declares hmr/change and hmr/reload. Vendored cordis events.ts returns to upstream content; the inherited Cordis API catalog and the event matrix are regenerated, and the tutorial example imports the package types.

* fix(cosmokit): compare sparse array slots in deepEqual; extend volatile diff coverage

Array.prototype.every skipped holes, so deepEqual(Array(1), ['new']) was true in one direction. Compare every index with holes read as undefined.

config/diff.ts treats expression objects as opaque values even when a schema field shares the marker name, and looks up field schemas with Object.hasOwn so unknown keys named after Object prototype members compare raw. Tests cover absent-object defaults, sparse arrays in both directions, prototype-named keys, metadata-free serialized schema nodes, and a runtime remount check in volatile-hmr. READMEs and the architecture note describe the object-default rule.

* test(schemastery): build serialized schema fixtures without unknown casts

verify-no-unknown-casts rejects the two as-unknown assertions in scripts/volatile-config.spec.ts. Parse the serialized nodes from JSON text instead, matching scripts/loader-config-diff.spec.ts.

* docs(volatile-hmr): drop the scope-scan tag from hmr/volatile-update

verify-scoped-events rejects @dshScopeScan on an event that carries no scoped keys. Catalogs and the event matrix are regenerated.

* docs: regenerate module graph for the volatile-hmr dependency edge

verify-module-graph reports the merged docs stale: volatile-hmr now lists its app-boot dependency.

* test(volatile-hmr): watch the real YAML through an exact config watch

The Windows coverage lane lost the whole spec: its vitest worker exited without a report once this test started a chokidar directory watch (root: ['.']), the only real directory watch in the test suite. Register the include file through hmr.watchConfig instead, the pattern the HMR package's own real-file tests run on Windows, and emit loader/config-reload from the refresh callback as the root-watch path does. packages/boot/hmr/tests/coordination.spec.ts already covers that root-watch emission with a mocked watcher; volatile-hmr and hmr sources stay at 100% coverage.

* test(volatile-hmr): pass the Include path as a file URL

On Windows the plain absolute path made the include subtree fail to load, so the profile-patch test found no consumer entry. Other Include fixtures in this spec already pass file URLs.

* refactor(loader): commit volatile config in Entry.update and remove volatile-hmr

loader/config-reload was emitted from two places (app-boot profile reconciliation and the HMR root watch) and by hand in tests, because a separate plugin needed a "configuration applied" signal to run its own pass. Every update path already funnels through Entry.update, which is where the Loader decides to skip a remount for a volatile-only change. Entry._commitVolatile now parses the candidate through the fiber's internal/config hook and schema, commits the new values into the running references when the ordinary effective values still match, and emits loader/volatile-update to the owning fiber. A changed ordinary effective value falls back to the ordinary remount instead of leaving the instance stale; an invalid candidate is logged and keeps the running references, with the raw config retained for the next activation. Forced group updates take the same path.

Remove packages/boot/volatile-hmr, its event, the app-boot loader/config-reload declaration and emit, and the HMR emission. Loader runtime and real-file tests move to scripts/loader-volatile-update.spec.ts. Docs, catalogs, the event matrix, module graph and the architecture note follow.

* test: share the volatile schema fixture between the schema and Loader specs

The duplication gate flagged the identical Config fixture in scripts/volatile-config.spec.ts and scripts/loader-volatile-update.spec.ts.

* refactor(loader): reduce config/diff.ts to equalExceptVolatile

Entry.update now computes the strict raw option changes itself and only asks diff.ts whether two raw configs differ at most in volatile fields. The ignoreVolatile flag, the options object and the "config is the sole change" rule leave diff.ts; they were the Loader's decision and now sit next to the commit they gate. The raw-config deepEqual in the volatile branch is gone because the strict change list already answers it.

* refactor(loader): type the volatile comparison against Schemastery's Schema

config/diff.ts narrows the plugin's Standard Schema to Schemastery's own Schema type instead of a hand-written structural interface, which makes vendor/loader a type-only dependent of @deepseek-ai/schemastery. The rescope codemod treats the vendor identifier comparison like schemastery's own source and renames the import through an exact edit.

* fix(loader): address review findings on the volatile commit path

Entry.update only replaces the fiber's pending raw config for a volatile-only change, so a noSave direct update survives a no-op entry update. _commitVolatile logs at debug level when ordinary effective values changed and the ordinary update applies, dispatches loader/volatile-update with a Context receiver like Cordis' own filtered emits, and isolates a throwing listener. Schemastery reports unsupported volatile values as ValidationError with their path and validates a lazy inner once when it is built. The inherited-event catalog gains internal/config and corrected source lines; the Loader spec imports dsh-hmr by package name and covers the new behaviors. Docs record that class-instance ordinary fields always take the remount path and that .default() accepts the raw input type.
2026-09-21 00:04:06 +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
Turtle 8f0dd55c2c feat(plugins): render icons from package manifests (#4697)
* feat(plugins): render icons from package manifests

* fix(plugins): include declared icons in canonical package payloads
2026-09-20 17:00:17 +08:00
Yichen Jiang c6bc1a2fab Merge remote-tracking branch 'origin/master' into worktree/experimental-voice-input
# Conflicts:
#	apps/desktop/electron-builder.config.mjs
#	apps/desktop/tests/macos-signature.spec.ts
#	apps/desktop/tests/main-startup.spec.ts
#	apps/web/tests/expected/plugin-config/official.expected.md
#	apps/web/tests/expected/plugin-manager/live-enabled.expected.md
#	apps/web/tests/expected/plugin-manager/manager.expected.md
#	apps/web/tests/plugin-config.e2e.ts
#	apps/web/tests/plugin-manager.e2e.ts
#	apps/web/tsconfig.json
#	docs/module-graph.i18n.yaml
#	docs/module-graph.md
#	docs/module-graph.zh.md
#	packages/client/ui-conversation/src/client/skeleton/InputBar.tsx
#	packages/client/ui-plugin-manager/src/client/locales.ts
#	packages/client/ui-plugin-manager/src/client/presentation.ts
#	packages/client/ui-plugin-manager/tests/components.client.spec.tsx
#	packages/client/ui-primitives/README.i18n.yaml
#	packages/client/ui-primitives/README.md
#	packages/client/ui-primitives/README.zh.md
#	packages/client/ui-primitives/src/StateDot.tsx
#	packages/client/ui-primitives/src/icons/index.tsx
#	packages/client/ui-primitives/tests/icons.client.spec.tsx
#	packages/experimental/README.i18n.yaml
#	packages/experimental/README.md
#	packages/experimental/README.zh.md
#	packages/extensions/cordis-client-runner/src/client/slot-catalog.ts
#	pnpm-lock.yaml
#	scripts/gen-cordis-catalog.ts
#	tsconfig.host.json
2026-09-20 13:22:11 +08:00
imccyu a05f2244cf fix(plugins): remove unknown casts from metadata loading and fixtures 2026-09-19 23:05:40 +08:00
imccyu a9f205c584 refactor(plugins): rename PackageMeta to PluginLocalizedMeta 2026-09-19 20:19:08 +08:00
imccyu 0b7cb4102b feat(plugins): resolve installed metadata through runtime resource exports 2026-09-19 20:19:04 +08:00
imccyu f8240240fa docs(plugins): define localized display metadata and fallback rules 2026-09-19 20:19:03 +08:00
imccyu f7f22a6b48 refactor(boot): finish the interception vocabulary in code comments and the app-boot README
The Consumer-owned strictness bullet names the Loader subtree that agent presets own instead of the runtime resolution; the service field, its file comment, the dispose JSDoc, and the route JSDoc use interception; the native-after-interception route documents both anchors it can carry; stale module-fallback comments name the runtime resolution; importFromResolution matches its parameter.
2026-09-19 17:06:35 +08:00
imccyu c0f34b4bcd docs(boot): name both installation anchor sources
The CLI derives the installation anchor from import.meta.url and the Desktop Host builds it from its runtime directory; neither is a symlink, so the installation root is kept as given. The note and the traversal comment say so instead of attributing every launcher to import.meta.url.
2026-09-19 17:06:35 +08:00
imccyu 15e07d2721 refactor(boot): rename the profile resolution generation to the runtime resolution
RuntimeResolution, RuntimeResolutionEntry, RuntimeResolutionOptions, and createRuntimeResolution replace the generation-named table types; installRuntimeInterception returns a RuntimeInterception; PluginPackages takes { resolution }; Worker environment data carries { resolution }; routes are native, interception, and native-after-interception. replace() keeps its successor-generation wording.
2026-09-19 17:06:34 +08:00
imccyu 5f48b2fff6 docs(boot): call the package table the runtime resolution
Notes and the app-boot README name the startup package table the runtime resolution and the installed hook the interception; generation remains the word for the additive successor semantics of replace().
2026-09-19 17:06:34 +08:00
imccyu 109efc7942 test(boot): cover the projection removal edge cases
A profile with the legacy directory but no node_modules, and a symlink whose target parent is gone, exercise the remaining branches of removeLinkProjections; the non-ENOENT realpath failure is a host filesystem fault and is excluded from coverage like its siblings.
2026-09-19 16:49:21 +08:00
imccyu 02fec3209e fix(boot): remove link-backend profile projections on profile load
removeLinkProjections unlinks the symlinks under a profile's node_modules whose target lies inside <profile>/.dsh-module-fallback/node_modules and then removes that directory; pnpm-installed packages and every other symlink stay. loadProfile and the Desktop applyRelease call it once per launch. Regression: with bundle A installed but deselected and bundle B selected, B's same-named plugin resolves instead of A's projected copy.
2026-09-19 16:34:07 +08:00
imccyu 0c342a81c4 docs(boot): record the one-time removal of link-backend profile projections
The lookup-order note, the cleanup-removal note, and the app-boot and Desktop READMEs state that profile load removes the .dsh-module-fallback projections the dsh 0.1.5 releases wrote into a profile, so a deselected bundle's projected plugin no longer shadows the same-named plugin the generation selects from another bundle.
2026-09-19 16:34:07 +08:00
imccyu 45417197c6 refactor(boot): name routes and collectors after the interception layer
Resolution routes are native, generation, and native-after-generation: Node keeps the importer's lookup, answers from the generation entry at the interception layer, or resumes its chain at that layer. The profile module collects installation-scope and profile-scope packages and reads package manifests under those names; the generation types describe the interception layer instead of a fallback table.
2026-09-19 15:34:09 +08:00
imccyu 82337a2e71 test(boot): canonicalize temporary roots with the native realpath
Windows tmpdir spells the user directory with an 8.3 short name. Generation anchors and Node's own resolution report the long name, so the resolver, generation, and real-launch fixtures now create their roots through realpathSync.native and compute expected paths the same way.
2026-09-19 15:19:32 +08:00
imccyu 71c7f6cc09 refactor(boot): name the interception layer parent and note intentional anchors
routeScoped names the profile parent whose node_modules is the interception layer. The installation traversal records why the installation root keeps its caller-supplied directory and why generation construction keeps its Promise return type.
2026-09-19 14:39:45 +08:00
imccyu 0e2097ac18 docs(boot): describe the interception layer in the generation note and README
The generation note and the app-boot README no longer claim that runtime lookup hides the shared position or recognizes profile projections: generation entries occupy their package names at $DSH_HOME/profiles/node_modules and every other name sees that directory as an ordinary ancestor. Pairing records re-recorded.
2026-09-19 14:39:44 +08:00
imccyu d71b8a2f1c refactor(boot): narrow legacy-links exports and refresh resolution JSDoc
isPackagedExecutable has one caller in its own module; the installation traversal comment names the runtime resolver instead of the removed fallback directory.
2026-09-19 14:12:02 +08:00
imccyu 6f57030958 fix(boot): occupy the interception layer instead of hiding it
The generation stands in for $DSH_HOME/profiles/node_modules/<name> only for names it carries; other names see that directory as an ordinary ancestor. The interception layer is the first ancestor node_modules above the importer's profile directory, so application-owned profiles follow the same rule. Stale .dsh-module-fallback projections are ordinary symlinks: their shape recognition leaves the resolver, the local package names, and the bundle dependency traversal.

The resolver spec gains a table-driven matrix over importer, package kind, ancestor layers, and directory or symlink form, with readable fixture names.
2026-09-19 13:29:13 +08:00
imccyu 518c7a604b fix(boot): preserve runtime fallback precedence 2026-09-19 13:29:13 +08:00
imccyu fcc6cc8c12 docs(boot): align runtime-only resolution contracts 2026-09-19 13:29:13 +08:00
imccyu 6d8aac066d fix(boot): resolve profile dependencies from real package directories 2026-09-19 13:29:13 +08:00
imccyu 9fd0a5ad52 refactor(boot): keep runtime-only profile resolution 2026-09-19 13:29:13 +08:00
Yichen Jiang 5fe06ee64b Merge remote-tracking branch 'origin/master' into worktree/npm-registry-mirror-config-029a47
# Conflicts:
#	packages/client/ui-plugin-manager/src/client/PluginManagerPage.tsx
#	packages/client/ui-plugin-manager/src/client/manager-store.ts
2026-09-18 20:37:39 +08:00
Yichen Jiang 27207bfedb test(plugin-manager): stop an install before its first run deterministically
The cancelled-between-attempts case raced a zero-delay timer against the
file restore between two registries; under parallel load the restore won,
the second attempt started, and the once-only mock let it reach the real
pnpm. The stop now lands while the registries are read, before any run,
which is the same guard and cannot race.
2026-09-18 20:28:15 +08:00
Yichen Jiang 1dfd026b84 Merge remote-tracking branch 'origin/master' into worktree/npm-registry-mirror-config-029a47
# Conflicts:
#	apps/web/tests/expected/plugin-install-cancel/cancelled.expected.md
#	docs/event-producer-consumer.i18n.yaml
#	docs/event-producer-consumer.md
#	docs/event-producer-consumer.zh.md
#	packages/boot/plugin-manager/README.i18n.yaml
#	packages/boot/plugin-manager/README.md
#	packages/boot/plugin-manager/README.zh.md
#	packages/client/ui-plugin-manager/README.i18n.yaml
#	packages/client/ui-plugin-manager/README.md
#	packages/client/ui-plugin-manager/README.zh.md
#	packages/client/ui-plugin-manager/src/client/PluginManagerPage.tsx
2026-09-18 20:21:59 +08:00
Yichen Jiang e7094c81e2 Merge origin/master into worktree/plugin-install-close 2026-09-18 20:03:08 +08:00
Yichen Jiang f938ebad64 fix(web): recover lost plugin installation responses 2026-09-18 19:58:04 +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
Turtle de662ee010 fix(boot): skip failed profile bundles and retain recovery controls (#4516) 2026-09-18 16:35:50 +08:00
Yichen Jiang e06c7852ff Merge remote-tracking branch 'origin/master' into worktree/npm-registry-mirror-config-029a47 2026-09-18 15:24:42 +08:00