dsh-vscode-mode 0.1.15 → 0.1.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/mcp.ts CHANGED
@@ -1,20 +1,19 @@
1
1
  /**
2
2
  * dsh-vscode-mode host — MCP 运行时管理适配层。
3
3
  * 只操作已装配的 @deepseek-ai/dsh-mcp-client loader entry,不在浏览器侧连接 MCP。
4
+ * 身份常量(包名/条目前缀/新旧格式判定)统一由 compat 层持有,本模块再导出保持既有 import 面。
4
5
  * 作者 ddj 2026年08月22号
5
6
  */
7
+ import {
8
+ LEGACY_PROJECT_PREFIX,
9
+ MCP_PACKAGE,
10
+ PROJECT_ENTRY_PREFIX,
11
+ isProjectEntryId,
12
+ } from './compat.js'
6
13
  import type { MpcConfig, MpcServer, MpcTool } from './shared/mcp.js'
7
14
  import type { Ctx } from './store.js'
8
15
 
9
- const MCP_PACKAGE = '@deepseek-ai/dsh-mcp-client'
10
- /** 项目级 MCP 的 loader entry id 固定前缀(区分 profile 全局 MCP;注意 loader id 不能用 ':' 分隔,故用 '.')。 */
11
- export const PROJECT_ENTRY_PREFIX = 'vsm-mcp.'
12
- const LEGACY_PROJECT_PREFIX = 'vsm-mcp:'
13
-
14
- /** 判断新旧格式项目 entry id,避免旧残留被显示为全局 MCP。 */
15
- export function isProjectEntryId(id: string): boolean {
16
- return id.startsWith(PROJECT_ENTRY_PREFIX) || id.startsWith(LEGACY_PROJECT_PREFIX)
17
- }
16
+ export { LEGACY_PROJECT_PREFIX, MCP_PACKAGE, PROJECT_ENTRY_PREFIX, isProjectEntryId }
18
17
 
19
18
  /** 列出当前 loader 中的 MCP entry。 */
20
19
  function entriesOf(ctx: Ctx): any[] {
@@ -4,11 +4,11 @@
4
4
  * tools.restrict() 负责模型可见性,tools.guard() 负责执行层兜底拒绝。
5
5
  * 作者 ddj 2026年08月22号
6
6
  */
7
- import { PROJECT_ENTRY_PREFIX, entriesOf } from './mcp.js'
7
+ import { LEGACY_PROJECT_PREFIX, entryHash, isProjectEntryId } from './compat.js'
8
+ import { entriesOf } from './mcp.js'
8
9
  import { hashWorkspace } from './mcpProject.js'
9
10
  import type { Ctx } from './store.js'
10
11
 
11
- const LEGACY_PREFIX = 'vsm-mcp:'
12
12
  const UNKNOWN_PROJECT = '__unknown_project__'
13
13
 
14
14
  type ToolProjects = Map<string, string>
@@ -32,17 +32,11 @@ export function matchWorkspace(cwd: string | undefined, paths: string[]): string
32
32
  return matches[0]
33
33
  }
34
34
 
35
- /** 根据 entry id 判断是否为新旧格式项目 MCP。 */
36
- export function isProjectEntry(id: string): boolean {
37
- return id.startsWith(PROJECT_ENTRY_PREFIX) || id.startsWith(LEGACY_PREFIX)
38
- }
35
+ /** 根据 entry id 判断是否为新旧格式项目 MCP(兼容层统一判定)。 */
36
+ export const isProjectEntry = isProjectEntryId
39
37
 
40
- /** 从项目 entry id 解析 workspace hash。 */
41
- function entryHash(id: string): string | undefined {
42
- if (id.startsWith(PROJECT_ENTRY_PREFIX)) return id.slice(PROJECT_ENTRY_PREFIX.length).split('.')[0]
43
- if (id.startsWith(LEGACY_PREFIX)) return id.slice(LEGACY_PREFIX.length).split(':')[0]
44
- return undefined
45
- }
38
+ /** 兼容:旧版前缀常量名(mcpIsolation 历史导出,语义不变)。 */
39
+ export const LEGACY_PREFIX = LEGACY_PROJECT_PREFIX
46
40
 
