aws-runtime-bridge 1.7.50 → 1.8.1
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/dist/adapter/AcodeSdkAdapter.d.ts +30 -3
- package/dist/adapter/AcodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/AcodeSdkAdapter.js +461 -105
- package/dist/adapter/AcodeSdkAdapter.test.js +1282 -93
- package/dist/adapter/ClaudeSdkAdapter.test.js +31 -8
- package/dist/adapter/CodexSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/CodexSdkAdapter.js +2 -0
- package/dist/adapter/OpencodeSdkAdapter.d.ts.map +1 -1
- package/dist/adapter/OpencodeSdkAdapter.js +2 -0
- package/dist/adapter/SdkProviderSpi.d.ts.map +1 -1
- package/dist/adapter/adapter.test.js +2 -2
- package/dist/adapter/idle-poll-loop.d.ts +4 -4
- package/dist/adapter/idle-poll-loop.d.ts.map +1 -1
- package/dist/adapter/idle-poll-loop.js +3 -3
- package/dist/adapter/idle-poll-loop.test.js +15 -1
- package/dist/adapter/types.d.ts +25 -6
- package/dist/adapter/types.d.ts.map +1 -1
- package/dist/adapter/types.js +226 -111
- package/dist/adapter/types.test.js +10 -3
- package/dist/routes/ai-sources.js +5 -1
- package/dist/routes/ai-sources.test.js +21 -0
- package/dist/routes/instance.d.ts.map +1 -1
- package/dist/routes/instance.js +6 -7
- package/dist/routes/runtime-binding.js +2 -2
- package/dist/routes/terminal.d.ts +22 -5
- package/dist/routes/terminal.d.ts.map +1 -1
- package/dist/routes/terminal.js +522 -170
- package/dist/routes/terminal.test.js +497 -208
- package/dist/services/mcp-launch-binding-queue.d.ts +1 -0
- package/dist/services/mcp-launch-binding-queue.d.ts.map +1 -1
- package/dist/services/mcp-launch-binding-queue.js +8 -2
- package/dist/services/mcp-launch-binding-queue.test.js +47 -4
- package/dist/services/session-output.d.ts +14 -6
- package/dist/services/session-output.d.ts.map +1 -1
- package/dist/services/session-output.js +29 -14
- package/dist/services/session-output.test.js +93 -39
- package/package/acode/dist/built-in-file-tools.d.ts +1 -1
- package/package/acode/dist/built-in-file-tools.d.ts.map +1 -1
- package/package/acode/dist/built-in-file-tools.js +388 -50
- package/package/acode/dist/runtime.d.ts +11 -3
- package/package/acode/dist/runtime.d.ts.map +1 -1
- package/package/acode/dist/runtime.js +902 -190
- package/package/acode/dist/types.d.ts +18 -5
- package/package/acode/dist/types.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.js +3 -2
- package/package/aws-client-agent-mcp/dist/mcp-server.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js +11 -9
- package/package/aws-client-agent-mcp/dist/mcp-server.test.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js +1 -0
- package/package/aws-client-agent-mcp/dist/runtime-launch-binding.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts +2 -0
- package/package/aws-client-agent-mcp/dist/status-reporter.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/status-reporter.js +31 -3
- package/package/aws-client-agent-mcp/dist/status-reporter.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts +1 -1
- package/package/aws-client-agent-mcp/dist/types.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/types.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.d.ts.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.js +2 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.js.map +1 -1
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js +43 -0
- package/package/aws-client-agent-mcp/dist/websocket-client.test.js.map +1 -1
- package/package.json +1 -1
package/dist/adapter/types.js
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
*
|
|
5
5
|
* 参考自 SpectrAI 项目的 adapter 设计,用于支持多种 AI Provider
|
|
6
6
|
*/
|
|
7
|
-
import { EventEmitter } from
|
|
8
|
-
const TEMP_CLIPBOARD_PASTE_PLACEHOLDER =
|
|
7
|
+
import { EventEmitter } from "node:events";
|
|
8
|
+
const TEMP_CLIPBOARD_PASTE_PLACEHOLDER = "[temp clipboard paste file]";
|
|
9
9
|
const WINDOWS_TEMP_CLIPBOARD_PASTE_PATH = /[A-Z]:(?:\\\\|\\)[^\r\n"'`]*?(?:\\\\|\\)mini-term-clipboard(?:\\\\|\\)paste-\d+\.txt/gi;
|
|
10
10
|
const POSIX_TEMP_CLIPBOARD_PASTE_PATH = /\/[^\r\n"'`]*?\/mini-term-clipboard\/paste-\d+\.txt/gi;
|
|
11
11
|
const BARE_TEMP_CLIPBOARD_PASTE_PATH = /mini-term-clipboard[\\/]paste-\d+\.txt/gi;
|
|
@@ -24,8 +24,12 @@ export function redactTransientClipboardPastePaths(text) {
|
|
|
24
24
|
* 主流程:剥离包裹引号 -> 要求整串匹配,避免误拦截显式读取该文件的正常命令。
|
|
25
25
|
*/
|
|
26
26
|
export function isTransientClipboardPasteFilePath(text) {
|
|
27
|
-
const trimmed = text.trim().replace(/^(["'])(.*)\1$/,
|
|
28
|
-
return [
|
|
27
|
+
const trimmed = text.trim().replace(/^(["'])(.*)\1$/, "$2");
|
|
28
|
+
return [
|
|
29
|
+
WINDOWS_TEMP_CLIPBOARD_PASTE_PATH,
|
|
30
|
+
POSIX_TEMP_CLIPBOARD_PASTE_PATH,
|
|
31
|
+
BARE_TEMP_CLIPBOARD_PASTE_PATH,
|
|
32
|
+
].some((pattern) => {
|
|
29
33
|
pattern.lastIndex = 0;
|
|
30
34
|
const match = pattern.exec(trimmed);
|
|
31
35
|
return Boolean(match && match[0] === trimmed);
|
|
@@ -35,13 +39,13 @@ export function isTransientClipboardPasteFilePath(text) {
|
|
|
35
39
|
* 递归脱敏工具展示参数,保留真实执行输入不变,仅用于日志/状态/时间线展示。
|
|
36
40
|
*/
|
|
37
41
|
export function redactTransientClipboardPastePathsInValue(value) {
|
|
38
|
-
if (typeof value ===
|
|
42
|
+
if (typeof value === "string") {
|
|
39
43
|
return redactTransientClipboardPastePaths(value);
|
|
40
44
|
}
|
|
41
45
|
if (Array.isArray(value)) {
|
|
42
46
|
return value.map((item) => redactTransientClipboardPastePathsInValue(item));
|
|
43
47
|
}
|
|
44
|
-
if (value && typeof value ===
|
|
48
|
+
if (value && typeof value === "object") {
|
|
45
49
|
return Object.fromEntries(Object.entries(value).map(([key, item]) => [
|
|
46
50
|
key,
|
|
47
51
|
redactTransientClipboardPastePathsInValue(item),
|
|
@@ -57,23 +61,63 @@ export function redactTransientClipboardPastePathsInValue(value) {
|
|
|
57
61
|
*/
|
|
58
62
|
const KNOWN_MCP_TOOLS = {
|
|
59
63
|
// aws-mcp 工具
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
64
|
+
get_colleague: "aws-mcp",
|
|
65
|
+
get_group_rooms: "aws-mcp",
|
|
66
|
+
get_profile: "aws-mcp",
|
|
67
|
+
get_status: "aws-mcp",
|
|
68
|
+
hire_colleague: "aws-mcp",
|
|
69
|
+
hire_colleagues_batch: "aws-mcp",
|
|
70
|
+
send_group: "aws-mcp",
|
|
71
|
+
send_private: "aws-mcp",
|
|
72
|
+
poll_message: "aws-mcp",
|
|
73
|
+
get_message: "aws-mcp",
|
|
74
|
+
get_dm_history: "aws-mcp",
|
|
75
|
+
get_group_history: "aws-mcp",
|
|
76
|
+
my_task: "aws-mcp",
|
|
77
|
+
submit_task_result: "aws-mcp",
|
|
78
|
+
reject_task: "aws-mcp",
|
|
79
|
+
terminate_task: "aws-mcp",
|
|
76
80
|
};
|
|
81
|
+
const AGENTSWORK_MCP_SERVER_ALIASES = new Set([
|
|
82
|
+
"aws-mcp",
|
|
83
|
+
"agents-work-studio",
|
|
84
|
+
]);
|
|
85
|
+
function canonicalizeMcpServer(serverName, toolName) {
|
|
86
|
+
const knownMcpServer = KNOWN_MCP_TOOLS[toolName.toLowerCase()];
|
|
87
|
+
if (knownMcpServer && AGENTSWORK_MCP_SERVER_ALIASES.has(serverName.toLowerCase())) {
|
|
88
|
+
return knownMcpServer;
|
|
89
|
+
}
|
|
90
|
+
return serverName;
|
|
91
|
+
}
|
|
92
|
+
export function getCanonicalMcpDetail(rawToolName) {
|
|
93
|
+
const normalizedRawToolName = rawToolName.trim();
|
|
94
|
+
if (!normalizedRawToolName)
|
|
95
|
+
return normalizedRawToolName;
|
|
96
|
+
if (normalizedRawToolName.includes("__")) {
|
|
97
|
+
const parts = normalizedRawToolName.split("__").filter(Boolean);
|
|
98
|
+
if (parts.length >= 3 && parts[0].toLowerCase() === "mcp") {
|
|
99
|
+
const serverName = parts[1];
|
|
100
|
+
const toolName = parts.slice(2).join("__");
|
|
101
|
+
return `${canonicalizeMcpServer(serverName, toolName)}/${toolName}`;
|
|
102
|
+
}
|
|
103
|
+
if (parts.length >= 2) {
|
|
104
|
+
const serverName = parts[0];
|
|
105
|
+
const toolName = parts.slice(1).join("__");
|
|
106
|
+
return `${canonicalizeMcpServer(serverName, toolName)}/${toolName}`;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
const knownMcpServer = KNOWN_MCP_TOOLS[normalizedRawToolName.toLowerCase()];
|
|
110
|
+
return knownMcpServer
|
|
111
|
+
? `${knownMcpServer}/${normalizedRawToolName}`
|
|
112
|
+
: normalizedRawToolName;
|
|
113
|
+
}
|
|
114
|
+
function formatCanonicalMcpDetail(serverName, toolName) {
|
|
115
|
+
if (!serverName)
|
|
116
|
+
return toolName;
|
|
117
|
+
if (!toolName)
|
|
118
|
+
return serverName;
|
|
119
|
+
return `${canonicalizeMcpServer(serverName, toolName)}/${toolName}`;
|
|
120
|
+
}
|
|
77
121
|
/**
|
|
78
122
|
* 根据工具名称获取动作类型信息
|
|
79
123
|
* @param toolName 工具名称
|
|
@@ -82,32 +126,36 @@ const KNOWN_MCP_TOOLS = {
|
|
|
82
126
|
*/
|
|
83
127
|
export function getToolActionInfo(toolName, toolInput, metadata) {
|
|
84
128
|
if (!toolName) {
|
|
85
|
-
return { actionType:
|
|
129
|
+
return { actionType: "other", actionLabel: "调用工具" };
|
|
86
130
|
}
|
|
87
131
|
const name = toolName.toLowerCase();
|
|
88
|
-
const normalizedName = name.replace(/[_-]/g,
|
|
89
|
-
const semanticToolName = name.startsWith(
|
|
90
|
-
? name.split(
|
|
91
|
-
: name.
|
|
92
|
-
? name.slice(
|
|
93
|
-
: name
|
|
94
|
-
|
|
132
|
+
const normalizedName = name.replace(/[_-]/g, "");
|
|
133
|
+
const semanticToolName = name.startsWith("mcp__")
|
|
134
|
+
? name.split("__").slice(2).join("__")
|
|
135
|
+
: name.startsWith("builtin__")
|
|
136
|
+
? name.split("__").slice(1).join("__")
|
|
137
|
+
: name.includes("-mcp__")
|
|
138
|
+
? name.slice(name.indexOf("-mcp__") + 6)
|
|
139
|
+
: name.includes("/")
|
|
140
|
+
? name.split("/").filter(Boolean).at(-1) || name
|
|
141
|
+
: name;
|
|
142
|
+
const normalizedSemanticToolName = semanticToolName.replace(/[_-]/g, "");
|
|
95
143
|
const normalizeSkillList = (value) => {
|
|
96
144
|
if (!Array.isArray(value)) {
|
|
97
145
|
return [];
|
|
98
146
|
}
|
|
99
147
|
return value
|
|
100
|
-
.map((item) => String(item ??
|
|
148
|
+
.map((item) => String(item ?? "").trim())
|
|
101
149
|
.filter((item) => item.length > 0);
|
|
102
150
|
};
|
|
103
151
|
const firstString = (...values) => {
|
|
104
152
|
for (const value of values) {
|
|
105
|
-
if (typeof value ===
|
|
153
|
+
if (typeof value === "string" && value.trim()) {
|
|
106
154
|
return redactTransientClipboardPastePaths(value);
|
|
107
155
|
}
|
|
108
156
|
if (Array.isArray(value) && value.length > 0) {
|
|
109
157
|
const first = value[0];
|
|
110
|
-
if (typeof first ===
|
|
158
|
+
if (typeof first === "string" && first.trim()) {
|
|
111
159
|
return redactTransientClipboardPastePaths(first);
|
|
112
160
|
}
|
|
113
161
|
}
|
|
@@ -115,17 +163,16 @@ export function getToolActionInfo(toolName, toolInput, metadata) {
|
|
|
115
163
|
return undefined;
|
|
116
164
|
};
|
|
117
165
|
const matchesToolName = (...toolNames) => {
|
|
118
|
-
const normalizedToolNames = toolNames.map((item) => item.replace(/[_-]/g,
|
|
119
|
-
return normalizedToolNames.includes(normalizedName) ||
|
|
166
|
+
const normalizedToolNames = toolNames.map((item) => item.replace(/[_-]/g, ""));
|
|
167
|
+
return (normalizedToolNames.includes(normalizedName) ||
|
|
168
|
+
normalizedToolNames.includes(normalizedSemanticToolName));
|
|
120
169
|
};
|
|
121
|
-
// 调试日志:记录工具名
|
|
122
|
-
console.log(`[getToolActionInfo] toolName="${toolName}", name="${name}"`);
|
|
123
170
|
const loadSkills = normalizeSkillList(toolInput?.load_skills);
|
|
124
|
-
if ((name ===
|
|
171
|
+
if ((name === "task" || name === "task()") && loadSkills.length > 0) {
|
|
125
172
|
return {
|
|
126
|
-
actionType:
|
|
127
|
-
actionLabel:
|
|
128
|
-
actionDetail: loadSkills.join(
|
|
173
|
+
actionType: "skill",
|
|
174
|
+
actionLabel: "加载技能",
|
|
175
|
+
actionDetail: loadSkills.join(", "),
|
|
129
176
|
};
|
|
130
177
|
}
|
|
131
178
|
// Skill 调用
|
|
@@ -136,62 +183,94 @@ export function getToolActionInfo(toolName, toolInput, metadata) {
|
|
|
136
183
|
//
|
|
137
184
|
// 注意:SDK 发送的 tool_name 可能是 'Skill'(大写)或 'skill'(小写)
|
|
138
185
|
// 使用 toLowerCase() 统一处理
|
|
139
|
-
const isSkillTool = name ===
|
|
140
|
-
name ===
|
|
141
|
-
name ===
|
|
142
|
-
(name.includes(
|
|
143
|
-
|
|
186
|
+
const isSkillTool = name === "skill" ||
|
|
187
|
+
name === "load_skill" ||
|
|
188
|
+
name === "disclosecontext" ||
|
|
189
|
+
(name.includes("skill") &&
|
|
190
|
+
!name.startsWith("mcp__") &&
|
|
191
|
+
!name.includes("mcp_"));
|
|
144
192
|
if (isSkillTool) {
|
|
145
193
|
// Skill 参数可能有多种格式:
|
|
146
194
|
// 1. { skill: "skillName" }
|
|
147
195
|
// 2. { name: "skillName" }
|
|
148
196
|
// 3. { args: { name: "skillName", ... } } // task() 格式
|
|
149
197
|
// 4. { load_skills: ["skill1", "skill2"] } // task() 加载技能格式
|
|
150
|
-
let skillName =
|
|
198
|
+
let skillName = "";
|
|
151
199
|
// 检查 load_skills 格式(task 工具)
|
|
152
|
-
if (Array.isArray(toolInput?.load_skills) &&
|
|
153
|
-
|
|
200
|
+
if (Array.isArray(toolInput?.load_skills) &&
|
|
201
|
+
toolInput.load_skills.length > 0) {
|
|
202
|
+
skillName = toolInput.load_skills.join(", ");
|
|
154
203
|
}
|
|
155
204
|
// 检查 skill 参数
|
|
156
|
-
else if (typeof toolInput?.skill ===
|
|
205
|
+
else if (typeof toolInput?.skill === "string") {
|
|
157
206
|
skillName = toolInput.skill;
|
|
158
207
|
}
|
|
159
208
|
// 检查 name 参数
|
|
160
|
-
else if (typeof toolInput?.name ===
|
|
209
|
+
else if (typeof toolInput?.name === "string") {
|
|
161
210
|
skillName = toolInput.name;
|
|
162
211
|
}
|
|
163
212
|
// 检查嵌套的 args.name
|
|
164
|
-
else if (toolInput?.args && typeof toolInput.args ===
|
|
213
|
+
else if (toolInput?.args && typeof toolInput.args === "object") {
|
|
165
214
|
const args = toolInput.args;
|
|
166
|
-
if (typeof args.name ===
|
|
215
|
+
if (typeof args.name === "string") {
|
|
167
216
|
skillName = args.name;
|
|
168
217
|
}
|
|
169
|
-
else if (typeof args.skill ===
|
|
218
|
+
else if (typeof args.skill === "string") {
|
|
170
219
|
skillName = args.skill;
|
|
171
220
|
}
|
|
172
221
|
}
|
|
173
|
-
|
|
174
|
-
|
|
222
|
+
return {
|
|
223
|
+
actionType: "skill",
|
|
224
|
+
actionLabel: "使用技能",
|
|
225
|
+
actionDetail: skillName || undefined,
|
|
226
|
+
};
|
|
175
227
|
}
|
|
176
228
|
// 文件/命令工具即使走 MCP 命名,也优先按语义显示为读取/编辑/命令执行。
|
|
177
|
-
if (matchesToolName(
|
|
229
|
+
if (matchesToolName("edit", "editfile", "strreplace", "editcode", "apply_patch", "multiedit", "replace")) {
|
|
178
230
|
const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.target_file, toolInput?.targetFile);
|
|
179
|
-
return {
|
|
231
|
+
return {
|
|
232
|
+
actionType: "edit_file",
|
|
233
|
+
actionLabel: "编辑文件",
|
|
234
|
+
actionDetail: filePath,
|
|
235
|
+
};
|
|
180
236
|
}
|
|
181
|
-
if (matchesToolName(
|
|
237
|
+
if (matchesToolName("write", "writefile", "fswrite")) {
|
|
182
238
|
const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.target_file, toolInput?.targetFile);
|
|
183
|
-
return {
|
|
239
|
+
return {
|
|
240
|
+
actionType: "write_file",
|
|
241
|
+
actionLabel: "写入文件",
|
|
242
|
+
actionDetail: filePath,
|
|
243
|
+
};
|
|
184
244
|
}
|
|
185
|
-
if (matchesToolName(
|
|
245
|
+
if (matchesToolName("read", "readfile", "readcode", "readmultiplefiles", "read_file")) {
|
|
186
246
|
if (Array.isArray(toolInput?.paths)) {
|
|
187
|
-
return {
|
|
247
|
+
return {
|
|
248
|
+
actionType: "read_file",
|
|
249
|
+
actionLabel: "读取文件",
|
|
250
|
+
actionDetail: `${toolInput.paths.length}个文件`,
|
|
251
|
+
};
|
|
188
252
|
}
|
|
189
253
|
const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.paths, toolInput?.target_file, toolInput?.targetFile);
|
|
190
|
-
return {
|
|
254
|
+
return {
|
|
255
|
+
actionType: "read_file",
|
|
256
|
+
actionLabel: "读取文件",
|
|
257
|
+
actionDetail: filePath,
|
|
258
|
+
};
|
|
191
259
|
}
|
|
192
|
-
if (matchesToolName(
|
|
260
|
+
if (matchesToolName("bash", "shell", "command", "executepwsh", "controlpwshprocess", "execute", "run_command", "execute_command")) {
|
|
193
261
|
const command = firstString(toolInput?.command, toolInput?.cmd, toolInput?.argv);
|
|
194
|
-
return {
|
|
262
|
+
return {
|
|
263
|
+
actionType: "bash",
|
|
264
|
+
actionLabel: "执行命令",
|
|
265
|
+
actionDetail: command?.slice(0, 50),
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
if (matchesToolName("poll_message", "get_message")) {
|
|
269
|
+
return {
|
|
270
|
+
actionType: "idle",
|
|
271
|
+
actionLabel: "监听消息",
|
|
272
|
+
actionDetail: getCanonicalMcpDetail(toolName),
|
|
273
|
+
};
|
|
195
274
|
}
|
|
196
275
|
// MCP 工具调用
|
|
197
276
|
// 优先使用 metadata 中的 mcpServer 信息(SDK 直接提供)
|
|
@@ -202,101 +281,137 @@ export function getToolActionInfo(toolName, toolInput, metadata) {
|
|
|
202
281
|
// 4. xxx-mcp__tool (自定义格式,如 aws-mcp__get_profile)
|
|
203
282
|
// 5. 包含 'mcp_' 的工具名
|
|
204
283
|
// 6. Kiro 的 kiroPowers
|
|
205
|
-
//
|
|
284
|
+
//
|
|
206
285
|
// actionDetail 格式: server/tool(包含 MCP server 名称,便于用户识别工具来源)
|
|
207
286
|
// 优先检查 metadata 中的 MCP server 信息
|
|
208
287
|
if (metadata?.mcpServer) {
|
|
209
288
|
const mcpServer = metadata.mcpServer;
|
|
210
|
-
const mcpTool = toolName ||
|
|
211
|
-
const detail =
|
|
212
|
-
|
|
213
|
-
return { actionType: 'mcp', actionLabel: '调用MCP', actionDetail: detail };
|
|
289
|
+
const mcpTool = toolName || "";
|
|
290
|
+
const detail = formatCanonicalMcpDetail(mcpServer, mcpTool);
|
|
291
|
+
return { actionType: "mcp", actionLabel: "调用MCP", actionDetail: detail };
|
|
214
292
|
}
|
|
215
293
|
// 检查是否在已知 MCP 工具列表中
|
|
216
294
|
const knownMcpServer = KNOWN_MCP_TOOLS[toolName];
|
|
217
295
|
if (knownMcpServer) {
|
|
218
296
|
const detail = `${knownMcpServer}/${toolName}`;
|
|
219
|
-
|
|
220
|
-
return { actionType: 'mcp', actionLabel: '调用MCP', actionDetail: detail };
|
|
297
|
+
return { actionType: "mcp", actionLabel: "调用MCP", actionDetail: detail };
|
|
221
298
|
}
|
|
222
|
-
const isMcpTool = name.startsWith(
|
|
223
|
-
name.includes(
|
|
224
|
-
(name.includes(
|
|
225
|
-
name ===
|
|
299
|
+
const isMcpTool = name.startsWith("mcp__") ||
|
|
300
|
+
name.includes("-mcp__") ||
|
|
301
|
+
(name.includes("mcp_") && !name.includes("skill")) ||
|
|
302
|
+
name === "kiropowers";
|
|
226
303
|
if (isMcpTool) {
|
|
227
|
-
let mcpServer =
|
|
228
|
-
let mcpTool =
|
|
229
|
-
if (name.startsWith(
|
|
304
|
+
let mcpServer = "";
|
|
305
|
+
let mcpTool = "";
|
|
306
|
+
if (name.startsWith("mcp__")) {
|
|
230
307
|
// 标准格式: mcp__server__tool
|
|
231
|
-
const parts = toolName.split(
|
|
232
|
-
mcpServer = parts[1] ||
|
|
233
|
-
mcpTool = parts.slice(2).join(
|
|
308
|
+
const parts = toolName.split("__");
|
|
309
|
+
mcpServer = parts[1] || "";
|
|
310
|
+
mcpTool = parts.slice(2).join("__") || "";
|
|
234
311
|
}
|
|
235
|
-
else if (name.includes(
|
|
312
|
+
else if (name.includes("-mcp__")) {
|
|
236
313
|
// 自定义格式: xxx-mcp__tool (如 aws-mcp__get_profile)
|
|
237
|
-
const idx = name.indexOf(
|
|
314
|
+
const idx = name.indexOf("-mcp__");
|
|
238
315
|
mcpServer = name.substring(0, idx + 4); // "xxx-mcp"(保留完整 server 名)
|
|
239
316
|
mcpTool = name.substring(idx + 6); // 去掉 "-mcp__" 前缀,得到工具名
|
|
240
317
|
}
|
|
241
|
-
else if (name ===
|
|
242
|
-
mcpServer =
|
|
243
|
-
mcpTool = (toolInput?.powerName || toolInput?.action) ||
|
|
318
|
+
else if (name === "kiropowers") {
|
|
319
|
+
mcpServer = "kiro";
|
|
320
|
+
mcpTool = (toolInput?.powerName || toolInput?.action) || "";
|
|
244
321
|
}
|
|
245
322
|
else {
|
|
246
323
|
// 其他包含 mcp_ 的格式
|
|
247
|
-
mcpServer =
|
|
324
|
+
mcpServer = "mcp";
|
|
248
325
|
mcpTool = toolName;
|
|
249
326
|
}
|
|
250
327
|
// 格式: server/tool(便于用户识别 MCP server 来源)
|
|
251
|
-
const detail = mcpServer
|
|
252
|
-
return { actionType:
|
|
253
|
-
}
|
|
254
|
-
if (matchesToolName('poll_message', 'get_message')) {
|
|
255
|
-
return { actionType: 'idle', actionLabel: '监听消息', actionDetail: toolName };
|
|
328
|
+
const detail = formatCanonicalMcpDetail(mcpServer, mcpTool) || toolName;
|
|
329
|
+
return { actionType: "mcp", actionLabel: "调用MCP", actionDetail: detail };
|
|
256
330
|
}
|
|
257
331
|
// 文件编辑 (Kiro: strReplace, editCode)
|
|
258
|
-
if (matchesToolName(
|
|
332
|
+
if (matchesToolName("edit", "editfile", "strreplace", "editcode", "apply_patch", "multiedit", "replace")) {
|
|
259
333
|
const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.target_file, toolInput?.targetFile);
|
|
260
|
-
return {
|
|
334
|
+
return {
|
|
335
|
+
actionType: "edit_file",
|
|
336
|
+
actionLabel: "编辑文件",
|
|
337
|
+
actionDetail: filePath,
|
|
338
|
+
};
|
|
261
339
|
}
|
|
262
340
|
// 文件写入 (Kiro: fsWrite)
|
|
263
|
-
if (matchesToolName(
|
|
341
|
+
if (matchesToolName("write", "writefile", "fswrite")) {
|
|
264
342
|
const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.target_file, toolInput?.targetFile);
|
|
265
|
-
return {
|
|
343
|
+
return {
|
|
344
|
+
actionType: "write_file",
|
|
345
|
+
actionLabel: "写入文件",
|
|
346
|
+
actionDetail: filePath,
|
|
347
|
+
};
|
|
266
348
|
}
|
|
267
349
|
// 文件读取 (Kiro: readFile, readCode, readMultipleFiles)
|
|
268
|
-
if (matchesToolName(
|
|
350
|
+
if (matchesToolName("read", "readfile", "readcode", "readmultiplefiles", "read_file")) {
|
|
269
351
|
if (Array.isArray(toolInput?.paths)) {
|
|
270
|
-
return {
|
|
352
|
+
return {
|
|
353
|
+
actionType: "read_file",
|
|
354
|
+
actionLabel: "读取文件",
|
|
355
|
+
actionDetail: `${toolInput.paths.length}个文件`,
|
|
356
|
+
};
|
|
271
357
|
}
|
|
272
358
|
const filePath = firstString(toolInput?.file_path, toolInput?.filePath, toolInput?.path, toolInput?.paths, toolInput?.target_file, toolInput?.targetFile);
|
|
273
|
-
return {
|
|
359
|
+
return {
|
|
360
|
+
actionType: "read_file",
|
|
361
|
+
actionLabel: "读取文件",
|
|
362
|
+
actionDetail: filePath,
|
|
363
|
+
};
|
|
274
364
|
}
|
|
275
365
|
// Bash 命令 (Kiro: executePwsh, controlPwshProcess)
|
|
276
|
-
if (matchesToolName(
|
|
366
|
+
if (matchesToolName("bash", "shell", "command", "executepwsh", "controlpwshprocess", "execute", "run_command", "execute_command")) {
|
|
277
367
|
const command = firstString(toolInput?.command, toolInput?.cmd, toolInput?.argv);
|
|
278
|
-
return {
|
|
368
|
+
return {
|
|
369
|
+
actionType: "bash",
|
|
370
|
+
actionLabel: "执行命令",
|
|
371
|
+
actionDetail: command?.slice(0, 50),
|
|
372
|
+
};
|
|
279
373
|
}
|
|
280
374
|
// 文件搜索 (Kiro: grepSearch, fileSearch)
|
|
281
|
-
if (name ===
|
|
375
|
+
if (name === "glob" ||
|
|
376
|
+
name === "grep" ||
|
|
377
|
+
name === "find" ||
|
|
378
|
+
name === "search" ||
|
|
379
|
+
name === "grepsearch" ||
|
|
380
|
+
name === "filesearch") {
|
|
282
381
|
const query = (toolInput?.query || toolInput?.pattern);
|
|
283
|
-
return {
|
|
382
|
+
return {
|
|
383
|
+
actionType: "search",
|
|
384
|
+
actionLabel: "搜索文件",
|
|
385
|
+
actionDetail: query?.slice(0, 30),
|
|
386
|
+
};
|
|
284
387
|
}
|
|
285
388
|
// 网络操作 (Kiro: webFetch, remote_web_search)
|
|
286
|
-
if (name ===
|
|
389
|
+
if (name === "webfetch" ||
|
|
390
|
+
name === "websearch" ||
|
|
391
|
+
name === "fetch" ||
|
|
392
|
+
name === "http" ||
|
|
393
|
+
name === "remote_web_search") {
|
|
287
394
|
const url = (toolInput?.url || toolInput?.query);
|
|
288
|
-
return {
|
|
395
|
+
return {
|
|
396
|
+
actionType: "web",
|
|
397
|
+
actionLabel: "网络请求",
|
|
398
|
+
actionDetail: url?.slice(0, 50),
|
|
399
|
+
};
|
|
289
400
|
}
|
|
290
401
|
// LSP 操作
|
|
291
|
-
if (name.startsWith(
|
|
292
|
-
return { actionType:
|
|
402
|
+
if (name.startsWith("lsp") || name.includes("language_server")) {
|
|
403
|
+
return { actionType: "lsp", actionLabel: "LSP操作" };
|
|
293
404
|
}
|
|
294
405
|
// Notebook 编辑
|
|
295
|
-
if (name ===
|
|
296
|
-
return { actionType:
|
|
406
|
+
if (name === "notebookedit" || name.includes("notebook")) {
|
|
407
|
+
return { actionType: "notebook", actionLabel: "编辑Notebook" };
|
|
297
408
|
}
|
|
298
409
|
// 其他工具
|
|
299
|
-
return {
|
|
410
|
+
return {
|
|
411
|
+
actionType: "other",
|
|
412
|
+
actionLabel: "调用工具",
|
|
413
|
+
actionDetail: toolName,
|
|
414
|
+
};
|
|
300
415
|
}
|
|
301
416
|
// ============ Adapter 基类 ============
|
|
302
417
|
/**
|
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
import { describe, expect, it } from 'vitest';
|
|
2
2
|
import { getToolActionInfo, isTransientClipboardPasteFilePath, redactTransientClipboardPastePaths, redactTransientClipboardPastePathsInValue, } from './types.js';
|
|
3
3
|
describe('getToolActionInfo', () => {
|
|
4
|
-
it('reports poll_message as
|
|
4
|
+
it('reports poll_message as an idle MCP listening tool call', () => {
|
|
5
5
|
expect(getToolActionInfo('poll_message')).toEqual({
|
|
6
|
-
actionType: '
|
|
7
|
-
actionLabel: '
|
|
6
|
+
actionType: 'idle',
|
|
7
|
+
actionLabel: '监听消息',
|
|
8
|
+
actionDetail: 'aws-mcp/poll_message',
|
|
9
|
+
});
|
|
10
|
+
});
|
|
11
|
+
it('reports prefixed poll_message as a canonical idle MCP listening tool call', () => {
|
|
12
|
+
expect(getToolActionInfo('aws-mcp__poll_message')).toEqual({
|
|
13
|
+
actionType: 'idle',
|
|
14
|
+
actionLabel: '监听消息',
|
|
8
15
|
actionDetail: 'aws-mcp/poll_message',
|
|
9
16
|
});
|
|
10
17
|
});
|
|
@@ -59,7 +59,7 @@ async function fetchRemoteModelList(baseUrl, apiKey) {
|
|
|
59
59
|
return normalizeListedModels(anthropicResponse.data);
|
|
60
60
|
}
|
|
61
61
|
aiSourcesRouter.post('/cc-switch/apply-ai-sources', validateToken, async (req, res) => {
|
|
62
|
-
const { agentId, template, ccSwitchEnabledTools, acodeTemplateId, claudecodeTemplateId, codexTemplateId, opencodeTemplateId, acode, claudecode, codex, opencode, } = req.body || {};
|
|
62
|
+
const { agentId, template, ccSwitchEnabledTools, acodeTemplateId, claudecodeTemplateId, codexTemplateId, opencodeTemplateId, acodeModelEffort, claudecodeModelEffort, codexModelEffort, opencodeModelEffort, acode, claudecode, codex, opencode, } = req.body || {};
|
|
63
63
|
if (!agentId) {
|
|
64
64
|
res.status(400).json({ error: 'agentId is required' });
|
|
65
65
|
return;
|
|
@@ -81,6 +81,10 @@ aiSourcesRouter.post('/cc-switch/apply-ai-sources', validateToken, async (req, r
|
|
|
81
81
|
claudecodeTemplateId: String(claudecodeTemplateId || ''),
|
|
82
82
|
codexTemplateId: String(codexTemplateId || ''),
|
|
83
83
|
opencodeTemplateId: String(opencodeTemplateId || ''),
|
|
84
|
+
acodeModelEffort: String(acodeModelEffort || ''),
|
|
85
|
+
claudecodeModelEffort: String(claudecodeModelEffort || ''),
|
|
86
|
+
codexModelEffort: String(codexModelEffort || ''),
|
|
87
|
+
opencodeModelEffort: String(opencodeModelEffort || ''),
|
|
84
88
|
generatedFromUnifiedTemplate: appliedSummary.generatedFromUnifiedTemplate,
|
|
85
89
|
applied: appliedSummary.applied,
|
|
86
90
|
skippedTools: appliedSummary.skippedTools,
|
|
@@ -76,6 +76,14 @@ function buildModelListCandidates(baseUrl, protocol) {
|
|
|
76
76
|
: [`${normalizedBaseUrl}/models`, `${normalizedBaseUrl}/v1/models`];
|
|
77
77
|
return [...openAiCandidates, `${normalizedBaseUrl}/v1/models`];
|
|
78
78
|
}
|
|
79
|
+
function buildApplyAiSourcesResponse(body) {
|
|
80
|
+
return {
|
|
81
|
+
acodeModelEffort: String(body.acodeModelEffort || ''),
|
|
82
|
+
claudecodeModelEffort: String(body.claudecodeModelEffort || ''),
|
|
83
|
+
codexModelEffort: String(body.codexModelEffort || ''),
|
|
84
|
+
opencodeModelEffort: String(body.opencodeModelEffort || ''),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
79
87
|
describe('ai source unified config generation', () => {
|
|
80
88
|
it('builds tool-specific configs from unified template fields', () => {
|
|
81
89
|
assert.deepEqual(buildUnifiedToolConfig({
|
|
@@ -145,4 +153,17 @@ describe('ai source unified config generation', () => {
|
|
|
145
153
|
model: 'gpt-test',
|
|
146
154
|
}).opencode.provider.default.options.baseURL).toBe('https://octopus.721579.xyz/v1');
|
|
147
155
|
});
|
|
156
|
+
it('echoes selected model effort metadata in apply-ai-sources responses', () => {
|
|
157
|
+
expect(buildApplyAiSourcesResponse({
|
|
158
|
+
acodeModelEffort: 'xhigh',
|
|
159
|
+
claudecodeModelEffort: 'high',
|
|
160
|
+
codexModelEffort: 'max',
|
|
161
|
+
opencodeModelEffort: 'high',
|
|
162
|
+
})).toEqual({
|
|
163
|
+
acodeModelEffort: 'xhigh',
|
|
164
|
+
claudecodeModelEffort: 'high',
|
|
165
|
+
codexModelEffort: 'max',
|
|
166
|
+
opencodeModelEffort: 'high',
|
|
167
|
+
});
|
|
168
|
+
});
|
|
148
169
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/routes/instance.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"instance.d.ts","sourceRoot":"","sources":["../../src/routes/instance.ts"],"names":[],"mappings":"AA0BA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAqBrD,wBAAgB,qBAAqB,CACnC,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,gBAAgB,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,GAC/D,IAAI,CAEN;AAED,eAAO,MAAM,cAAc,4CAAW,CAAC;AAsJvC,wBAAgB,4BAA4B,CAAC,gBAAgB,EAAE,MAAM,GAAG;IACtE,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE;QACJ,EAAE,EAAE,OAAO,CAAC;QACZ,aAAa,CAAC,EAAE,SAAS,CAAC;QAC1B,oBAAoB,EAAE,OAAO,CAAC;QAC9B,qBAAqB,EAAE,OAAO,CAAC;QAC/B,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;CACH,CAqCA;AAED,MAAM,WAAW,4BAA4B;IAC3C,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;CACd;AAED,UAAU,iCAAiC;IACzC,EAAE,EAAE,KAAK,CAAC;IACV,KAAK,EAAE,8BAA8B,CAAC;IACtC,YAAY,EAAE,gBAAgB,CAAC;IAC/B,aAAa,EAAE,SAAS,CAAC;IACzB,gBAAgB,EAAE,MAAM,CAAC;IACzB,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;GAGG;AACH,wBAAgB,iCAAiC,CAC/C,KAAK,EAAE,KAAK,EACZ,0BAA0B,EAAE,MAAM,GACjC,4BAA4B,CAiB9B;AAoDD;;;GAGG;AACH,wBAAgB,sCAAsC,CACpD,UAAU,EAAE,MAAM,EAAE,GACnB,iCAAiC,CAUnC;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAAC,YAAY,EAAE,MAAM,EAAE,GAAG,MAAM,EAAE,CAQhF;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAC1C,cAAc,EAAE,MAAM,EAAE,EACxB,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC5C,MAAM,CAaR;AAED,wBAAgB,mBAAmB,CACjC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,iBAAiB,CAAC,GAC5C,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,iBAAiB,EAAE,WAAW,GAAG,SAAS,GAAG,YAAY,GAAG,OAAO,CAAC,CAAC,CAY3F"}
|
package/dist/routes/instance.js
CHANGED
|
@@ -2,16 +2,15 @@ import { Router } from "express";
|
|
|
2
2
|
import axios from "axios";
|
|
3
3
|
import { spawn } from "node:child_process";
|
|
4
4
|
import { createHash, timingSafeEqual } from "node:crypto";
|
|
5
|
-
import { validateToken } from "../middleware/auth.js";
|
|
6
5
|
import { resolveSchedulerBaseUrlFrom } from "../config.js";
|
|
7
|
-
import {
|
|
8
|
-
import { loadInstanceState, saveInstanceState, } from "../services/instance-state.js";
|
|
9
|
-
import { initInstance } from "../services/instance-init-service.js";
|
|
10
|
-
import { discoverCcSwitchConfiguredItems, loadCcSwitchSdk } from "../services/cc-switch-sdk.js";
|
|
11
|
-
import { syncLegacyStateFromSdk } from "../services/instance-init-service.js";
|
|
12
|
-
import { detectToolStatuses, ensureToolsInstalled, SUPPORTED_INSTALLABLE_TOOLS, SUPPORTED_UNINSTALLABLE_TOOLS, uninstallTools, } from "../services/tool-installer.js";
|
|
6
|
+
import { validateToken } from "../middleware/auth.js";
|
|
13
7
|
import { getConfiguredConnectionKeys, requestRuntimeAccessTokenRefreshForServer } from "../services/auto-register.js";
|
|
8
|
+
import { discoverCcSwitchConfiguredItems, loadCcSwitchSdk } from "../services/cc-switch-sdk.js";
|
|
9
|
+
import { initInstance, syncLegacyStateFromSdk } from "../services/instance-init-service.js";
|
|
14
10
|
import { createBridgeLifecycleOperation, deliverPendingBridgeLifecycleNotifications, upsertBridgeLifecycleOperation, } from "../services/lifecycle-state.js";
|
|
11
|
+
import { loadInstanceState, saveInstanceState, } from "../services/instance-state.js";
|
|
12
|
+
import { getRuntimeAccessToken } from "../services/runtime-binding.js";
|
|
13
|
+
import { detectToolStatuses, ensureToolsInstalled, SUPPORTED_INSTALLABLE_TOOLS, SUPPORTED_UNINSTALLABLE_TOOLS, uninstallTools, } from "../services/tool-installer.js";
|
|
15
14
|
import { createLogger } from "../utils/logger.js";
|
|
16
15
|
const log = createLogger("instance");
|
|
17
16
|
const PANEL_TOOL_STATUS_KEYS = ["acode", "claude", "opencode", "codex"];
|
|
@@ -102,7 +102,7 @@ runtimeBindingRouter.post("/binding/claim-agent", (req, res) => {
|
|
|
102
102
|
res.status(403).json({ ok: false, error: "loopback request required" });
|
|
103
103
|
return;
|
|
104
104
|
}
|
|
105
|
-
const { workspacePath, serverUrl } = req.body || {};
|
|
105
|
+
const { agentId, workspacePath, serverUrl } = req.body || {};
|
|
106
106
|
if (!workspacePath || !String(workspacePath).trim()) {
|
|
107
107
|
res.status(400).json({ ok: false, error: "workspacePath is required" });
|
|
108
108
|
return;
|
|
@@ -111,7 +111,7 @@ runtimeBindingRouter.post("/binding/claim-agent", (req, res) => {
|
|
|
111
111
|
res.status(400).json({ ok: false, error: "serverUrl is required" });
|
|
112
112
|
return;
|
|
113
113
|
}
|
|
114
|
-
const claimed = claimMcpLaunchBinding({ workspacePath, serverUrl });
|
|
114
|
+
const claimed = claimMcpLaunchBinding({ agentId, workspacePath, serverUrl });
|
|
115
115
|
if (!claimed) {
|
|
116
116
|
res.status(404).json({
|
|
117
117
|
ok: false,
|