@gird/ccli 2.10.0 → 2.11.0
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 -1
- package/dist/tools/bash-background.js +128 -0
- package/dist/tools/bash-background.js.map +1 -0
- package/dist/tools/bash-output.js +45 -0
- package/dist/tools/bash-output.js.map +1 -0
- package/dist/tools/bash.js +15 -3
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/index.js +6 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/kill-bash.js +41 -0
- package/dist/tools/kill-bash.js.map +1 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ ccli --role coder "review 这段代码" # 使用 coding 角色单次调用
|
|
|
37
37
|
进入对话后:
|
|
38
38
|
|
|
39
39
|
- 直接输入即与 AI 对话,模型会记住上文(多轮对话)。
|
|
40
|
-
- 模型可调用的工具:读文件、列目录、搜索代码、写文件、局部编辑文件、执行 shell
|
|
40
|
+
- 模型可调用的工具:读文件、列目录、搜索代码、写文件、局部编辑文件、执行 shell 命令(写入/编辑/执行操作会请你确认;shell 命令可用 `background: true` 放后台长跑,再用 `bash_output` 查看输出、`kill_bash` 终止)。
|
|
41
41
|
- `/help` 查看可用命令,`/clear` 清空历史,`/undo [n]` 撤销最近 n 轮对话。
|
|
42
42
|
- `/compact` 把对话历史压缩成一段摘要,释放上下文(长对话逼近上限时用;仅在确有收益时替换)。上下文占比达到约 80% 时也会**自动压缩**。
|
|
43
43
|
- `/model [id]` 查看当前模型或切换到新模型(输入空参数只查看)。
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 后台 bash 进程管理。
|
|
3
|
+
*
|
|
4
|
+
* 普通 bash 工具同步等待命令结束;长跑进程(dev server / watch / MCP server)
|
|
5
|
+
* 会因此卡住,直到超时被杀。这里把进程放到后台:spawn 后立即返回一个 id,
|
|
6
|
+
* 调用方可继续做事,随时用 bash_output 读取累计输出、查状态,用 kill_bash
|
|
7
|
+
* 主动终止。
|
|
8
|
+
*
|
|
9
|
+
* 进程表驻留在模块内存里,随进程退出而清空(不持久化)。
|
|
10
|
+
*/
|
|
11
|
+
import { spawn } from 'node:child_process';
|
|
12
|
+
/** 单个进程 stdout/stderr 各自保留的尾部上限,避免长跑输出撑爆内存。 */
|
|
13
|
+
const MAX_BUF = 30_000;
|
|
14
|
+
/** 被信号终止时换算成的退出码(shell 惯例:128 + 信号序号)。 */
|
|
15
|
+
const SIGNAL_EXIT = {
|
|
16
|
+
SIGINT: 130,
|
|
17
|
+
SIGTERM: 143,
|
|
18
|
+
SIGKILL: 137,
|
|
19
|
+
};
|
|
20
|
+
const processes = new Map();
|
|
21
|
+
let counter = 0;
|
|
22
|
+
function append(buf, chunk) {
|
|
23
|
+
const next = buf + chunk.toString();
|
|
24
|
+
// 只保留尾部:长跑进程最新输出在末尾最有价值。
|
|
25
|
+
return next.length > MAX_BUF ? next.slice(-MAX_BUF) : next;
|
|
26
|
+
}
|
|
27
|
+
function toInfo(p) {
|
|
28
|
+
return {
|
|
29
|
+
id: p.id,
|
|
30
|
+
command: p.command,
|
|
31
|
+
status: p.status,
|
|
32
|
+
exitCode: p.exitCode,
|
|
33
|
+
stdout: p.stdout,
|
|
34
|
+
stderr: p.stderr,
|
|
35
|
+
startedAt: p.startedAt,
|
|
36
|
+
endedAt: p.endedAt,
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
/** 后台启动一条命令,立即返回进程快照(不等待结束)。 */
|
|
40
|
+
export function startBackground(command) {
|
|
41
|
+
const id = `bg_${++counter}`;
|
|
42
|
+
const shell = process.env.SHELL || '/bin/bash';
|
|
43
|
+
const child = spawn(shell, ['-c', command], {
|
|
44
|
+
cwd: process.cwd(),
|
|
45
|
+
env: process.env,
|
|
46
|
+
});
|
|
47
|
+
const proc = {
|
|
48
|
+
id,
|
|
49
|
+
command,
|
|
50
|
+
status: 'running',
|
|
51
|
+
exitCode: null,
|
|
52
|
+
stdout: '',
|
|
53
|
+
stderr: '',
|
|
54
|
+
startedAt: Date.now(),
|
|
55
|
+
endedAt: null,
|
|
56
|
+
child,
|
|
57
|
+
};
|
|
58
|
+
processes.set(id, proc);
|
|
59
|
+
child.stdout?.on('data', (d) => {
|
|
60
|
+
proc.stdout = append(proc.stdout, d);
|
|
61
|
+
});
|
|
62
|
+
child.stderr?.on('data', (d) => {
|
|
63
|
+
proc.stderr = append(proc.stderr, d);
|
|
64
|
+
});
|
|
65
|
+
child.on('error', () => {
|
|
66
|
+
proc.status = 'error';
|
|
67
|
+
proc.endedAt = Date.now();
|
|
68
|
+
});
|
|
69
|
+
child.on('close', (code, signal) => {
|
|
70
|
+
proc.status = 'done';
|
|
71
|
+
proc.exitCode = code ?? (signal ? SIGNAL_EXIT[signal] ?? null : null);
|
|
72
|
+
proc.endedAt = Date.now();
|
|
73
|
+
});
|
|
74
|
+
return toInfo(proc);
|
|
75
|
+
}
|
|
76
|
+
/** 取单个进程快照。 */
|
|
77
|
+
export function getBackground(id) {
|
|
78
|
+
const p = processes.get(id);
|
|
79
|
+
return p ? toInfo(p) : undefined;
|
|
80
|
+
}
|
|
81
|
+
/** 列出全部后台进程快照。 */
|
|
82
|
+
export function listBackground() {
|
|
83
|
+
return [...processes.values()].map(toInfo);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* 终止指定后台进程:先发 SIGTERM 让其优雅退出,1 秒内仍未 close 再 SIGKILL 兜底。
|
|
87
|
+
* 返回 'killed'(已发信号)/ 'not_running'(已结束)/ 'not_found'(无此 id)。
|
|
88
|
+
*/
|
|
89
|
+
export function killBackground(id) {
|
|
90
|
+
const p = processes.get(id);
|
|
91
|
+
if (!p)
|
|
92
|
+
return 'not_found';
|
|
93
|
+
if (p.status !== 'running')
|
|
94
|
+
return 'not_running';
|
|
95
|
+
try {
|
|
96
|
+
p.child.kill('SIGTERM');
|
|
97
|
+
}
|
|
98
|
+
catch {
|
|
99
|
+
return 'not_running';
|
|
100
|
+
}
|
|
101
|
+
const timer = setTimeout(() => {
|
|
102
|
+
if (p.status === 'running') {
|
|
103
|
+
try {
|
|
104
|
+
p.child.kill('SIGKILL');
|
|
105
|
+
}
|
|
106
|
+
catch {
|
|
107
|
+
// 进程可能已退出,忽略。
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}, 1000);
|
|
111
|
+
// 不让兜底定时器单独阻止 Node 进程退出。
|
|
112
|
+
timer.unref?.();
|
|
113
|
+
return 'killed';
|
|
114
|
+
}
|
|
115
|
+
/** 仅供测试:杀掉所有后台进程并清空表,避免事件循环被句柄挂着不退出。 */
|
|
116
|
+
export function resetBackground() {
|
|
117
|
+
for (const p of processes.values()) {
|
|
118
|
+
try {
|
|
119
|
+
p.child.kill('SIGKILL');
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
// 进程可能已退出,忽略。
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
processes.clear();
|
|
126
|
+
counter = 0;
|
|
127
|
+
}
|
|
128
|
+
//# sourceMappingURL=bash-background.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-background.js","sourceRoot":"","sources":["../../src/tools/bash-background.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* bash_output 工具 —— 读取后台 bash 进程的累计输出与状态。
|
|
3
|
+
*
|
|
4
|
+
* 配合 `bash` 的 background: true 使用:后台启动拿到 id 后,随时用本工具
|
|
5
|
+
* 查看进程还在不在跑、有没有退出码、目前累计的 stdout/stderr。
|
|
6
|
+
* 只读,无副作用,不需要用户确认。
|
|
7
|
+
*/
|
|
8
|
+
import { getBackground, listBackground } from './bash-background.js';
|
|
9
|
+
function formatDurationMs(ms) {
|
|
10
|
+
return `${(ms / 1000).toFixed(1)}s`;
|
|
11
|
+
}
|
|
12
|
+
export const bashOutputTool = {
|
|
13
|
+
name: 'bash_output',
|
|
14
|
+
description: '查看后台 bash 进程的累计输出与状态。' +
|
|
15
|
+
'传入 bash 后台模式返回的 id;可反复调用以观察进展。' +
|
|
16
|
+
'只读操作,不会修改任何状态。',
|
|
17
|
+
parameters: {
|
|
18
|
+
type: 'object',
|
|
19
|
+
properties: {
|
|
20
|
+
id: {
|
|
21
|
+
type: 'string',
|
|
22
|
+
description: '后台进程 id(bash background 模式返回的 bg_1 之类)',
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
required: ['id'],
|
|
26
|
+
},
|
|
27
|
+
async run({ id }) {
|
|
28
|
+
const proc = getBackground(String(id || ''));
|
|
29
|
+
if (!proc) {
|
|
30
|
+
const ids = listBackground()
|
|
31
|
+
.map((p) => p.id)
|
|
32
|
+
.join(', ');
|
|
33
|
+
return `错误:未找到后台进程 ${id}。当前后台进程:${ids || '(无)'}`;
|
|
34
|
+
}
|
|
35
|
+
const ended = proc.endedAt ?? Date.now();
|
|
36
|
+
const duration = formatDurationMs(ended - proc.startedAt);
|
|
37
|
+
const exitNote = proc.exitCode !== null ? `(退出码 ${proc.exitCode})` : '';
|
|
38
|
+
const combined = [proc.stdout, proc.stderr].filter(Boolean).join('\n');
|
|
39
|
+
const body = combined.trim() || '(暂无输出)';
|
|
40
|
+
return (`[${proc.id}] 状态:${proc.status}${exitNote} 运行:${duration}\n` +
|
|
41
|
+
`命令:${proc.command}\n` +
|
|
42
|
+
`--- 输出 ---\n${body}`);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
//# sourceMappingURL=bash-output.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bash-output.js","sourceRoot":"","sources":["../../src/tools/bash-output.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGrE,SAAS,gBAAgB,CAAC,EAAU;IAChC,OAAO,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AACxC,CAAC;AAED,MAAM,CAAC,MAAM,cAAc,GAAS;IAChC,IAAI,EAAE,aAAa;IACnB,WAAW,EACP,uBAAuB;QACvB,gCAAgC;QAChC,gBAAgB;IACpB,UAAU,EAAE;QACR,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACR,EAAE,EAAE;gBACA,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,wCAAwC;aACxD;SACJ;QACD,QAAQ,EAAE,CAAC,IAAI,CAAC;KACnB;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,EAAkB;QAC5B,MAAM,IAAI,GAAG,aAAa,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC;QAC7C,IAAI,CAAC,IAAI,EAAE,CAAC;YACR,MAAM,GAAG,GAAG,cAAc,EAAE;iBACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAChB,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,OAAO,cAAc,EAAE,WAAW,GAAG,IAAI,KAAK,EAAE,CAAC;QACrD,CAAC;QAED,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,CAAC;QACzC,MAAM,QAAQ,GAAG,gBAAgB,CAAC,KAAK,GAAG,IAAI,CAAC,SAAS,CAAC,CAAC;QAC1D,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,KAAK,IAAI,CAAC,CAAC,CAAC,QAAQ,IAAI,CAAC,QAAQ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;QACxE,MAAM,QAAQ,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACvE,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,EAAE,IAAI,QAAQ,CAAC;QAEzC,OAAO,CACH,IAAI,IAAI,CAAC,EAAE,QAAQ,IAAI,CAAC,MAAM,GAAG,QAAQ,QAAQ,QAAQ,IAAI;YAC7D,MAAM,IAAI,CAAC,OAAO,IAAI;YACtB,eAAe,IAAI,EAAE,CACxB,CAAC;IACN,CAAC;CACJ,CAAC"}
|
package/dist/tools/bash.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { spawn } from 'node:child_process';
|
|
2
|
+
import { startBackground } from './bash-background.js';
|
|
2
3
|
/** bash 默认超时 120s,最大 600s。 */
|
|
3
4
|
const BASH_DEFAULT_TIMEOUT = 120_000;
|
|
4
5
|
const BASH_MAX_TIMEOUT = 600_000;
|
|
@@ -8,7 +9,9 @@ export const bashTool = {
|
|
|
8
9
|
name: 'bash',
|
|
9
10
|
description: '在当前工作目录执行 shell 命令。用于运行测试、构建、git、安装依赖等。' +
|
|
10
11
|
'不要用它读写文件——请用 read_file / write_file。避免交互式命令。' +
|
|
11
|
-
'执行前会请用户确认。'
|
|
12
|
+
'执行前会请用户确认。' +
|
|
13
|
+
'设置 background: true 可在后台启动长跑进程(如 dev server),立即返回 id,' +
|
|
14
|
+
'之后用 bash_output 查看输出与状态。',
|
|
12
15
|
parameters: {
|
|
13
16
|
type: 'object',
|
|
14
17
|
properties: {
|
|
@@ -22,7 +25,11 @@ export const bashTool = {
|
|
|
22
25
|
},
|
|
23
26
|
timeout: {
|
|
24
27
|
type: 'integer',
|
|
25
|
-
description: `超时毫秒数(默认 ${BASH_DEFAULT_TIMEOUT},最大 ${BASH_MAX_TIMEOUT}
|
|
28
|
+
description: `超时毫秒数(默认 ${BASH_DEFAULT_TIMEOUT},最大 ${BASH_MAX_TIMEOUT};后台模式忽略)`,
|
|
29
|
+
},
|
|
30
|
+
background: {
|
|
31
|
+
type: 'boolean',
|
|
32
|
+
description: '为 true 时在后台启动长跑进程并立即返回 id(默认 false,同步等待结束)',
|
|
26
33
|
},
|
|
27
34
|
},
|
|
28
35
|
required: ['command'],
|
|
@@ -34,10 +41,15 @@ export const bashTool = {
|
|
|
34
41
|
lines.push(`说明:${description}`);
|
|
35
42
|
return lines.join('\n');
|
|
36
43
|
},
|
|
37
|
-
async run({ command, timeout, }) {
|
|
44
|
+
async run({ command, timeout, background, }) {
|
|
38
45
|
const cmd = String(command || '');
|
|
39
46
|
if (!cmd)
|
|
40
47
|
return '错误:command 不能为空';
|
|
48
|
+
if (background) {
|
|
49
|
+
const proc = startBackground(cmd);
|
|
50
|
+
return (`已在后台启动(id: ${proc.id})。\n` +
|
|
51
|
+
`用 bash_output({"id":"${proc.id}"}) 查看输出与状态;长跑进程不会被超时终止。`);
|
|
52
|
+
}
|
|
41
53
|
const limit = Math.min(Math.max(1, timeout ?? BASH_DEFAULT_TIMEOUT), BASH_MAX_TIMEOUT);
|
|
42
54
|
const shell = process.env.SHELL || '/bin/bash';
|
|
43
55
|
return new Promise((resolve) => {
|
package/dist/tools/bash.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"bash.js","sourceRoot":"","sources":["../../src/tools/bash.ts"],"names":[],"mappings":"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"}
|
package/dist/tools/index.js
CHANGED
|
@@ -5,10 +5,11 @@
|
|
|
5
5
|
* 模型据此决定「要不要调用、传什么参数」;我们负责真正执行并把结果回传。
|
|
6
6
|
*
|
|
7
7
|
* 工具分两类:
|
|
8
|
-
* 只读勘探(无副作用,直接放行):read_file / list_dir / grep
|
|
8
|
+
* 只读勘探(无副作用,直接放行):read_file / list_dir / grep / bash_output
|
|
9
9
|
* 有副作用(执行前需用户确认):write_file / edit_file / bash
|
|
10
10
|
*
|
|
11
11
|
* 此外,通过 MCP (Model Context Protocol) 接入的外部工具名以 `mcp__` 开头。
|
|
12
|
+
* 后台长跑进程由 bash(background:true) / bash_output / kill_bash 三件套管理。
|
|
12
13
|
*/
|
|
13
14
|
import { readFileTool } from './read-file.js';
|
|
14
15
|
import { listDirTool } from './list-dir.js';
|
|
@@ -16,6 +17,8 @@ import { grepTool } from './grep.js';
|
|
|
16
17
|
import { writeFileTool } from './write-file.js';
|
|
17
18
|
import { editFileTool } from './edit-file.js';
|
|
18
19
|
import { bashTool } from './bash.js';
|
|
20
|
+
import { bashOutputTool } from './bash-output.js';
|
|
21
|
+
import { killBashTool } from './kill-bash.js';
|
|
19
22
|
/** 内置工具集合。 */
|
|
20
23
|
const builtinTools = [
|
|
21
24
|
readFileTool,
|
|
@@ -24,6 +27,8 @@ const builtinTools = [
|
|
|
24
27
|
writeFileTool,
|
|
25
28
|
editFileTool,
|
|
26
29
|
bashTool,
|
|
30
|
+
bashOutputTool,
|
|
31
|
+
killBashTool,
|
|
27
32
|
];
|
|
28
33
|
/** 当前已注册的工具(内置 + MCP)。 */
|
|
29
34
|
let tools = [...builtinTools];
|
package/dist/tools/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/tools/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC9C,OAAO,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AACrC,OAAO,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAClD,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAO9C,cAAc;AACd,MAAM,YAAY,GAAW;IACzB,YAAY;IACZ,WAAW;IACX,QAAQ;IACR,aAAa;IACb,YAAY;IACZ,QAAQ;IACR,cAAc;IACd,YAAY;CACf,CAAC;AAEF,0BAA0B;AAC1B,IAAI,KAAK,GAAW,CAAC,GAAG,YAAY,CAAC,CAAC;AAEtC,8CAA8C;AAC9C,IAAI,UAAU,GAA4B,IAAI,CAAC;AAE/C,uBAAuB;AACvB,MAAM,UAAU,UAAU;IACtB,KAAK,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC;IAC1B,UAAU,GAAG,IAAI,CAAC;AACtB,CAAC;AAED,mCAAmC;AACnC,MAAM,UAAU,wBAAwB,CAAC,UAAkB;IACvD,MAAM,MAAM,GAAG,QAAQ,UAAU,IAAI,CAAC;IACtC,KAAK,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AAC5D,CAAC;AAED,8BAA8B;AAC9B,MAAM,UAAU,qBAAqB;IACjC,KAAK,GAAG,CAAC,GAAG,YAAY,CAAC,CAAC;AAC9B,CAAC;AAED,kCAAkC;AAClC,MAAM,CAAC,KAAK,UAAU,gBAAgB,CAAC,OAAyB;IAC5D,UAAU,GAAG,OAAO,CAAC;IACrB,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,SAAS,EAAE,CAAC;IAC3C,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACvB,MAAM,OAAO,GAAS;YAClB,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;YACxB,YAAY,EAAE,IAAI,EAAE,oBAAoB;YACxC,OAAO,EAAE,CAAC,IAAS,EAAE,EAAE;gBACnB,OAAO,IAAI,CAAC,CAAC,UAAU,KAAK,CAAC,CAAC,YAAY,QAAQ,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC;YACtF,CAAC;YACD,GAAG,EAAE,KAAK,EAAE,IAAS,EAAE,EAAE;gBACrB,IAAI,CAAC,UAAU,EAAE,CAAC;oBACd,OAAO,sBAAsB,CAAC;gBAClC,CAAC;gBACD,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,CAAC;YAC7D,CAAC;SACJ,CAAC;QACF,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACxB,CAAC;AACL,CAAC;AAED,+BAA+B;AAC/B,MAAM,UAAU,OAAO,CAAC,IAAY;IAChC,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AAC9C,CAAC;AAED,4CAA4C;AAC5C,MAAM,UAAU,QAAQ;IACpB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;QACrB,IAAI,EAAE,UAAmB;QACzB,QAAQ,EAAE;YACN,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,WAAW,EAAE,CAAC,CAAC,WAAW;YAC1B,UAAU,EAAE,CAAC,CAAC,UAAU;SAC3B;KACJ,CAAC,CAAC,CAAC;AACR,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAC7B,IAAY,EACZ,QAAgB;IAEhB,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IAChD,IAAI,CAAC,IAAI;QAAE,OAAO,WAAW,IAAI,EAAE,CAAC;IAEpC,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACD,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,QAAQ,IAAI,IAAI,CAAC,CAAC;IACxC,CAAC;IAAC,MAAM,CAAC;QACL,OAAO,kBAAkB,QAAQ,EAAE,CAAC;IACxC,CAAC;IAED,IAAI,CAAC;QACD,OAAO,MAAM,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,CAAM,EAAE,CAAC;QACd,OAAO,MAAM,CAAC,EAAE,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC3C,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* kill_bash 工具 —— 终止后台 bash 进程。
|
|
3
|
+
*
|
|
4
|
+
* 配合 `bash` 的 background: true 使用:后台启动拿到 id 后,可用本工具主动
|
|
5
|
+
* 结束长跑进程(dev server / watch 等)。先发 SIGTERM 让其优雅退出,1 秒内
|
|
6
|
+
* 仍未 close 再 SIGKILL 兜底。终止本会话自己启动的后台进程,不需要用户确认。
|
|
7
|
+
*/
|
|
8
|
+
import { getBackground, killBackground, listBackground, } from './bash-background.js';
|
|
9
|
+
export const killBashTool = {
|
|
10
|
+
name: 'kill_bash',
|
|
11
|
+
description: '终止一个后台 bash 进程。传入 bash 后台模式返回的 id;' +
|
|
12
|
+
'先发送 SIGTERM,1 秒内未退出再 SIGKILL 兜底。' +
|
|
13
|
+
'进程已结束或 id 不存在时返回提示。',
|
|
14
|
+
parameters: {
|
|
15
|
+
type: 'object',
|
|
16
|
+
properties: {
|
|
17
|
+
id: {
|
|
18
|
+
type: 'string',
|
|
19
|
+
description: '要终止的后台进程 id(bg_1 之类)',
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
required: ['id'],
|
|
23
|
+
},
|
|
24
|
+
async run({ id }) {
|
|
25
|
+
const key = String(id || '');
|
|
26
|
+
const result = killBackground(key);
|
|
27
|
+
if (result === 'not_found') {
|
|
28
|
+
const ids = listBackground()
|
|
29
|
+
.map((p) => p.id)
|
|
30
|
+
.join(', ');
|
|
31
|
+
return `错误:未找到后台进程 ${key}。当前后台进程:${ids || '(无)'}`;
|
|
32
|
+
}
|
|
33
|
+
if (result === 'not_running') {
|
|
34
|
+
const proc = getBackground(key);
|
|
35
|
+
return `[${key}] 已不在运行(状态:${proc?.status ?? '未知'}),无需终止。`;
|
|
36
|
+
}
|
|
37
|
+
return (`已发送终止信号给 [${key}](先 SIGTERM,1 秒内未退出再 SIGKILL)。\n` +
|
|
38
|
+
`用 bash_output({"id":"${key}"}) 确认进程已退出。`);
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
//# sourceMappingURL=kill-bash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kill-bash.js","sourceRoot":"","sources":["../../src/tools/kill-bash.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EACH,aAAa,EACb,cAAc,EACd,cAAc,GACjB,MAAM,sBAAsB,CAAC;AAG9B,MAAM,CAAC,MAAM,YAAY,GAAS;IAC9B,IAAI,EAAE,WAAW;IACjB,WAAW,EACP,oCAAoC;QACpC,kCAAkC;QAClC,qBAAqB;IACzB,UAAU,EAAE;QACR,IAAI,EAAE,QAAQ;QACd,UAAU,EAAE;YACR,EAAE,EAAE;gBACA,IAAI,EAAE,QAAQ;gBACd,WAAW,EAAE,sBAAsB;aACtC;SACJ;QACD,QAAQ,EAAE,CAAC,IAAI,CAAC;KACnB;IACD,KAAK,CAAC,GAAG,CAAC,EAAE,EAAE,EAAkB;QAC5B,MAAM,GAAG,GAAG,MAAM,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;QAC7B,MAAM,MAAM,GAAG,cAAc,CAAC,GAAG,CAAC,CAAC;QAEnC,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,cAAc,EAAE;iBACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;iBAChB,IAAI,CAAC,IAAI,CAAC,CAAC;YAChB,OAAO,cAAc,GAAG,WAAW,GAAG,IAAI,KAAK,EAAE,CAAC;QACtD,CAAC;QACD,IAAI,MAAM,KAAK,aAAa,EAAE,CAAC;YAC3B,MAAM,IAAI,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC;YAChC,OAAO,IAAI,GAAG,cAAc,IAAI,EAAE,MAAM,IAAI,IAAI,SAAS,CAAC;QAC9D,CAAC;QACD,OAAO,CACH,aAAa,GAAG,kCAAkC;YAClD,wBAAwB,GAAG,cAAc,CAC5C,CAAC;IACN,CAAC;CACJ,CAAC"}
|