47
41
  /** 返回一个 agent 不应继承的项目工具名。 */
48
42
  export function denyTools(toolProjects: ToolProjects, currentPath: string | undefined): string[] {
package/src/routes.ts CHANGED
@@ -6,6 +6,7 @@
6
6
  import { readFile } from 'node:fs/promises'
7
7
  import { dirname, extname, join, normalize, resolve, sep } from 'node:path'
8
8
  import { fileURLToPath } from 'node:url'
9
+ import { ROUTE_PREFIX, noteOwnRoute, resetOwnRoutes, routeConflict } from './compat.js'
9
10
  import { RPC_PATH } from './shared/rpc.js'
10
11
  import type { RpcMethod, RpcRequestMap } from './shared/rpc.js'
11
12
  import type { Ctx } from './store.js'
@@ -45,22 +46,40 @@ function imageDirOf(config: unknown): string {
45
46
  const vendorDir = join(dirname(fileURLToPath(import.meta.url)), '..', 'assets', 'vendor')
46
47
 
47
48
  /**
48
- * 注册全部 webServer 路由。
49
+ * 注册全部 webServer 路由(带兼容护栏:前缀冲突预警 + 注册失败降级跳过)。
49
50
  * @author ddj 2026年08月20号
50
51
  * @param ctx DSH 上下文
51
52
  * @param config 插件配置(可选 imageDir 等)
52
53
  * @param handleRpc RPC 分发入口(method,args → RpcResult)
54
+ * @param onWarning 兼容性警告收集(可选)
53
55
  */
54
56
  export function registerRoutes(
55
57
  ctx: Ctx,
56
58
  config: unknown,
57
59
  handleRpc: <M extends RpcMethod>(method: M, args: RpcRequestMap[M]) => Promise<unknown>,
60
+ onWarning?: (warning: string) => void,
58
61
  ): void {
59
62
  const imgDir = imageDirOf(config)
60
63
  const web = ctx.get('webServer')
61
64
  if (!web) return
62
65
 
63
- ctx.effect(() => web.register({
66
+ resetOwnRoutes()
67
+ const conflict = routeConflict(web, ROUTE_PREFIX)
68
+ if (conflict) onWarning?.('兼容性:' + conflict)
69
+
70
+ /** 护栏注册:重复路由抛错时降级为警告并跳过,不让单条路由拖垮装配。 */
71
+ const guarded = (route: { kind: 'exact' | 'prefix'; path: string; handler?: unknown }, label: string): (() => void) | undefined => {
72
+ try {
73
+ const dispose = web.register(route)
74
+ noteOwnRoute(route.kind, route.path)
75
+ return dispose
76
+ } catch (error) {
77
+ onWarning?.('兼容性:' + label + ' 注册失败(' + String(error) + ')')
78
+ return undefined
79
+ }
80
+ }
81
+
82
+ ctx.effect(() => guarded({
64
83
  kind: 'exact',
65
84
  path: RPC_PATH,
66
85
  handler: async (req: unknown, res: { statusCode?: number; setHeader: (k: string, v: string) => void; end: (b?: string) => void }) => {
@@ -83,10 +102,10 @@ export function registerRoutes(
83
102
  res.end(JSON.stringify({ ok: false, error: String(error) }))
84
103
  }
85
104
  },
86
- }), 'edrv: /edrv/rpc route')
105
+ }, '/edrv/rpc 精确路由'), 'edrv: routes')
87
106
 
88
107
  for (const r of IMG_ROUTES) {
89
- ctx.effect(() => web.register({
108
+ ctx.effect(() => guarded({
90
109
  kind: 'exact',
91
110
  path: r.url,
92
111
  handler: async (req: unknown, res: { statusCode?: number; setHeader: (k: string, v: string) => void; end: (b?: string | Uint8Array) => void }) => {
@@ -101,11 +120,11 @@ export function registerRoutes(
101
120
  res.end()
102
121
  }
103
122
  },
104
- }), 'edrv: static ' + r.file)
123
+ }, '静态资源 ' + r.file), 'edrv: routes')
105
124
  }
106
125
 
107
126
  // Monaco Editor AMD 构建:/edrv/vendor/* → assets/vendor/*(路径穿越防护 + 按扩展名 MIME)
108
- ctx.effect(() => web.register({
127
+ ctx.effect(() => guarded({
109
128
  kind: 'prefix',
110
129
  path: VENDOR_PREFIX,
111
130
  handler: async (req: { method?: string; url?: string }, res: { statusCode?: number; setHeader: (k: string, v: string) => void; end: (b?: string | Uint8Array) => void }) => {
@@ -134,5 +153,5 @@ export function registerRoutes(
134
153
  res.end()
135
154
  }
136
155
  },
137
- }), 'edrv: /edrv/vendor prefix')
156
+ }, '/edrv/vendor 前缀路由'), 'edrv: routes')
138
157
  }
package/src/rpc.ts CHANGED
@@ -27,6 +27,7 @@ import { restoreFile, revertCall, revertHunk } from './revert.js'
27
27
  import { listMcp, refreshMcp, removeMcp, saveMcp, toggleMcp } from './mcp.js'
28
28
  import { listProjects, projectRefresh, projectRemove, projectSave, projectToggle } from './mcpProject.js'
29
29
  import { normalizeFileOpenTool, FILE_OPEN_DEFAULT, FILE_OPEN_SETTINGS_NS } from './fileOpenSettings.js'
30
+ import { buildReport } from './compat.js'
30
31
 
31
32
  /** cwd → Promise 链:串行化 debug 日志追加(fs read+write 非原子,避免并发丢行)。 */
32
33
  const debugWriteQueues = new Map<string, Promise<void>>()
@@ -322,6 +323,7 @@ export function buildHandlers(ctx: Ctx, registry: Registry, searcher = newSearch
322
323
  } catch (error) { return { ok: false, error: String(error) }
323
324
  }
324
325
  },
326
+ 'compat': async () => ({ ok: true, report: await buildReport(ctx) }),
325
327
  }
