ai-helper-agent 1.5.1 → 1.6.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/package.json +1 -1
- package/src/executor.js +50 -1
- package/src/server.js +48 -2
- package/src/skill/registry.js +9 -1
- package/src/skill/skill-creator-seed.js +6 -5
package/package.json
CHANGED
package/src/executor.js
CHANGED
|
@@ -1,8 +1,56 @@
|
|
|
1
1
|
// agent/src/executor.js - 命令执行器(child_process + 流式输出)
|
|
2
2
|
import { spawn } from 'child_process';
|
|
3
3
|
import crypto from 'crypto';
|
|
4
|
+
import os from 'os';
|
|
4
5
|
import { loadConfig } from './config.js';
|
|
5
6
|
|
|
7
|
+
function getShellForExec() {
|
|
8
|
+
const platform = os.platform();
|
|
9
|
+
const envShell = process.env.SHELL || process.env.COMSPEC || '';
|
|
10
|
+
|
|
11
|
+
if (platform === 'win32') {
|
|
12
|
+
if (envShell.toLowerCase().includes('bash')) {
|
|
13
|
+
return { shell: envShell, args: ['-c'] };
|
|
14
|
+
} else if (envShell.toLowerCase().includes('powershell')) {
|
|
15
|
+
return { shell: envShell, args: ['-Command'] };
|
|
16
|
+
} else if (envShell.toLowerCase().includes('cmd')) {
|
|
17
|
+
return { shell: envShell, args: ['/c'] };
|
|
18
|
+
} else {
|
|
19
|
+
const gitBashPaths = [
|
|
20
|
+
'C:\\Program Files\\Git\\bin\\bash.exe',
|
|
21
|
+
'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
|
|
22
|
+
`${process.env.USERPROFILE}\\AppData\\Local\\Programs\\Git\\bin\\bash.exe`,
|
|
23
|
+
];
|
|
24
|
+
for (const path of gitBashPaths) {
|
|
25
|
+
try {
|
|
26
|
+
if (require('fs').existsSync(path)) {
|
|
27
|
+
return { shell: path, args: ['-c'] };
|
|
28
|
+
}
|
|
29
|
+
} catch {}
|
|
30
|
+
}
|
|
31
|
+
return { shell: 'cmd.exe', args: ['/c'] };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (platform === 'darwin') {
|
|
36
|
+
if (envShell.toLowerCase().includes('zsh')) {
|
|
37
|
+
return { shell: envShell, args: ['-c'] };
|
|
38
|
+
} else if (envShell.toLowerCase().includes('bash')) {
|
|
39
|
+
return { shell: envShell, args: ['-c'] };
|
|
40
|
+
}
|
|
41
|
+
return { shell: '/bin/zsh', args: ['-c'] };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
if (envShell.toLowerCase().includes('bash')) {
|
|
45
|
+
return { shell: envShell, args: ['-c'] };
|
|
46
|
+
} else if (envShell.toLowerCase().includes('zsh')) {
|
|
47
|
+
return { shell: envShell, args: ['-c'] };
|
|
48
|
+
} else if (envShell.toLowerCase().includes('fish')) {
|
|
49
|
+
return { shell: envShell, args: ['-c'] };
|
|
50
|
+
}
|
|
51
|
+
return { shell: '/bin/bash', args: ['-c'] };
|
|
52
|
+
}
|
|
53
|
+
|
|
6
54
|
// 运行中的进程映射:execId → { process, wsClients: Set, timeoutId, forceKillId }
|
|
7
55
|
const runningProcesses = new Map();
|
|
8
56
|
|
|
@@ -62,7 +110,8 @@ function executeCommand(command, cwd, wsClient, onComplete, collectOutput = fals
|
|
|
62
110
|
const execId = generateExecId();
|
|
63
111
|
const timeout = config.commandTimeout || 300000;
|
|
64
112
|
|
|
65
|
-
const
|
|
113
|
+
const { shell, args } = getShellForExec();
|
|
114
|
+
const proc = spawn(shell, [...args, command], {
|
|
66
115
|
cwd: workdir,
|
|
67
116
|
env: buildSafeEnv(),
|
|
68
117
|
stdio: ['ignore', 'pipe', 'pipe'],
|
package/src/server.js
CHANGED
|
@@ -51,7 +51,53 @@ const MAX_LOG_QUERY_LIMIT = 1000; // 日志查询最大条数
|
|
|
51
51
|
const PID_FILE = join(homedir(), '.ai-helper-agent', 'agent.pid');
|
|
52
52
|
const DEFAULT_MAX_SIZE = 50 * 1024 * 1024; // 文件默认大小限制
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
function detectShell() {
|
|
55
|
+
const platform = os.platform();
|
|
56
|
+
const envShell = process.env.SHELL || process.env.COMSPEC || '';
|
|
57
|
+
|
|
58
|
+
if (platform === 'win32') {
|
|
59
|
+
if (envShell.toLowerCase().includes('bash')) {
|
|
60
|
+
return envShell;
|
|
61
|
+
} else if (envShell.toLowerCase().includes('powershell')) {
|
|
62
|
+
return envShell;
|
|
63
|
+
} else if (envShell.toLowerCase().includes('cmd')) {
|
|
64
|
+
return envShell;
|
|
65
|
+
} else {
|
|
66
|
+
const gitBashPaths = [
|
|
67
|
+
'C:\\Program Files\\Git\\bin\\bash.exe',
|
|
68
|
+
'C:\\Program Files (x86)\\Git\\bin\\bash.exe',
|
|
69
|
+
`${process.env.USERPROFILE}\\AppData\\Local\\Programs\\Git\\bin\\bash.exe`,
|
|
70
|
+
];
|
|
71
|
+
for (const path of gitBashPaths) {
|
|
72
|
+
try {
|
|
73
|
+
if (require('fs').existsSync(path)) {
|
|
74
|
+
return path;
|
|
75
|
+
}
|
|
76
|
+
} catch {}
|
|
77
|
+
}
|
|
78
|
+
return 'cmd.exe';
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (platform === 'darwin') {
|
|
83
|
+
if (envShell.toLowerCase().includes('zsh')) {
|
|
84
|
+
return envShell;
|
|
85
|
+
} else if (envShell.toLowerCase().includes('bash')) {
|
|
86
|
+
return envShell;
|
|
87
|
+
}
|
|
88
|
+
return '/bin/zsh';
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
if (envShell.toLowerCase().includes('bash')) {
|
|
92
|
+
return envShell;
|
|
93
|
+
} else if (envShell.toLowerCase().includes('zsh')) {
|
|
94
|
+
return envShell;
|
|
95
|
+
} else if (envShell.toLowerCase().includes('fish')) {
|
|
96
|
+
return envShell;
|
|
97
|
+
}
|
|
98
|
+
return '/bin/bash';
|
|
99
|
+
}
|
|
100
|
+
|
|
55
101
|
const PLATFORM_INFO = {
|
|
56
102
|
platform: os.platform(),
|
|
57
103
|
platformName: (() => {
|
|
@@ -60,7 +106,7 @@ const PLATFORM_INFO = {
|
|
|
60
106
|
})(),
|
|
61
107
|
arch: os.arch(),
|
|
62
108
|
hostname: os.hostname(),
|
|
63
|
-
shell:
|
|
109
|
+
shell: detectShell(),
|
|
64
110
|
homeDir: os.homedir(),
|
|
65
111
|
nodeVersion: process.version
|
|
66
112
|
};
|
package/src/skill/registry.js
CHANGED
|
@@ -66,7 +66,15 @@ export function initializeSkillRegistry() {
|
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
68
|
|
|
69
|
-
// 3.
|
|
69
|
+
// 3. 标记内置技能的特殊属性(skill-creator 是内置技能,不可编辑、不可删除)
|
|
70
|
+
const skillCreator = skills.get('skill-creator');
|
|
71
|
+
if (skillCreator) {
|
|
72
|
+
skillCreator.builtin = true;
|
|
73
|
+
skillCreator.editable = false;
|
|
74
|
+
skillCreator.deletable = false;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// 4. 恢复内置技能的启用状态(用户通过停止/启用的状态)
|
|
70
78
|
const builtinState = loadBuiltinState();
|
|
71
79
|
for (const [name, enabled] of Object.entries(builtinState)) {
|
|
72
80
|
const skill = skills.get(name);
|
|
@@ -106,9 +106,10 @@ enabled: true
|
|
|
106
106
|
- **Windows (PowerShell)**:\`New-Item -ItemType Directory -Force -Path "$env:USERPROFILE\\.ai-helper-agent\\skills\\<skill-name>"\`
|
|
107
107
|
- 如果 \`mkdir\` 报错,尝试不带参数或使用 \`agent_write_file\` 工具在目标路径写入一个临时文件,让系统自动创建父目录。
|
|
108
108
|
|
|
109
|
-
5. **写入 SKILL.md**:使用 \`agent_write_file\`
|
|
110
|
-
-
|
|
111
|
-
-
|
|
109
|
+
5. **写入 SKILL.md**:使用 \`agent_write_file\` 工具将内容写入。
|
|
110
|
+
- **重要**:\`agent_write_file\` 不会展开 \`~\`,必须使用绝对路径!
|
|
111
|
+
- **macOS/Linux**:从当前工作目录推断用户主目录(工作目录通常是 \`/Users/<你的用户名>/...\`),完整路径为 \`/Users/<你的用户名>/.ai-helper-agent/skills/<skill-name>/SKILL.md\`
|
|
112
|
+
- **Windows**:\`C:\\Users\\<你的用户名>\\.ai-helper-agent\\skills\\<skill-name>\\SKILL.md\`
|
|
112
113
|
|
|
113
114
|
6. **告知用户**:创建完成后,告知用户技能已就绪,说明触发关键词。建议用户在工具箱页面点击「重新加载」使技能生效。
|
|
114
115
|
|
|
@@ -117,10 +118,10 @@ enabled: true
|
|
|
117
118
|
当用户要求更新/完善已有技能时:
|
|
118
119
|
|
|
119
120
|
1. **确认技能名称**:与用户确认要更新的技能名称
|
|
120
|
-
2. **读取现有内容**:使用 \`agent_read_file\` 读取目标 SKILL.md
|
|
121
|
+
2. **读取现有内容**:使用 \`agent_read_file\` 读取目标 SKILL.md(路径规则同创建流程,需使用绝对路径,如 \`/Users/<你的用户名>/.ai-helper-agent/skills/<skill-name>/SKILL.md\`)
|
|
121
122
|
3. **分析更新需求**:结合当前对话上下文,识别需要更新的部分
|
|
122
123
|
4. **合并内容**:保留原技能的核心结构,将新内容融入适当位置
|
|
123
|
-
5. **覆盖写入**:使用 \`agent_write_file\` 覆盖写入 SKILL.md
|
|
124
|
+
5. **覆盖写入**:使用 \`agent_write_file\` 覆盖写入 SKILL.md(路径规则同上,必须使用绝对路径)
|
|
124
125
|
6. **告知用户**:告知更新内容,建议重新加载
|
|
125
126
|
|
|
126
127
|
## 更新注意事项
|