dsh-vscode-mode 0.1.16 → 0.1.17
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/README.md +17 -0
- package/lib/client.js +31 -4
- package/lib/client.js.map +1 -1
- package/lib/index.js +168 -3
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
- package/src/client/styles/mcp.css +9 -0
- package/src/client/ui/McpSettings.ts +32 -1
- package/src/compat.ts +3 -2
- package/src/devForm.ts +133 -0
- package/src/rpc.ts +11 -0
- package/src/shared/compat.ts +7 -0
- package/src/shared/rpc.ts +5 -1
package/package.json
CHANGED
|
@@ -101,3 +101,12 @@
|
|
|
101
101
|
.vsm-compat-item.warn .vsm-compat-name::before{content:"● ";color:var(--dsw-alias-state-warn-primary,#e5b93f)}
|
|
102
102
|
.vsm-compat-name{font-weight:500;min-width:0}
|
|
103
103
|
.vsm-compat-note{color:var(--dsw-alias-label-tertiary,#9aa5b1);font-size:12px;text-align:right;min-width:0}
|
|
104
|
+
|
|
105
|
+
/* 开发形态开关 */
|
|
106
|
+
.vsm-devform-note{display:block;margin-top:4px;font-size:11px;color:var(--dsw-alias-label-tertiary,#9aa5b1);word-break:break-all}
|
|
107
|
+
.vsm-devform-actions{display:flex;flex-direction:column;align-items:flex-end;gap:6px;min-width:0}
|
|
108
|
+
.vsm-devform-msg{color:var(--dsw-alias-state-warn-primary,#e5b93f);font-size:11px;text-align:right}
|
|
109
|
+
.vsm-general-row button{border:1px solid var(--dsw-alias-border-l2,#555);border-radius:6px;padding:6px 12px;background:none;color:var(--dsw-alias-label-primary,#eee);cursor:pointer;font-size:12px;white-space:nowrap}
|
|
110
|
+
.vsm-general-row button:hover:not(:disabled){background:var(--dsw-alias-interactive-bg-hover,#ffffff14)}
|
|
111
|
+
.vsm-general-row button:disabled{opacity:.55;cursor:default}
|
|
112
|
+
.vsm-general-row button.vsm-devform-close{color:var(--dsw-alias-state-error-primary,#f07878);border-color:var(--dsw-alias-state-error-secondary,#d9534f55)}
|
|
@@ -119,11 +119,14 @@ function ProjectGroup({ project, busy, onAdd, onRefresh, onToggle, onRemove }) {
|
|
|
119
119
|
return React.createElement('section', { className: 'vsm-project' }, head, body)
|
|
120
120
|
}
|
|
121
121
|
|
|
122
|
-
/**
|
|
122
|
+
/** 通用设置:选择当前对话文件链接的打开器 + 开发形态开关。 */
|
|
123
123
|
function GeneralSettings({ registry }) {
|
|
124
124
|
const [tool, setTool] = React.useState(AUTO_OPEN_TOOL)
|
|
125
125
|
const [busy, setBusy] = React.useState(false)
|
|
126
126
|
const [error, setError] = React.useState('')
|
|
127
|
+
const [devForm, setDevForm] = React.useState(null)
|
|
128
|
+
const [devBusy, setDevBusy] = React.useState(false)
|
|
129
|
+
const [devMessage, setDevMessage] = React.useState('')
|
|
127
130
|
const settings = React.useContext(SettingsContext)
|
|
128
131
|
const snapshot = settings?.getSnapshot?.()
|
|
129
132
|
const loading = !snapshot || snapshot.status === 'loading'
|
|
@@ -137,19 +140,42 @@ function GeneralSettings({ registry }) {
|
|
|
137
140
|
onChange()
|
|
138
141
|
return settings?.subscribe?.(onChange)
|
|
139
142
|
}, [settings])
|
|
143
|
+
React.useEffect(() => {
|
|
144
|
+
rpc('vscode.devFormGet', {}).then((result) => {
|
|
145
|
+
if (result.ok) setDevForm(result.devForm)
|
|
146
|
+
}).catch(() => { /* 开发形态查询失败静默 */ })
|
|
147
|
+
}, [])
|
|
140
148
|
const save = (value) => {
|
|
141
149
|
setTool(value); setBusy(true); setError('')
|
|
142
150
|
if (!settings?.set) { setError('设置服务不可用'); setBusy(false); return }
|
|
143
151
|
settings.set('fileOpenTool', value).catch((e) => setError(String(e))).finally(() => setBusy(false))
|
|
144
152
|
}
|
|
153
|
+
const closeDevForm = () => {
|
|
154
|
+
if (!window.confirm('关闭开发形态:插件将切换为正式版安装(版本依赖 + 删除工作区链接),pnpm 装配后需重启 DSH 生效。确认关闭?')) return
|
|
155
|
+
setDevBusy(true)
|
|
156
|
+
setDevMessage('')
|
|
157
|
+
rpc('vscode.devFormSet', { enabled: false }).then((result) => {
|
|
158
|
+
if (!result.ok) { setDevMessage('关闭失败:' + String(result.error)); return }
|
|
159
|
+
setDevForm(result.devForm)
|
|
160
|
+
setDevMessage(result.restart ? '已切换为正式版,重启 DSH 后生效。' : '已关闭开发形态。')
|
|
161
|
+
}).catch((e) => setDevMessage('关闭失败:' + String(e))).finally(() => setDevBusy(false))
|
|
162
|
+
}
|
|
145
163
|
const options = [{ id: AUTO_OPEN_TOOL, label: '自动选择', description: '按当前已注册工具自动选择' }, ...availableOpeners(registry).map((item) => ({ id: item.id, label: item.label, description: item.description }))]
|
|
146
164
|
const currentOption = options.some((item) => item.id === tool) ? tool : AUTO_OPEN_TOOL
|
|
165
|
+
const devFormRow = devForm?.enabled ? React.createElement('div', { className: 'vsm-general-row' },
|
|
166
|
+
React.createElement('span', null, '开发形态', React.createElement('small', { className: 'vsm-devform-note' }, '工作区链接安装:' + devForm.path)),
|
|
167
|
+
React.createElement('div', { className: 'vsm-devform-actions' },
|
|
168
|
+
devMessage && React.createElement('small', { className: 'vsm-devform-msg' }, devMessage),
|
|
169
|
+
React.createElement('button', { className: 'vsm-devform-close', disabled: devBusy, onClick: closeDevForm }, devBusy ? '切换中…' : '关闭开发形态'),
|
|
170
|
+
),
|
|
171
|
+
) : null
|
|
147
172
|
return React.createElement('section', { className: 'vsm-general-page' },
|
|
148
173
|
React.createElement('h2', null, '通用'),
|
|
149
174
|
React.createElement('p', null, '配置 DSH 对话中文件链接的打开工具。工具由当前运行时自动扫描。'),
|
|
150
175
|
unavailable && React.createElement('div', { className: 'vsm-mcp-error vsm-mcp-banner' }, '设置服务暂不可用,当前使用自动选择。'),
|
|
151
176
|
error && React.createElement('div', { className: 'vsm-mcp-error vsm-mcp-banner' }, error),
|
|
152
177
|
React.createElement('label', { className: 'vsm-general-row' }, React.createElement('span', null, '文件链接使用工具'), React.createElement('select', { value: currentOption, disabled: loading || unavailable || notReady || busy || snapshot?.writable === false, onChange: (event) => save(event.target.value) }, options.map((item) => React.createElement('option', { key: item.id, value: item.id }, item.label))), loading && React.createElement('small', null, '读取中…')),
|
|
178
|
+
devFormRow,
|
|
153
179
|
)
|
|
154
180
|
}
|
|
155
181
|
|
|
@@ -171,10 +197,15 @@ function CompatSection({ getSummary }) {
|
|
|
171
197
|
React.createElement('span', { className: 'vsm-compat-note' }, item.note ?? (item.active ? '正常' : '未生效')),
|
|
172
198
|
))
|
|
173
199
|
const warnings = report?.warnings ?? []
|
|
200
|
+
const devForm = report?.devForm
|
|
174
201
|
return React.createElement('section', { className: 'vsm-general-page' },
|
|
175
202
|
React.createElement('h2', null, '兼容性'),
|
|
176
203
|
React.createElement('p', null, '检测与其他插件 / DSH 版本的适配状态与已知问题。' + (report?.pluginVersion ? '(插件版本 ' + report.pluginVersion + ')' : '')),
|
|
177
204
|
error && React.createElement('div', { className: 'vsm-mcp-error vsm-mcp-banner' }, error),
|
|
205
|
+
devForm?.enabled && React.createElement(React.Fragment, null,
|
|
206
|
+
React.createElement('h3', { className: 'vsm-compat-h3' }, '开发形态'),
|
|
207
|
+
React.createElement('div', { className: 'vsm-compat-item ok' }, React.createElement('span', { className: 'vsm-compat-name' }, '开发形态开启(工作区链接安装)'), React.createElement('span', { className: 'vsm-compat-note' }, devForm.path)),
|
|
208
|
+
),
|
|
178
209
|
React.createElement('h3', { className: 'vsm-compat-h3' }, '外部插件与服务'),
|
|
179
210
|
renderItems(report?.external),
|
|
180
211
|
React.createElement('h3', { className: 'vsm-compat-h3' }, '客户端适配'),
|
package/src/compat.ts
CHANGED
|
@@ -11,6 +11,7 @@ import { dirname, join } from 'node:path'
|
|
|
11
11
|
import { fileURLToPath } from 'node:url'
|
|
12
12
|
import { entriesOf } from './mcp.js'
|
|
13
13
|
import { loadSettingsDeps } from './fileOpenSettings.js'
|
|
14
|
+
import { readDevForm } from './devForm.js'
|
|
14
15
|
import type { CompatAdapter, CompatReport } from './shared/compat.js'
|
|
15
16
|
import type { Ctx } from './store.js'
|
|
16
17
|
|
|
@@ -36,7 +37,7 @@ export function entryHash(id: string): string | undefined {
|
|
|
36
37
|
}
|
|
37
38
|
|
|
38
39
|
/** 读取随包 package.json 版本号(缺失/解析失败降级为空串)。 */
|
|
39
|
-
function pluginVersionOf(): string {
|
|
40
|
+
export function pluginVersionOf(): string {
|
|
40
41
|
try {
|
|
41
42
|
const file = join(dirname(fileURLToPath(import.meta.url)), '..', 'package.json')
|
|
42
43
|
const pkg = JSON.parse(readFileSync(file, 'utf8')) as { version?: unknown }
|
|
@@ -142,5 +143,5 @@ export async function buildReport(
|
|
|
142
143
|
for (const guard of guards) {
|
|
143
144
|
if (!guard.active && guard.note) warnings.push(guard.note)
|
|
144
145
|
}
|
|
145
|
-
return { pluginVersion: options?.version ?? pluginVersionOf(), external, guards, warnings }
|
|
146
|
+
return { pluginVersion: options?.version ?? pluginVersionOf(), external, guards, warnings, devForm: readDevForm() }
|
|
146
147
|
}
|
package/src/devForm.ts
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* dsh-vscode-mode host — 开发形态管理(安装形态切换)。
|
|
3
|
+
* 开发形态 = profile 中以 link: 依赖 + junction 指向工作区的安装(用于插件开发);
|
|
4
|
+
* 关闭 = 改回版本依赖 + 删 junction + pnpm install 装配正式版。
|
|
5
|
+
* 切换后需重启 DSH 生效(运行中的代码仍是旧形态)。
|
|
6
|
+
* 与 compat.ts 存在模块环(devForm 用 pluginVersionOf、compat 用 readDevForm),
|
|
7
|
+
* 双方仅在函数体内交叉使用,模块求值期无依赖,ESM 安全。
|
|
8
|
+
* 作者 ddj 2026-08-24
|
|
9
|
+
*/
|
|
10
|
+
import { existsSync, readFileSync, readdirSync, rmSync, symlinkSync, writeFileSync } from 'node:fs'
|
|
11
|
+
import { dirname, join } from 'node:path'
|
|
12
|
+
import { homedir } from 'node:os'
|
|
13
|
+
import { PLUGIN_NAME, pluginVersionOf } from './compat.js'
|
|
14
|
+
import type { DevFormInfo } from './shared/compat.js'
|
|
15
|
+
import type { Ctx } from './store.js'
|
|
16
|
+
|
|
17
|
+
/** 扫描 $DSH_HOME/profiles 找到依赖本插件的 profile 目录(无 env 依赖,多 profile 安全)。 */
|
|
18
|
+
export function findProfileDir(): string | undefined {
|
|
19
|
+
const home = process.env.DSH_HOME ?? join(homedir(), '.dsh')
|
|
20
|
+
try {
|
|
21
|
+
for (const name of readdirSync(join(home, 'profiles'))) {
|
|
22
|
+
const pkgFile = join(home, 'profiles', name, 'package.json')
|
|
23
|
+
if (!existsSync(pkgFile)) continue
|
|
24
|
+
try {
|
|
25
|
+
const pkg = JSON.parse(readFileSync(pkgFile, 'utf8')) as { dependencies?: Record<string, unknown> } | undefined
|
|
26
|
+
if (pkg?.dependencies?.[PLUGIN_NAME]) return dirname(pkgFile)
|
|
27
|
+
} catch {
|
|
28
|
+
/* 坏 manifest 跳过该 profile */
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
/* profiles 目录不存在时放弃 */
|
|
33
|
+
}
|
|
34
|
+
return undefined
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* 读取当前开发形态(manifest link: 判定)。
|
|
39
|
+
* @author ddj 2026年08月24号
|
|
40
|
+
* @returns 开发形态状态
|
|
41
|
+
*/
|
|
42
|
+
export function readDevForm(): DevFormInfo {
|
|
43
|
+
const profileDir = findProfileDir()
|
|
44
|
+
if (!profileDir) return { enabled: false }
|
|
45
|
+
try {
|
|
46
|
+
const text = readFileSync(join(profileDir, 'package.json'), 'utf8').replace(/^\uFEFF/, '')
|
|
47
|
+
const pkg = JSON.parse(text) as { dependencies?: Record<string, unknown> } | undefined
|
|
48
|
+
const spec = pkg?.dependencies?.[PLUGIN_NAME]
|
|
49
|
+
if (typeof spec === 'string' && spec.startsWith('link:')) {
|
|
50
|
+
return { enabled: true, path: spec.slice('link:'.length) }
|
|
51
|
+
}
|
|
52
|
+
} catch {
|
|
53
|
+
/* manifest 不可读视为非开发形态 */
|
|
54
|
+
}
|
|
55
|
+
return { enabled: false }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* 规划切换后的 manifest 文本(纯函数,可单测;非法 JSON 抛错)。
|
|
60
|
+
* @author ddj 2026年08月24号
|
|
61
|
+
* @param text 原 manifest 文本
|
|
62
|
+
* @param enabled 目标形态(true=link:,false=版本)
|
|
63
|
+
* @param path 工作区路径(开启时必填)
|
|
64
|
+
* @param version 版本号(关闭时使用,写入 ^<version>)
|
|
65
|
+
* @returns 新 manifest 文本(无 BOM,2 空格缩进)
|
|
66
|
+
*/
|
|
67
|
+
export function planManifest(text: string, enabled: boolean, path: string, version: string): string {
|
|
68
|
+
const data = JSON.parse(text.replace(/^\uFEFF/, '')) as { dependencies?: Record<string, unknown> }
|
|
69
|
+
if (!data.dependencies || typeof data.dependencies !== 'object') {
|
|
70
|
+
throw new Error('profile package.json 缺少 dependencies 字段')
|
|
71
|
+
}
|
|
72
|
+
data.dependencies[PLUGIN_NAME] = enabled ? 'link:' + path.replace(/\\/g, '/') : '^' + version
|
|
73
|
+
return JSON.stringify(data, null, 2) + '\n'
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** 建立或移除 node_modules 下的开发 junction(目标不存在时开启失败)。 */
|
|
77
|
+
function applyJunction(profileDir: string, enabled: boolean, target: string): void {
|
|
78
|
+
const linkDir = join(profileDir, 'node_modules', PLUGIN_NAME)
|
|
79
|
+
if (existsSync(linkDir)) rmSync(linkDir, { recursive: true, force: true })
|
|
80
|
+
if (!enabled) return
|
|
81
|
+
if (!existsSync(target)) throw new Error('工作区路径不存在:' + target)
|
|
82
|
+
symlinkSync(target, linkDir, 'junction')
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* 切换开发形态:改 manifest → 建/删 junction → pnpm install(subprocess)。
|
|
87
|
+
* @author ddj 2026年08月24号
|
|
88
|
+
* @param ctx DSH host 上下文(subprocess 服务)
|
|
89
|
+
* @param enabled 目标形态
|
|
90
|
+
* @param path 工作区绝对路径(开启时必填)
|
|
91
|
+
* @returns 切换结果(restart=true 表示需重启 DSH 生效)
|
|
92
|
+
*/
|
|
93
|
+
export async function setDevForm(ctx: Ctx, enabled: boolean, path?: string): Promise<{ ok: boolean; error?: string; restart: boolean }> {
|
|
94
|
+
const profileDir = findProfileDir()
|
|
95
|
+
if (!profileDir) return { ok: false, error: '未找到依赖本插件的 profile(检查 DSH_HOME/profiles)', restart: false }
|
|
96
|
+
const current = readDevForm()
|
|
97
|
+
const target = (path ?? current.path ?? '').replace(/\\/g, '/')
|
|
98
|
+
if (enabled && !target) return { ok: false, error: '开启开发形态需要提供工作区路径(path 参数)', restart: false }
|
|
99
|
+
if (enabled === current.enabled && (!enabled || target === current.path)) {
|
|
100
|
+
return { ok: true, restart: false }
|
|
101
|
+
}
|
|
102
|
+
try {
|
|
103
|
+
const pkgFile = join(profileDir, 'package.json')
|
|
104
|
+
const version = pluginVersionOf()
|
|
105
|
+
if (!version) return { ok: false, error: '读取插件版本失败,无法写入版本依赖', restart: false }
|
|
106
|
+
const next = planManifest(readFileSync(pkgFile, 'utf8').replace(/^\uFEFF/, ''), enabled, target, version)
|
|
107
|
+
writeFileSync(pkgFile, next, 'utf8')
|
|
108
|
+
applyJunction(profileDir, enabled, target)
|
|
109
|
+
const installError = await runPnpmInstall(ctx, profileDir)
|
|
110
|
+
return { ok: !installError, error: installError, restart: true }
|
|
111
|
+
} catch (error) {
|
|
112
|
+
return { ok: false, error: String(error), restart: false }
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** 在 profile 目录执行 pnpm install(subprocess 契约:spawn({argv,cwd,stdio,graceMs}))。 */
|
|
117
|
+
async function runPnpmInstall(ctx: Ctx, profileDir: string): Promise<string | undefined> {
|
|
118
|
+
const sub = ctx.get('subprocess')
|
|
119
|
+
if (!sub || typeof sub.spawn !== 'function') return 'manifest/junction 已更新,但缺少 subprocess:请手动在 profile 目录运行 pnpm install'
|
|
120
|
+
const handle = sub.spawn({
|
|
121
|
+
argv: ['pnpm', 'install'],
|
|
122
|
+
cwd: profileDir,
|
|
123
|
+
stdio: { stdout: { maxBytes: 1 << 16 }, stderr: { maxBytes: 1 << 16 }, stdin: 'ignore' },
|
|
124
|
+
graceMs: 120000,
|
|
125
|
+
})
|
|
126
|
+
const outcome = await handle.done
|
|
127
|
+
const code = outcome?.exitCode ?? outcome?.code
|
|
128
|
+
if (code !== 0) {
|
|
129
|
+
const stderr = handle.collected?.stderr?.readFrom(0).text ?? ''
|
|
130
|
+
return 'pnpm install 失败(exit ' + code + '):' + stderr.slice(0, 500)
|
|
131
|
+
}
|
|
132
|
+
return undefined
|
|
133
|
+
}
|
package/src/rpc.ts
CHANGED
|
@@ -28,6 +28,7 @@ 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
30
|
import { buildReport } from './compat.js'
|
|
31
|
+
import { readDevForm, setDevForm } from './devForm.js'
|
|
31
32
|
|
|
32
33
|
/** cwd → Promise 链:串行化 debug 日志追加(fs read+write 非原子,避免并发丢行)。 */
|
|
33
34
|
const debugWriteQueues = new Map<string, Promise<void>>()
|
|
@@ -324,6 +325,16 @@ export function buildHandlers(ctx: Ctx, registry: Registry, searcher = newSearch
|
|
|
324
325
|
}
|
|
325
326
|
},
|
|
326
327
|
'compat': async () => ({ ok: true, report: await buildReport(ctx) }),
|
|
328
|
+
'vscode.devFormGet': async () => ({ ok: true, devForm: readDevForm() }),
|
|
329
|
+
'vscode.devFormSet': async (args) => {
|
|
330
|
+
try {
|
|
331
|
+
const result = await setDevForm(ctx, args.enabled === true, args.path)
|
|
332
|
+
if (!result.ok) return { ok: false, error: result.error ?? '切换开发形态失败' }
|
|
333
|
+
return { ok: true, devForm: readDevForm(), restart: result.restart }
|
|
334
|
+
} catch (error) {
|
|
335
|
+
return { ok: false, error: String(error) }
|
|
336
|
+
}
|
|
337
|
+
},
|
|
327
338
|
}
|
|
328
339
|
}
|
|
329
340
|
|
package/src/shared/compat.ts
CHANGED
|
@@ -11,10 +11,17 @@ export interface CompatAdapter {
|
|
|
11
11
|
note?: string
|
|
12
12
|
}
|
|
13
13
|
|
|
14
|
+
/** 开发形态信息:profile 中以 link: 依赖 + junction 指向工作区的安装形态。 */
|
|
15
|
+
export interface DevFormInfo {
|
|
16
|
+
enabled: boolean
|
|
17
|
+
path?: string
|
|
18
|
+
}
|
|
19
|
+
|
|
14
20
|
/** 兼容性报告:host 侧构建(RPC edrv.compat 返回),client 侧补充本地适配项后展示。 */
|
|
15
21
|
export interface CompatReport {
|
|
16
22
|
pluginVersion: string
|
|
17
23
|
external: CompatAdapter[]
|
|
18
24
|
guards: CompatAdapter[]
|
|
19
25
|
warnings: string[]
|
|
26
|
+
devForm?: DevFormInfo
|
|
20
27
|
}
|
package/src/shared/rpc.ts
CHANGED
|
@@ -13,7 +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
|
+
import type { CompatReport, DevFormInfo } from './compat.js'
|
|
17
17
|
|
|
18
18
|
/** webServer 精确路由。 */
|
|
19
19
|
export const RPC_PATH = '/edrv/rpc'
|
|
@@ -46,6 +46,8 @@ export interface RpcRequestMap {
|
|
|
46
46
|
'mcp.projectRefresh': { workspacePath: string; serverName: string }
|
|
47
47
|
'vscode.fileOpenSettingsGet': {}
|
|
48
48
|
'vscode.fileOpenSettingsUpdate': { fileOpenTool: string; expectedRevision?: number }
|
|
49
|
+
'vscode.devFormGet': {}
|
|
50
|
+
'vscode.devFormSet': { enabled: boolean; path?: string }
|
|
49
51
|
'compat': {}
|
|
50
52
|
}
|
|
51
53
|
|
|
@@ -76,6 +78,8 @@ export interface RpcOkMap {
|
|
|
76
78
|
'mcp.projectRefresh': { project: MpcProject }
|
|
77
79
|
'vscode.fileOpenSettingsGet': { fileOpenTool: string; revision?: number }
|
|
78
80
|
'vscode.fileOpenSettingsUpdate': { fileOpenTool: string; revision?: number }
|
|
81
|
+
'vscode.devFormGet': { devForm: DevFormInfo }
|
|
82
|
+
'vscode.devFormSet': { devForm: DevFormInfo; restart: boolean }
|
|
79
83
|
'compat': { report: CompatReport }
|
|
80
84
|
}
|
|
81
85
|
|