mirror of
https://github.com/hansjone/dsh-search-mcp.git
synced 2026-10-11 22:10:46 +08:00
Fix Desktop search-mcp takeover so web_search uses Bailian.
Drop nested Config.volatile that left the fiber inactive, unwrap the cordis web proxy to pin/wrap search at runtime, and restore deepseek on disable. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
d729b03f70
commit
23685cd545
8 changed files with 343 additions and 72 deletions
73
scripts/_validate-config.mjs
Normal file
73
scripts/_validate-config.mjs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
/**
|
||||
* Reproduce Desktop Config validation for search-mcp.
|
||||
* Usage: node scripts/_validate-config.mjs
|
||||
*/
|
||||
import { createRequire } from 'node:module'
|
||||
import { pathToFileURL } from 'node:url'
|
||||
import { join, dirname } from 'node:path'
|
||||
import { fileURLToPath } from 'node:url'
|
||||
|
||||
const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const nm = join(process.env.USERPROFILE || '', '.dsh', 'profiles', 'node_modules')
|
||||
const req = createRequire(join(nm, 'package.json'))
|
||||
const cordisUrl = pathToFileURL(req.resolve('@deepseek-ai/cordis')).href
|
||||
const { Context, Service } = await import(cordisUrl)
|
||||
const mod = await import(pathToFileURL(join(pkgRoot, 'lib', 'index.js')).href)
|
||||
|
||||
class WebRuntime extends Service {
|
||||
searchProviders = new Map()
|
||||
searchProviderId = 'deepseek-official'
|
||||
constructor(ctx) {
|
||||
super(ctx, 'web')
|
||||
}
|
||||
registerSearchProvider(p) {
|
||||
this.searchProviders.set(p.id, p)
|
||||
return () => this.searchProviders.delete(p.id)
|
||||
}
|
||||
async search() {
|
||||
return { used: this.searchProviderId }
|
||||
}
|
||||
}
|
||||
|
||||
function plugin(ctx, config) {
|
||||
mod.apply(ctx, config)
|
||||
}
|
||||
plugin.inject = ['web']
|
||||
plugin.Config = mod.Config
|
||||
|
||||
const root = new Context()
|
||||
try {
|
||||
await root.plugin(WebRuntime)
|
||||
await root.plugin(plugin, {
|
||||
defaultServer: 'bailian',
|
||||
maxResults: 8,
|
||||
searchTimeoutMs: 30000,
|
||||
servers: [{ id: 'bailian', kind: 'bailian', apiKeyEnv: 'DASHSCOPE_API_KEY' }],
|
||||
})
|
||||
const raw = root.web[Symbol.for('cordis.original')]
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
ok: true,
|
||||
faceId: root.web.searchProviderId,
|
||||
rawId: raw?.searchProviderId,
|
||||
wrapped: !!raw?.search?.__searchMcpWrapped,
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
)
|
||||
} catch (e) {
|
||||
console.log(
|
||||
JSON.stringify(
|
||||
{
|
||||
ok: false,
|
||||
message: String(e?.message || e),
|
||||
stack: String(e?.stack || '').slice(0, 1200),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
)
|
||||
process.exit(1)
|
||||
}
|
||||
78
scripts/_verify-takeover.mjs
Normal file
78
scripts/_verify-takeover.mjs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
/**
|
||||
* Prove unwrap + direct web.search wrap routes off deepseek-official.
|
||||
* Run: node scripts/_verify-takeover.mjs
|
||||
*/
|
||||
import { createRequire } from 'node:module'
|
||||
import { dirname, join } from 'node:path'
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url'
|
||||
|
||||
const pkgRoot = join(dirname(fileURLToPath(import.meta.url)), '..')
|
||||
const profilesNm = join(process.env.USERPROFILE || '', '.dsh', 'profiles', 'node_modules')
|
||||
const require = createRequire(join(profilesNm, 'package.json'))
|
||||
const { Context, Service } = require('@deepseek-ai/cordis')
|
||||
const { apply } = await import(pathToFileURL(join(pkgRoot, 'lib', 'index.js')).href)
|
||||
const ORIGINAL = Symbol.for('cordis.original')
|
||||
|
||||
class WebRuntime extends Service {
|
||||
searchProviders = new Map()
|
||||
searchProviderId
|
||||
constructor(ctx, config = {}) {
|
||||
super(ctx, 'web')
|
||||
this.searchProviderId = config.searchProvider ?? 'deepseek-official'
|
||||
}
|
||||
registerSearchProvider(p) {
|
||||
this.searchProviders.set(p.id, p)
|
||||
return () => this.searchProviders.delete(p.id)
|
||||
}
|
||||
async search() {
|
||||
if (this.searchProviderId === 'deepseek-official') {
|
||||
return { used: 'deepseek-official', error: 'DeepSeek API error (HTTP 401)' }
|
||||
}
|
||||
const p = this.searchProviders.get(this.searchProviderId)
|
||||
return { used: this.searchProviderId, via: p?.id }
|
||||
}
|
||||
}
|
||||
|
||||
const smcp = Object.assign(function (ctx) {
|
||||
apply(ctx, {
|
||||
defaultServer: 'bailian',
|
||||
maxResults: 8,
|
||||
searchTimeoutMs: 30000,
|
||||
servers: [{ id: 'bailian', kind: 'bailian', apiKeyEnv: 'DASHSCOPE_API_KEY' }],
|
||||
})
|
||||
}, { inject: ['web'] })
|
||||
|
||||
const app = new Context()
|
||||
await app.plugin(WebRuntime, { searchProvider: 'deepseek-official' })
|
||||
const fiber = await app.plugin(smcp)
|
||||
|
||||
const raw = app.web[ORIGINAL]
|
||||
let thrown
|
||||
try {
|
||||
await app.web.search({ query: 'venezuela', maxResults: 5 })
|
||||
} catch (error) {
|
||||
thrown = String(error?.message || error)
|
||||
}
|
||||
|
||||
const enabledOk =
|
||||
raw.searchProviderId === 'search-mcp'
|
||||
&& raw.search.__searchMcpWrapped === true
|
||||
&& /search-mcp/.test(thrown || '')
|
||||
|
||||
await fiber.dispose()
|
||||
const afterDisable = await app.web.search({ query: 'venezuela', maxResults: 5 })
|
||||
const disabledOk =
|
||||
raw.searchProviderId === 'deepseek-official'
|
||||
&& !raw.search.__searchMcpWrapped
|
||||
&& afterDisable?.used === 'deepseek-official'
|
||||
|
||||
const ok = enabledOk && disabledOk
|
||||
console.log(JSON.stringify({
|
||||
ok,
|
||||
enabledOk,
|
||||
disabledOk,
|
||||
thrown: thrown?.slice(0, 160),
|
||||
afterDisable,
|
||||
rawId: raw.searchProviderId,
|
||||
}, null, 2))
|
||||
process.exit(ok ? 0 : 1)
|
||||
Loading…
Add table
Add a link
Reference in a new issue