From ae0353dd12a38c3b1f51fa9c20645ef008973058 Mon Sep 17 00:00:00 2001 From: oliver Date: Sat, 10 Oct 2026 01:37:47 +0800 Subject: [PATCH] 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 --- lib/client.browser.js | 34 +++++++++++++++++++++++++++------- lib/index.js | 11 +++++++++-- package.json | 2 +- test/compatibility.test.js | 2 +- 4 files changed, 38 insertions(+), 11 deletions(-) diff --git a/lib/client.browser.js b/lib/client.browser.js index 75d7e37..8859368 100644 --- a/lib/client.browser.js +++ b/lib/client.browser.js @@ -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). diff --git a/lib/index.js b/lib/index.js index 713753b..3717ec4 100644 --- a/lib/index.js +++ b/lib/index.js @@ -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 diff --git a/package.json b/package.json index 050566d..8f6379b 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/test/compatibility.test.js b/test/compatibility.test.js index 29d0eef..62d3b51 100644 --- a/test/compatibility.test.js +++ b/test/compatibility.test.js @@ -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',