迭代18: QMT Bridge MCP 能力内建(R-020)—— 动态挂载 dsh-mcp-client + 状态出口(设置页/会话头部灯)

This commit is contained in:
2026-09-09 08:58:49 +08:00
parent 9382ace3a0
commit c700f2f760
16 changed files with 1402 additions and 9 deletions
+2 -2
View File
@@ -60,8 +60,8 @@ const HANDLERS = [
* @param {import('@deepseek-ai/cordis').Context} ctx
* @param {object} runtime { manager, settings, dataSource, marketCache }
*/
export function registerApi(ctx, { manager, settings, dataSource, marketHub, marketFeed, qmtHealthMonitor, storage, tradeSync, positionSync, attribution }) {
const runtime = { ctx, manager, settings, dataSource, marketHub, marketFeed, qmtHealthMonitor, storage, tradeSync, positionSync, attribution };
export function registerApi(ctx, { manager, settings, dataSource, marketHub, marketFeed, qmtHealthMonitor, storage, tradeSync, positionSync, attribution, mcpManager }) {
const runtime = { ctx, manager, settings, dataSource, marketHub, marketFeed, qmtHealthMonitor, storage, tradeSync, positionSync, attribution, mcpManager };
ctx.effect(() => ctx.webServer.register({
kind: 'prefix',
+2 -1
View File
@@ -42,7 +42,7 @@ function projectQuote(hub, code, snap) {
* @param {object} args
* @param {object} runtime { settings, dataSource, marketHub, marketFeed(=QuoteSync), qmtHealthMonitor, positionSync }
*/
export async function handleMarket(method, args, { settings, dataSource, marketHub, marketFeed: marketSync, qmtHealthMonitor, positionSync }) {
export async function handleMarket(method, args, { settings, dataSource, marketHub, marketFeed: marketSync, qmtHealthMonitor, positionSync, mcpManager }) {
switch (method) {
case 'market-snapshot': {
const codes = Array.isArray(args.codes) ? args.codes : [];
@@ -86,6 +86,7 @@ export async function handleMarket(method, args, { settings, dataSource, marketH
? { ...marketSync.getStatus(), state: state(marketSync.getStatus()) }
: null,
qmt: qmtHealthMonitor ? qmtHealthMonitor.getStatus() : null,
mcp: mcpManager ? mcpManager.getStatus() : null, // R-020/迭代18QMT MCP 状态(会话头部指示灯)
};
}
case 'sync-now': {
+14 -1
View File
@@ -28,6 +28,7 @@ export const QMT_METHODS = new Set([
'qmt-connections/set-default',
'qmt-connections/test',
'qmt-connections/active-host',
'qmt-connections/mcp-status', // R-020/迭代18QMT MCP 状态(probe / 缓存)
]);
/** 服务端代理测试连接:GET {baseUrl}/health(浏览器跨域规避,Q3 手动测试) */
@@ -63,7 +64,7 @@ async function testQmtConnection(ctx, args) {
* @param {object} args
* @param {object} runtime { manager, settings, dataSource, marketCache }
*/
export async function handleQmt(method, args, { ctx, settings, dataSource, marketFeed, qmtHealthMonitor }) {
export async function handleQmt(method, args, { ctx, settings, dataSource, marketFeed, qmtHealthMonitor, mcpManager }) {
switch (method) {
case 'qmt-connections': {
const state = getQmtConnections(settings);
@@ -80,6 +81,8 @@ export async function handleQmt(method, args, { ctx, settings, dataSource, marke
marketFeed?.setBaseUrl(value.baseUrl);
qmtHealthMonitor?.probe(); // 切换后立即补探健康
ctx?.logger?.info('[one-divine-lot] 激活配置地址已变更,数据源热切换 → ' + value.baseUrl);
// R-020/迭代18:MCP 目标地址随激活地址变更重挂
mcpManager?.resync(value.baseUrl).catch(() => {});
}
return value;
}
@@ -94,6 +97,8 @@ export async function handleQmt(method, args, { ctx, settings, dataSource, marke
qmtHealthMonitor?.probe(); // 切换后立即补探健康
ctx?.logger?.info('[one-divine-lot] 激活配置已删除,自动切换 → ' + active.name + ' (' + active.baseUrl + ')');
}
// R-020/迭代18:激活已变(或列表清空)→ MCP 跟随新激活 / 卸载
mcpManager?.resync(active?.baseUrl).catch(() => {});
return { ...afterState, active, removedWasActive: result.removedWasActive, removedWasDefault: result.removedWasDefault };
}
case 'qmt-connections/activate': {
@@ -105,6 +110,8 @@ export async function handleQmt(method, args, { ctx, settings, dataSource, marke
qmtHealthMonitor?.probe(); // 切换后立即补探健康
ctx?.logger?.info('[one-divine-lot] QMT 连接激活: ' + active.name + ' (' + active.baseUrl + ')');
}
// R-020/迭代18:激活切换 → MCP 实例跟随(卸旧挂新,url=新激活 baseUrl+/mcp
mcpManager?.resync(active?.baseUrl).catch(() => {});
return { ...state, active };
}
case 'qmt-connections/set-default':
@@ -132,6 +139,12 @@ export async function handleQmt(method, args, { ctx, settings, dataSource, marke
} catch { /* baseUrl 非法时返回空 */ }
return { name: active.name, baseUrl, host, wsUrl, activeId: state.activeId ?? '' };
}
case 'qmt-connections/mcp-status': {
// R-020/迭代18MCP 状态(缓存秒回;refresh=true 即时探测)
if (!mcpManager) return { state: 'disabled', error: 'mcpManager 不可用', checkedAt: 0 };
if (args.refresh) return await mcpManager.probe();
return mcpManager.getStatus();
}
default:
throw Object.assign(new Error('unknown qmt method: ' + method), { code: 'not-found' });
}