Fix Desktop boot failure from unconditional settings.plugin.item inject.

Always register settings.section; mount the Web plugins card only after configForms attaches. Harden snapshot store init (0.2.4).

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
oliver 2026-10-10 01:37:47 +08:00
parent c1fed965b1
commit ae0353dd12
4 changed files with 38 additions and 11 deletions

View file

@ -500,12 +500,19 @@ window.__ModuleLoader__.load({
this.secretStateReady = false;
this.legacySecretIds = new Set();
this.credentialStates = {};
this.store = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)(this.project());
try {
this.store = (0, _deepseek_ai_dsh_client_runtime_client.createSnapshotStore)(this.project());
} catch {
this.store = createSnapshotStoreFallback(this.project());
}
if (!this.store || typeof this.store.set !== "function") {
this.store = createSnapshotStoreFallback(this.project());
}
scope.subscribe(() => {
if (this.draft === null) this.publish();
this.readSecretState();
void this.readSecretState();
});
this.readSecretState();
void this.readSecretState().catch(() => {});
}
section() {
return this.scope.getSnapshot();
@ -611,6 +618,7 @@ window.__ModuleLoader__.load({
};
}
publish() {
if (!this.store || typeof this.store.set !== "function") return;
this.store.set(this.project());
}
actions() {
@ -1252,7 +1260,13 @@ window.__ModuleLoader__.load({
const deferred = createDeferredScope(createMemoryScope(MEMORY_DEFAULTS));
const remote = resolveRemote(ctx);
const controller = new SearchMcpCardController(deferred, remote, noopDescribeFace());
let controller;
try {
controller = new SearchMcpCardController(deferred, remote, noopDescribeFace());
} catch (error) {
ctx.logger?.error?.("dsh-search-mcp: controller init failed: %s", error);
return;
}
let pluginsItemId;
let pluginCardMounted = false;
@ -1280,7 +1294,12 @@ window.__ModuleLoader__.load({
ctx.logger?.warn?.("dsh-search-mcp: settings.section unavailable: %s", error);
}
/** Web Plugins → 插件配置 card (also useful on some Desktop builds). */
/**
* Web Plugins → 插件配置 card. Do NOT register this unconditionally:
* Desktop 0.2 may lack `settings.plugin.item`, and injecting a missing
* slot fails the whole client fiber (web boot: dsh-search-mcp: failed).
* Mount only after configForms / settingsScope attaches (web path).
*/
const mountPluginCard = () => {
if (pluginCardMounted) return;
pluginCardMounted = true;
@ -1298,7 +1317,6 @@ window.__ModuleLoader__.load({
ctx.logger?.warn?.("dsh-search-mcp: settings.plugin.item unavailable: %s", error);
}
};
mountPluginCard();
const registerPluginsItem = (id) => {
if (pluginsItemId === id) return () => {};
@ -1336,7 +1354,9 @@ window.__ModuleLoader__.load({
const attachForm = (hit, source) => {
ctx.logger?.info?.("dsh-search-mcp: attach form via %s (%s)", source, hit.ns);
deferred.attach(hit.scope);
return registerPluginsItem(hit.ns);
mountPluginCard();
const offPlugins = registerPluginsItem(hit.ns);
return () => { offPlugins(); };
};
// DSH ≥0.1.7 / 0.2.0 — configForms (persist + plugins.item).

View file

@ -93,8 +93,15 @@ export function apply(ctx, config) {
onChange: () => {},
};
if (typeof settings?.installSection === 'function') {
settings.installSection(ctx, SEARCH_MCP_SETTINGS_NAMESPACE, Config, config, hooks);
return;
try {
settings.installSection(ctx, SEARCH_MCP_SETTINGS_NAMESPACE, Config, config, hooks);
return;
} catch (error) {
settingsCtx.logger?.warn?.(
'dsh-search-mcp: installSection failed (will try register/describe): %s',
error instanceof Error ? error.message : error,
);
}
}
// ≤0.1.5 / some Desktop builds: explicit namespace registration.
// ≥0.1.7: Config is projected from the Loader entry — register may be

View file

@ -1,6 +1,6 @@
{
"name": "dsh-search-mcp",
"version": "0.2.3",
"version": "0.2.4",
"description": "Replace dsh's built-in web search with search MCP servers (Tavily / Brave / Exa / Perplexity / DuckDuckGo / custom), configured from the web Settings page. When this plugin is enabled the built-in DeepSeek search provider is disabled.",
"type": "module",
"main": "lib/index.js",

View file

@ -12,7 +12,7 @@ test('package exports resolve and peerDependencies stay open', async () => {
assert.equal(pkg.exports['.'], './lib/index.js');
assert.equal(pkg.exports['./client'], './lib/client.browser.js');
assert.equal(pkg.engines.node, '>=20');
assert.equal(pkg.version, '0.2.3');
assert.equal(pkg.version, '0.2.4');
for (const name of [
'@deepseek-ai/dsh-api-remotes',