dingtalk-ask-mcp-server 0.1.2 → 0.1.3
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 +1 -0
- package/dist/installCli.js +59 -2
- package/dist/installConfig.js +59 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -138,6 +138,7 @@ Codex 若 `approval_policy="never"` 本就不弹命令审批,故只提供 `ask_h
|
|
|
138
138
|
|
|
139
139
|
```bash
|
|
140
140
|
dingtalk-ask install … # 一键接入(见上)
|
|
141
|
+
dingtalk-ask uninstall [--host …] # 一键卸载:删 MCP+钩子(CC/Codex) + 清运行时(config.toml 先备份 .bak)
|
|
141
142
|
dingtalk-ask info # 查看配置与进程状态(守护进程/离开模式/各宿主是否配好)
|
|
142
143
|
dingtalk-ask daemon status|stop # 查看/停止共享守护进程
|
|
143
144
|
dingtalk-ask away on|off|status # 本地切换离开模式
|
package/dist/installCli.js
CHANGED
|
@@ -21,7 +21,7 @@ import { mergeHook, permissionHookPath, resolveSettingsPath } from './installHoo
|
|
|
21
21
|
import { daemonLockPath, readDaemonLock, removeDaemonLock } from './daemon/lock.js';
|
|
22
22
|
import { probeHealth } from './daemonClient.js';
|
|
23
23
|
import { isAway, setAway, awayFlagPath } from './awayState.js';
|
|
24
|
-
import { buildMcpEntry, mergeMcpServers, ensureMcpToolTimeout, codexHasDingtalk, buildCodexBlock, inspectMcpJson, inspectSettingsHook, MCP_SERVER_NAME, } from './installConfig.js';
|
|
24
|
+
import { buildMcpEntry, mergeMcpServers, ensureMcpToolTimeout, codexHasDingtalk, buildCodexBlock, inspectMcpJson, inspectSettingsHook, removeMcpServer, removeHook, removeCodexBlock, MCP_SERVER_NAME, } from './installConfig.js';
|
|
25
25
|
/** 解析目标宿主。默认 CC;Codex 见 resolveCodexTarget。 */
|
|
26
26
|
function parseHost(argv) {
|
|
27
27
|
const i = argv.indexOf('--host');
|
|
@@ -99,6 +99,7 @@ function usage() {
|
|
|
99
99
|
console.log('用法:');
|
|
100
100
|
console.log(' dingtalk-ask install --client-id … --client-secret … --robot-code … --user-id … \\');
|
|
101
101
|
console.log(' [--timeout <ms>] [--host cc|codex|both] # 一键装 MCP+钩子');
|
|
102
|
+
console.log(' dingtalk-ask uninstall [--host cc|codex|both] # 一键删 MCP+钩子 + 清运行时');
|
|
102
103
|
console.log(' dingtalk-ask daemon <status|stop> # 查看/停止共享守护进程');
|
|
103
104
|
console.log(' dingtalk-ask away <on|off|status> # 本地切换离开模式');
|
|
104
105
|
console.log(' dingtalk-ask info # 查看配置与进程状态');
|
|
@@ -201,6 +202,9 @@ function main() {
|
|
|
201
202
|
if (cmd === 'install') {
|
|
202
203
|
runInstall(rest);
|
|
203
204
|
}
|
|
205
|
+
else if (cmd === 'uninstall') {
|
|
206
|
+
runUninstall(rest);
|
|
207
|
+
}
|
|
204
208
|
else if (cmd === 'daemon') {
|
|
205
209
|
void runDaemon(rest);
|
|
206
210
|
}
|
|
@@ -218,7 +222,8 @@ function main() {
|
|
|
218
222
|
}
|
|
219
223
|
}
|
|
220
224
|
main();
|
|
221
|
-
|
|
225
|
+
/** 清理运行时状态(停守护进程 + 删锁/离开flag/就绪标记/日志)。clean 与 uninstall 共用。 */
|
|
226
|
+
function cleanRuntime() {
|
|
222
227
|
const tmp = tmpdir();
|
|
223
228
|
// 1. 停守护进程 + 删锁。
|
|
224
229
|
const lock = readDaemonLock(daemonLockPath());
|
|
@@ -269,8 +274,60 @@ function runClean() {
|
|
|
269
274
|
catch {
|
|
270
275
|
/* 忽略 */
|
|
271
276
|
}
|
|
277
|
+
}
|
|
278
|
+
/** clean 子命令:只清运行时状态,不动配置文件。 */
|
|
279
|
+
function runClean() {
|
|
280
|
+
cleanRuntime();
|
|
272
281
|
console.log('✅ 已清空运行时状态。配置文件(.mcp.json / settings.json / config.toml)未改动。');
|
|
273
282
|
}
|
|
283
|
+
/** uninstall 子命令:删 MCP + 钩子(CC/Codex) + 清运行时状态(install 的逆操作)。 */
|
|
284
|
+
function runUninstall(rest) {
|
|
285
|
+
const cwd = process.cwd();
|
|
286
|
+
const host = parseHost(rest);
|
|
287
|
+
const doCc = host === undefined || host === 'cc' || host === 'both';
|
|
288
|
+
const doCodex = host === 'codex' || host === 'both' || (host === undefined && existsSync(join(homedir(), '.codex')));
|
|
289
|
+
if (doCc) {
|
|
290
|
+
const mcpPath = join(cwd, '.mcp.json');
|
|
291
|
+
const mcp = removeMcpServer(tolerantJson(mcpPath), MCP_SERVER_NAME);
|
|
292
|
+
if (mcp.removed) {
|
|
293
|
+
writeJson(mcpPath, mcp.settings);
|
|
294
|
+
console.log(`· [CC] 从 ${mcpPath} 移除 MCP server`);
|
|
295
|
+
}
|
|
296
|
+
else {
|
|
297
|
+
console.log(`· [CC] ${mcpPath} 无 dingtalk-ask,跳过`);
|
|
298
|
+
}
|
|
299
|
+
const settingsPath = resolveSettingsPath(rest.includes('--global') ? ['--global'] : [], cwd, homedir());
|
|
300
|
+
const hookRes = removeHook(tolerantJson(settingsPath));
|
|
301
|
+
if (hookRes.removed) {
|
|
302
|
+
writeJson(settingsPath, hookRes.settings);
|
|
303
|
+
console.log(`· [CC] 从 ${settingsPath} 移除命令审批钩子(MCP_TOOL_TIMEOUT 保留,可能被其它 MCP 用)`);
|
|
304
|
+
}
|
|
305
|
+
else {
|
|
306
|
+
console.log(`· [CC] ${settingsPath} 无本工具钩子,跳过`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
if (doCodex) {
|
|
310
|
+
const cfgPath = join(homedir(), '.codex', 'config.toml');
|
|
311
|
+
let content = '';
|
|
312
|
+
try {
|
|
313
|
+
content = readFileSync(cfgPath, 'utf8');
|
|
314
|
+
}
|
|
315
|
+
catch {
|
|
316
|
+
content = '';
|
|
317
|
+
}
|
|
318
|
+
const res = removeCodexBlock(content);
|
|
319
|
+
if (res.removed) {
|
|
320
|
+
copyFileSync(cfgPath, `${cfgPath}.bak`);
|
|
321
|
+
writeFileSync(cfgPath, res.content, 'utf8');
|
|
322
|
+
console.log(`· [Codex] 从 ${cfgPath} 移除 [mcp_servers.dingtalk-ask](已备份 .bak)`);
|
|
323
|
+
}
|
|
324
|
+
else {
|
|
325
|
+
console.log(`· [Codex] ${cfgPath} 无 dingtalk-ask,跳过`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
cleanRuntime();
|
|
329
|
+
console.log('\n✅ 已卸载。重启 Claude Code / 重开 Codex 会话后彻底生效。');
|
|
330
|
+
}
|
|
274
331
|
/** install 子命令:装 MCP + 钩子。 */
|
|
275
332
|
function runInstall(rest) {
|
|
276
333
|
let config;
|
package/dist/installConfig.js
CHANGED
|
@@ -80,6 +80,65 @@ export function inspectSettingsHook(obj) {
|
|
|
80
80
|
const t = env?.MCP_TOOL_TIMEOUT;
|
|
81
81
|
return { hook, mcpToolTimeout: typeof t === 'string' ? t : undefined };
|
|
82
82
|
}
|
|
83
|
+
/** 从 .mcp.json 对象移除某 server(纯函数)。 */
|
|
84
|
+
export function removeMcpServer(obj, name) {
|
|
85
|
+
const servers = obj.mcpServers;
|
|
86
|
+
if (servers === undefined || !(name in servers)) {
|
|
87
|
+
return { settings: obj, removed: false };
|
|
88
|
+
}
|
|
89
|
+
const copy = { ...servers };
|
|
90
|
+
delete copy[name];
|
|
91
|
+
return { settings: { ...obj, mcpServers: copy }, removed: true };
|
|
92
|
+
}
|
|
93
|
+
/** 从 settings.json 对象移除本工具的命令审批钩子(纯函数,不动 MCP_TOOL_TIMEOUT)。 */
|
|
94
|
+
export function removeHook(obj) {
|
|
95
|
+
const hooks = obj.hooks;
|
|
96
|
+
const list = hooks?.PermissionRequest;
|
|
97
|
+
if (!Array.isArray(list)) {
|
|
98
|
+
return { settings: obj, removed: false };
|
|
99
|
+
}
|
|
100
|
+
const kept = list.filter((e) => !(Array.isArray(e.hooks) && e.hooks.some((h) => typeof h.command === 'string' && h.command.includes('permissionHook.js'))));
|
|
101
|
+
if (kept.length === list.length) {
|
|
102
|
+
return { settings: obj, removed: false };
|
|
103
|
+
}
|
|
104
|
+
const newHooks = { ...hooks };
|
|
105
|
+
if (kept.length === 0) {
|
|
106
|
+
delete newHooks.PermissionRequest;
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
newHooks.PermissionRequest = kept;
|
|
110
|
+
}
|
|
111
|
+
return { settings: { ...obj, hooks: newHooks }, removed: true };
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* 从 Codex config.toml 文本移除 dingtalk-ask 的两个 section(纯函数)。
|
|
115
|
+
*
|
|
116
|
+
* 逐行扫描:遇到 `[mcp_servers.dingtalk-ask]` 或 `[...dingtalk-ask.env]` 段头即进入跳过,
|
|
117
|
+
* 直到下一个 `[` 段头或文件末尾。
|
|
118
|
+
*
|
|
119
|
+
* @param content 原 config.toml 文本。
|
|
120
|
+
* @returns { content: 移除后文本, removed: 是否有删除 }。
|
|
121
|
+
*/
|
|
122
|
+
export function removeCodexBlock(content) {
|
|
123
|
+
const lines = content.split('\n');
|
|
124
|
+
const out = [];
|
|
125
|
+
let skipping = false;
|
|
126
|
+
let removed = false;
|
|
127
|
+
for (const line of lines) {
|
|
128
|
+
const trimmed = line.trim();
|
|
129
|
+
if (trimmed.startsWith('[')) {
|
|
130
|
+
skipping =
|
|
131
|
+
trimmed.startsWith(`[mcp_servers.${MCP_SERVER_NAME}]`) || trimmed.startsWith(`[mcp_servers.${MCP_SERVER_NAME}.env]`);
|
|
132
|
+
if (skipping) {
|
|
133
|
+
removed = true;
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (!skipping) {
|
|
137
|
+
out.push(line);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return { content: out.join('\n'), removed };
|
|
141
|
+
}
|
|
83
142
|
/**
|
|
84
143
|
* 构造 Codex config.toml 追加块(TOML 基本字符串转义,无需 TOML 库)。
|
|
85
144
|
*
|