326
328
  }
327
329
 
@@ -0,0 +1,20 @@
1
+ /**
2
+ * dsh-vscode-mode 兼容层双面契约 — 与其他插件 / DSH 版本的探测与自诊断报告。
3
+ * Purity rule: no node/react imports (same as types.ts / rpc.ts)。
4
+ * 作者 ddj 2026-08-24
5
+ */
6
+
7
+ /** 单项适配状态:外部插件/服务或护栏项,active 表示健康/生效。 */
8
+ export interface CompatAdapter {
9
+ name: string
10
+ active: boolean
11
+ note?: string
12
+ }
13
+
14
+ /** 兼容性报告:host 侧构建(RPC edrv.compat 返回),client 侧补充本地适配项后展示。 */
15
+ export interface CompatReport {
16
+ pluginVersion: string
17
+ external: CompatAdapter[]
18
+ guards: CompatAdapter[]
19
+ warnings: string[]
20
+ }
package/src/shared/rpc.ts CHANGED
@@ -13,6 +13,7 @@ import type {
13
13
  StaleHunk,
14
14
  } from './types.js'
15
15
  import type { MpcConfig, MpcProject, MpcProjectSaveInput, MpcServer } from './mcp.js'
16
+ import type { CompatReport } from './compat.js'
16
17
 
17
18
  /** webServer 精确路由。 */
18
19
  export const RPC_PATH = '/edrv/rpc'
@@ -45,6 +46,7 @@ export interface RpcRequestMap {
45
46
  'mcp.projectRefresh': { workspacePath: string; serverName: string }
46
47
  'vscode.fileOpenSettingsGet': {}
47
48
  'vscode.fileOpenSettingsUpdate': { fileOpenTool: string; expectedRevision?: number }
49
+ 'compat': {}
48
50
  }
49
51
 
50
52
  export type RpcMethod = keyof RpcRequestMap
@@ -74,6 +76,7 @@ export interface RpcOkMap {
74
76
  'mcp.projectRefresh': { project: MpcProject }
75
77
  'vscode.fileOpenSettingsGet': { fileOpenTool: string; revision?: number }
76
78
  'vscode.fileOpenSettingsUpdate': { fileOpenTool: string; revision?: number }
79
+ 'compat': { report: CompatReport }
77
80
  }
78
81
 
79
82
  /** 统一响应:{ok:true, ...payload} 或 {ok:false, error}。 */