@seandong/seno 0.1.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 +70 -0
- package/dist/agent/conversation.d.ts +39 -0
- package/dist/agent/conversation.js +60 -0
- package/dist/agent/conversation.js.map +1 -0
- package/dist/agent/loop.d.ts +41 -0
- package/dist/agent/loop.js +203 -0
- package/dist/agent/loop.js.map +1 -0
- package/dist/agent/session.d.ts +63 -0
- package/dist/agent/session.js +135 -0
- package/dist/agent/session.js.map +1 -0
- package/dist/cli/commands.d.ts +52 -0
- package/dist/cli/commands.js +667 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/logger.d.ts +38 -0
- package/dist/cli/logger.js +79 -0
- package/dist/cli/logger.js.map +1 -0
- package/dist/cli/output.d.ts +75 -0
- package/dist/cli/output.js +305 -0
- package/dist/cli/output.js.map +1 -0
- package/dist/cli/prompt.d.ts +30 -0
- package/dist/cli/prompt.js +196 -0
- package/dist/cli/prompt.js.map +1 -0
- package/dist/cli/repl.d.ts +27 -0
- package/dist/cli/repl.js +485 -0
- package/dist/cli/repl.js.map +1 -0
- package/dist/commands/init.d.ts +4 -0
- package/dist/commands/init.js +170 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/commands/model.d.ts +10 -0
- package/dist/commands/model.js +270 -0
- package/dist/commands/model.js.map +1 -0
- package/dist/config/manager.d.ts +67 -0
- package/dist/config/manager.js +194 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/config/types.d.ts +98 -0
- package/dist/config/types.js +2 -0
- package/dist/config/types.js.map +1 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.js +54 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +185 -0
- package/dist/index.js.map +1 -0
- package/dist/llm/anthropic.d.ts +27 -0
- package/dist/llm/anthropic.js +189 -0
- package/dist/llm/anthropic.js.map +1 -0
- package/dist/llm/factory.d.ts +47 -0
- package/dist/llm/factory.js +163 -0
- package/dist/llm/factory.js.map +1 -0
- package/dist/llm/openai-codex.d.ts +45 -0
- package/dist/llm/openai-codex.js +398 -0
- package/dist/llm/openai-codex.js.map +1 -0
- package/dist/llm/openai.d.ts +16 -0
- package/dist/llm/openai.js +288 -0
- package/dist/llm/openai.js.map +1 -0
- package/dist/llm/provider.d.ts +19 -0
- package/dist/llm/provider.js +2 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/llm/types.d.ts +102 -0
- package/dist/llm/types.js +2 -0
- package/dist/llm/types.js.map +1 -0
- package/dist/mcp/bridge.d.ts +30 -0
- package/dist/mcp/bridge.js +73 -0
- package/dist/mcp/bridge.js.map +1 -0
- package/dist/mcp/config.d.ts +6 -0
- package/dist/mcp/config.js +26 -0
- package/dist/mcp/config.js.map +1 -0
- package/dist/mcp/manager.d.ts +54 -0
- package/dist/mcp/manager.js +171 -0
- package/dist/mcp/manager.js.map +1 -0
- package/dist/prompts/system.d.ts +14 -0
- package/dist/prompts/system.js +194 -0
- package/dist/prompts/system.js.map +1 -0
- package/dist/skills/loader.d.ts +7 -0
- package/dist/skills/loader.js +81 -0
- package/dist/skills/loader.js.map +1 -0
- package/dist/skills/registry.d.ts +48 -0
- package/dist/skills/registry.js +104 -0
- package/dist/skills/registry.js.map +1 -0
- package/dist/skills/sync.d.ts +34 -0
- package/dist/skills/sync.js +179 -0
- package/dist/skills/sync.js.map +1 -0
- package/dist/skills/types.d.ts +29 -0
- package/dist/skills/types.js +2 -0
- package/dist/skills/types.js.map +1 -0
- package/dist/tools/ask.d.ts +16 -0
- package/dist/tools/ask.js +57 -0
- package/dist/tools/ask.js.map +1 -0
- package/dist/tools/registry.d.ts +54 -0
- package/dist/tools/registry.js +114 -0
- package/dist/tools/registry.js.map +1 -0
- package/dist/tools/shell.d.ts +10 -0
- package/dist/tools/shell.js +131 -0
- package/dist/tools/shell.js.map +1 -0
- package/dist/tools/ssh.d.ts +40 -0
- package/dist/tools/ssh.js +302 -0
- package/dist/tools/ssh.js.map +1 -0
- package/dist/tools/types.d.ts +20 -0
- package/dist/tools/types.js +2 -0
- package/dist/tools/types.js.map +1 -0
- package/dist/utils/retry.d.ts +20 -0
- package/dist/utils/retry.js +33 -0
- package/dist/utils/retry.js.map +1 -0
- package/package.json +51 -0
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
+
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
3
|
+
import { logger } from '../cli/logger.js';
|
|
4
|
+
/**
|
|
5
|
+
* MCP 多服务器连接管理器
|
|
6
|
+
* 管理多个 MCP Server 的生命周期:连接、工具发现、调用、断开
|
|
7
|
+
*/
|
|
8
|
+
export class MCPManager {
|
|
9
|
+
connections = new Map();
|
|
10
|
+
_userInfo = null;
|
|
11
|
+
/**
|
|
12
|
+
* 当前用户信息(从 ONES MCP who_am_i 获取)
|
|
13
|
+
*/
|
|
14
|
+
get userInfo() {
|
|
15
|
+
return this._userInfo;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* 并行连接所有 MCP 服务器
|
|
19
|
+
* required 服务器连接失败会抛出错误
|
|
20
|
+
* optional 服务器连接失败仅输出警告
|
|
21
|
+
*/
|
|
22
|
+
async connectAll(servers) {
|
|
23
|
+
const results = await Promise.allSettled(servers.map(async (server) => {
|
|
24
|
+
try {
|
|
25
|
+
await this.connectServer(server);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (server.required) {
|
|
29
|
+
throw new Error(`必需的 MCP 服务器 "${server.name}" 连接失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
30
|
+
}
|
|
31
|
+
console.warn(`⚠ MCP 服务器 "${server.name}" 连接失败(可选),跳过: ${error instanceof Error ? error.message : String(error)}`);
|
|
32
|
+
}
|
|
33
|
+
}));
|
|
34
|
+
// 检查 required 服务器是否有连接失败
|
|
35
|
+
for (const result of results) {
|
|
36
|
+
if (result.status === 'rejected') {
|
|
37
|
+
throw result.reason;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
// ONES MCP 连接成功后,获取用户身份
|
|
41
|
+
if (this.connections.has('ones')) {
|
|
42
|
+
try {
|
|
43
|
+
this._userInfo = await this.fetchUserInfo();
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
console.warn(`⚠ 获取用户信息失败: ${error instanceof Error ? error.message : String(error)}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* 连接单个 MCP 服务器
|
|
52
|
+
*/
|
|
53
|
+
async connectServer(server) {
|
|
54
|
+
const transport = new StdioClientTransport({
|
|
55
|
+
command: server.command,
|
|
56
|
+
args: server.args,
|
|
57
|
+
env: {
|
|
58
|
+
...process.env,
|
|
59
|
+
...server.env,
|
|
60
|
+
},
|
|
61
|
+
stderr: 'pipe',
|
|
62
|
+
});
|
|
63
|
+
const client = new Client({ name: 'seno', version: '0.1.0' }, { capabilities: {} });
|
|
64
|
+
await client.connect(transport);
|
|
65
|
+
// 发现工具列表
|
|
66
|
+
const response = await client.listTools();
|
|
67
|
+
const tools = response.tools;
|
|
68
|
+
this.connections.set(server.name, { client, transport, tools, config: server });
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* 通过 ONES MCP 的 who_am_i 获取用户身份
|
|
72
|
+
*/
|
|
73
|
+
async fetchUserInfo() {
|
|
74
|
+
const result = await this.callTool('ones', 'who_am_i', {});
|
|
75
|
+
// who_am_i 返回的是 content 数组,提取文本内容
|
|
76
|
+
const content = result;
|
|
77
|
+
if (content.content && content.content.length > 0) {
|
|
78
|
+
const textContent = content.content.find((c) => c.type === 'text');
|
|
79
|
+
if (textContent?.text) {
|
|
80
|
+
const data = JSON.parse(textContent.text);
|
|
81
|
+
return {
|
|
82
|
+
id: data.id || data.uuid || '',
|
|
83
|
+
name: data.name || data.username || '',
|
|
84
|
+
email: data.email || '',
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
throw new Error('who_am_i 返回数据格式异常');
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* 获取指定服务器的工具列表
|
|
92
|
+
*/
|
|
93
|
+
getTools(serverName) {
|
|
94
|
+
const conn = this.connections.get(serverName);
|
|
95
|
+
if (!conn) {
|
|
96
|
+
throw new Error(`MCP 服务器 "${serverName}" 未连接`);
|
|
97
|
+
}
|
|
98
|
+
return conn.tools;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* 获取所有已连接服务器的工具列表
|
|
102
|
+
*/
|
|
103
|
+
getAllTools() {
|
|
104
|
+
const result = new Map();
|
|
105
|
+
for (const [name, conn] of this.connections) {
|
|
106
|
+
result.set(name, conn.tools);
|
|
107
|
+
}
|
|
108
|
+
return result;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* 获取所有已连接的服务器名称
|
|
112
|
+
*/
|
|
113
|
+
getConnectedServers() {
|
|
114
|
+
return Array.from(this.connections.keys());
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* 调用指定服务器的工具
|
|
118
|
+
* 调用失败时尝试断开重连一次
|
|
119
|
+
*/
|
|
120
|
+
async callTool(serverName, toolName, args) {
|
|
121
|
+
const conn = this.connections.get(serverName);
|
|
122
|
+
if (!conn) {
|
|
123
|
+
throw new Error(`MCP 服务器 "${serverName}" 未连接`);
|
|
124
|
+
}
|
|
125
|
+
try {
|
|
126
|
+
return await conn.client.callTool({
|
|
127
|
+
name: toolName,
|
|
128
|
+
arguments: args,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
catch (error) {
|
|
132
|
+
logger.debug('mcp', `Tool call ${toolName} failed on ${serverName}, attempting reconnect...`);
|
|
133
|
+
// 尝试重连一次
|
|
134
|
+
try {
|
|
135
|
+
await conn.transport.close().catch(() => { });
|
|
136
|
+
this.connections.delete(serverName);
|
|
137
|
+
await this.connectServer(conn.config);
|
|
138
|
+
// 重连成功,重试调用
|
|
139
|
+
const newConn = this.connections.get(serverName);
|
|
140
|
+
if (newConn) {
|
|
141
|
+
return await newConn.client.callTool({
|
|
142
|
+
name: toolName,
|
|
143
|
+
arguments: args,
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
catch (reconnectError) {
|
|
148
|
+
logger.error('mcp', `Reconnection failed for ${serverName}: ${reconnectError instanceof Error ? reconnectError.message : String(reconnectError)}`);
|
|
149
|
+
}
|
|
150
|
+
// 重连失败或重试失败,抛出原始错误
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
/**
|
|
155
|
+
* 断开所有 MCP 连接,清理子进程
|
|
156
|
+
*/
|
|
157
|
+
async disconnectAll() {
|
|
158
|
+
const closePromises = Array.from(this.connections.entries()).map(async ([name, conn]) => {
|
|
159
|
+
try {
|
|
160
|
+
await conn.transport.close();
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
console.warn(`⚠ 关闭 MCP 服务器 "${name}" 时出错: ${error instanceof Error ? error.message : String(error)}`);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
await Promise.allSettled(closePromises);
|
|
167
|
+
this.connections.clear();
|
|
168
|
+
this._userInfo = null;
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
//# sourceMappingURL=manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/mcp/manager.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SkillMeta } from '../skills/types.js';
|
|
2
|
+
import type { UserInfo } from '../mcp/manager.js';
|
|
3
|
+
/**
|
|
4
|
+
* System Prompt 构建上下文
|
|
5
|
+
*/
|
|
6
|
+
export interface SystemPromptContext {
|
|
7
|
+
userInfo: UserInfo | null;
|
|
8
|
+
skills: SkillMeta[];
|
|
9
|
+
mcpTools: string[];
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 构建完整的 System Prompt
|
|
13
|
+
*/
|
|
14
|
+
export declare function buildSystemPrompt(context: SystemPromptContext): string;
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 构建完整的 System Prompt
|
|
3
|
+
*/
|
|
4
|
+
export function buildSystemPrompt(context) {
|
|
5
|
+
const sections = [
|
|
6
|
+
buildRoleSection(),
|
|
7
|
+
buildUserSection(context.userInfo),
|
|
8
|
+
buildSkillsSection(context.skills),
|
|
9
|
+
buildKnowledgeSection(),
|
|
10
|
+
buildMCPToolsSection(context.mcpTools),
|
|
11
|
+
buildSafetySection(),
|
|
12
|
+
buildGuidelinesSection(),
|
|
13
|
+
];
|
|
14
|
+
return sections.join('\n\n');
|
|
15
|
+
}
|
|
16
|
+
// ─── Section 1: 角色定义 ───
|
|
17
|
+
function buildRoleSection() {
|
|
18
|
+
return `你是 SENO,ONES 企业内部 AI 助手。你的职责是帮助 ONES 团队成员完成日常工作,包括:
|
|
19
|
+
- 运维自动化(ONES 安装、升级、备份、巡检等)
|
|
20
|
+
- 插件开发(环境搭建、仓库初始化、文档查询)
|
|
21
|
+
- 知识检索(企业内部 Wiki 和外部文档查询)
|
|
22
|
+
- ONES 协作(任务管理、项目协作、Wiki、工时记录)
|
|
23
|
+
|
|
24
|
+
默认使用中文交流,跟随用户的语言偏好。回答应简洁专业,直接解决问题。`;
|
|
25
|
+
}
|
|
26
|
+
// ─── Section 2: 用户身份 ───
|
|
27
|
+
function buildUserSection(userInfo) {
|
|
28
|
+
if (!userInfo) {
|
|
29
|
+
return '当前用户:未登录';
|
|
30
|
+
}
|
|
31
|
+
return `当前用户:${userInfo.name}(${userInfo.email})`;
|
|
32
|
+
}
|
|
33
|
+
// ─── Section 3: 技能索引 ───
|
|
34
|
+
function buildSkillsSection(skills) {
|
|
35
|
+
if (skills.length === 0) {
|
|
36
|
+
return '## 可用技能\n\n暂无可用技能。';
|
|
37
|
+
}
|
|
38
|
+
const header = `## 可用技能
|
|
39
|
+
|
|
40
|
+
以下是你可以执行的运维和开发技能。当用户需要执行这些操作时,先用 list_skills 搜索匹配的技能,再用 load_skill 加载完整的执行步骤。
|
|
41
|
+
|
|
42
|
+
| 技能 | 域 | 说明 |
|
|
43
|
+
|------|-----|------|`;
|
|
44
|
+
const rows = skills.map((s) => `| ${s.name} | ${s.domain} | ${s.description} |`);
|
|
45
|
+
return [header, ...rows].join('\n');
|
|
46
|
+
}
|
|
47
|
+
// ─── Section 4: 知识检索指引 ───
|
|
48
|
+
function buildKnowledgeSection() {
|
|
49
|
+
return `## 知识检索
|
|
50
|
+
|
|
51
|
+
当用户提出需要查阅文档或知识的问题时,按以下策略选择数据源:
|
|
52
|
+
|
|
53
|
+
### 内部知识(ONES 企业 Wiki)
|
|
54
|
+
使用 ones__search_for_references_in_wiki 工具搜索企业内部 Wiki 知识库。
|
|
55
|
+
适用场景:公司内部流程、团队规范、项目文档、产品知识等。
|
|
56
|
+
- 可选参数 spaceID:限定搜索范围到特定 Wiki 空间。如需指定空间,先调用 ones__get_space_list 获取空间列表。
|
|
57
|
+
- 如果搜索结果摘要不够详细,可调用 ones__get_page_details 获取完整页面内容。
|
|
58
|
+
|
|
59
|
+
### ONES 运维实施手册
|
|
60
|
+
使用 context7__query-docs 工具,libraryId 为 "/websites/opsdoc_ones_cn"。
|
|
61
|
+
适用场景:ONES 安装部署、升级维护、故障排查、运维操作等。
|
|
62
|
+
无需先调用 resolve-library-id,直接使用此固定 ID。
|
|
63
|
+
|
|
64
|
+
### ONES 开发者平台文档
|
|
65
|
+
使用 context7__query-docs 工具,libraryId 为 "/websites/developer_ones_cn"。
|
|
66
|
+
适用场景:ONES 插件开发、OpenAPI 接口、开发者指南等。
|
|
67
|
+
无需先调用 resolve-library-id,直接使用此固定 ID。
|
|
68
|
+
|
|
69
|
+
### 通用编程文档
|
|
70
|
+
对于第三方库和框架文档,按两步操作:
|
|
71
|
+
1. 先调用 context7__resolve-library-id 获取库的 Context7 ID
|
|
72
|
+
2. 再调用 context7__query-docs 查询具体文档内容
|
|
73
|
+
适用场景:React、Node.js、Python 等通用编程库的文档查询。`;
|
|
74
|
+
}
|
|
75
|
+
// ─── Section 5: MCP 工具使用 ───
|
|
76
|
+
function buildMCPToolsSection(mcpTools) {
|
|
77
|
+
if (mcpTools.length === 0) {
|
|
78
|
+
return '## 平台工具\n\n暂无可用的 MCP 工具。';
|
|
79
|
+
}
|
|
80
|
+
// 按 server 前缀分组
|
|
81
|
+
const grouped = new Map();
|
|
82
|
+
for (const tool of mcpTools) {
|
|
83
|
+
const sepIdx = tool.indexOf('__');
|
|
84
|
+
if (sepIdx === -1)
|
|
85
|
+
continue;
|
|
86
|
+
const server = tool.substring(0, sepIdx);
|
|
87
|
+
const name = tool.substring(sepIdx + 2);
|
|
88
|
+
if (!grouped.has(server)) {
|
|
89
|
+
grouped.set(server, []);
|
|
90
|
+
}
|
|
91
|
+
grouped.get(server).push(name);
|
|
92
|
+
}
|
|
93
|
+
const sections = ['## 平台工具'];
|
|
94
|
+
for (const [server, tools] of grouped) {
|
|
95
|
+
if (server === 'ones') {
|
|
96
|
+
sections.push(buildOnesToolsSection(tools));
|
|
97
|
+
}
|
|
98
|
+
else if (server === 'context7') {
|
|
99
|
+
sections.push(buildContext7ToolsSection(tools));
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
// 其他 MCP server
|
|
103
|
+
sections.push(`### ${server}\n可用工具:${tools.join('、')}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return sections.join('\n\n');
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* ONES MCP 工具分类说明
|
|
110
|
+
*/
|
|
111
|
+
function buildOnesToolsSection(tools) {
|
|
112
|
+
const categories = [
|
|
113
|
+
{ title: '任务管理', pattern: /issue|onesql/, items: [] },
|
|
114
|
+
{ title: '项目管理', pattern: /project/, items: [] },
|
|
115
|
+
{ title: 'Wiki 知识库', pattern: /wiki|page|space/, items: [] },
|
|
116
|
+
{ title: '用户信息', pattern: /user|who_am_i/, items: [] },
|
|
117
|
+
{ title: '工时管理', pattern: /manhour|workhour/, items: [] },
|
|
118
|
+
{ title: '其他', pattern: /.*/, items: [] },
|
|
119
|
+
];
|
|
120
|
+
for (const tool of tools) {
|
|
121
|
+
let matched = false;
|
|
122
|
+
for (const cat of categories) {
|
|
123
|
+
if (cat.title !== '其他' && cat.pattern.test(tool)) {
|
|
124
|
+
cat.items.push(tool);
|
|
125
|
+
matched = true;
|
|
126
|
+
break;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
if (!matched) {
|
|
130
|
+
categories[categories.length - 1].items.push(tool);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const lines = ['### ONES 平台工具'];
|
|
134
|
+
// 工时特殊说明
|
|
135
|
+
const hasManHour = categories.some((c) => c.title === '工时管理' && c.items.length > 0);
|
|
136
|
+
for (const cat of categories) {
|
|
137
|
+
if (cat.items.length === 0)
|
|
138
|
+
continue;
|
|
139
|
+
lines.push(`\n**${cat.title}**`);
|
|
140
|
+
for (const item of cat.items) {
|
|
141
|
+
lines.push(`- ones__${item}`);
|
|
142
|
+
}
|
|
143
|
+
if (cat.title === '工时管理' && hasManHour) {
|
|
144
|
+
lines.push('\n> 工时操作前必须先调用 ones__get_manhour_mode 确定模式(simple/summary),再使用对应模式的工具。');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return lines.join('\n');
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Context7 工具说明
|
|
151
|
+
*/
|
|
152
|
+
function buildContext7ToolsSection(tools) {
|
|
153
|
+
const lines = ['### Context7 文档检索工具'];
|
|
154
|
+
for (const tool of tools) {
|
|
155
|
+
lines.push(`- context7__${tool}`);
|
|
156
|
+
}
|
|
157
|
+
return lines.join('\n');
|
|
158
|
+
}
|
|
159
|
+
// ─── Section 6: 安全约束 ───
|
|
160
|
+
function buildSafetySection() {
|
|
161
|
+
return `## 安全规则
|
|
162
|
+
|
|
163
|
+
### 危险操作确认
|
|
164
|
+
以下操作必须先调用 ask_user 工具获取用户确认后才能执行:
|
|
165
|
+
- 卸载或删除操作(uninstall、rm -rf、drop 等)
|
|
166
|
+
- 数据库变更(SQL UPDATE、DELETE、TRUNCATE)
|
|
167
|
+
- 服务启停(systemctl stop、k3s-killall.sh)
|
|
168
|
+
- 不可逆的配置修改
|
|
169
|
+
- 任何可能导致数据丢失的操作
|
|
170
|
+
|
|
171
|
+
### 命令执行安全
|
|
172
|
+
- 执行 Shell/SSH 命令前,确认命令的影响范围
|
|
173
|
+
- 避免执行包含敏感信息(密码、密钥)的命令
|
|
174
|
+
- 长时间运行的命令应告知用户预期耗时
|
|
175
|
+
- **ssh_execute 失败时必须立即停止当前任务**,向用户报告错误原因,不要继续执行后续步骤。SSH 连接失败意味着目标服务器不可达,继续执行没有意义
|
|
176
|
+
|
|
177
|
+
### 插件开发限制
|
|
178
|
+
在插件开发辅助场景中:
|
|
179
|
+
- 仅协助环境搭建、仓库初始化和文档查询
|
|
180
|
+
- 不生成插件业务代码
|
|
181
|
+
- 引导用户参考开发者平台文档自行开发`;
|
|
182
|
+
}
|
|
183
|
+
// ─── Section 7: 行为指南 ───
|
|
184
|
+
function buildGuidelinesSection() {
|
|
185
|
+
return `## 行为指南
|
|
186
|
+
|
|
187
|
+
1. 执行 Skill 时严格按照 SKILL.md 中定义的步骤顺序执行,不跳过步骤
|
|
188
|
+
2. 每个步骤执行后验证结果,确认成功后再进入下一步
|
|
189
|
+
3. 遇到错误时,分析原因并尝试修复,无法修复则告知用户
|
|
190
|
+
4. 使用 ONESQL 查询时,严格遵循 ONESQL 语法(参考工具描述中的规范)
|
|
191
|
+
5. 工时操作前必须先获取工时模式(simple/summary)
|
|
192
|
+
6. 对于不确定的操作,主动询问用户而不是猜测`;
|
|
193
|
+
}
|
|
194
|
+
//# sourceMappingURL=system.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"system.js","sourceRoot":"","sources":["../../src/prompts/system.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import matter from 'gray-matter';
|
|
2
|
+
import { readFile } from 'node:fs/promises';
|
|
3
|
+
import { existsSync } from 'node:fs';
|
|
4
|
+
import { join } from 'node:path';
|
|
5
|
+
/**
|
|
6
|
+
* 创建 load_skill 工具
|
|
7
|
+
* 读取并解析 SKILL.md,返回 frontmatter 元数据和 Markdown 正文
|
|
8
|
+
*/
|
|
9
|
+
export function createLoadSkillTool(registry) {
|
|
10
|
+
return {
|
|
11
|
+
name: 'load_skill',
|
|
12
|
+
description: '加载指定技能的完整内容。传入技能名称(从 list_skills 获取),返回技能的元数据和执行步骤(Playbook)。加载后按步骤执行即可完成对应运维/开发任务。',
|
|
13
|
+
inputSchema: {
|
|
14
|
+
type: 'object',
|
|
15
|
+
properties: {
|
|
16
|
+
name: {
|
|
17
|
+
type: 'string',
|
|
18
|
+
description: '技能名称,从 list_skills 工具的返回结果中获取',
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
required: ['name'],
|
|
22
|
+
},
|
|
23
|
+
execute: async (args) => {
|
|
24
|
+
const name = args.name;
|
|
25
|
+
// 1. 查找技能元数据
|
|
26
|
+
const skillMeta = registry.getByName(name);
|
|
27
|
+
if (!skillMeta) {
|
|
28
|
+
return {
|
|
29
|
+
content: `未找到名为 "${name}" 的技能。请先使用 list_skills 工具搜索可用技能。`,
|
|
30
|
+
isError: true,
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
// 2. 拼接 SKILL.md 完整路径
|
|
34
|
+
const projectRoot = registry.getProjectRoot();
|
|
35
|
+
const skillPath = join(projectRoot, skillMeta.path, 'SKILL.md');
|
|
36
|
+
// 3. 检查文件是否存在
|
|
37
|
+
if (!existsSync(skillPath)) {
|
|
38
|
+
return {
|
|
39
|
+
content: `技能文件不存在:${skillPath}`,
|
|
40
|
+
isError: true,
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
// 4. 读取并解析
|
|
44
|
+
try {
|
|
45
|
+
const raw = await readFile(skillPath, 'utf-8');
|
|
46
|
+
// 5. 使用 gray-matter 解析 frontmatter
|
|
47
|
+
let metadata = {};
|
|
48
|
+
let body;
|
|
49
|
+
try {
|
|
50
|
+
const parsed = matter(raw);
|
|
51
|
+
metadata = {
|
|
52
|
+
name: parsed.data.name || skillMeta.name,
|
|
53
|
+
description: parsed.data.description || skillMeta.description,
|
|
54
|
+
domain: parsed.data.domain || skillMeta.domain,
|
|
55
|
+
...parsed.data,
|
|
56
|
+
};
|
|
57
|
+
body = parsed.content.trim();
|
|
58
|
+
}
|
|
59
|
+
catch {
|
|
60
|
+
// frontmatter 解析失败 → 返回原始内容
|
|
61
|
+
body = raw.trim();
|
|
62
|
+
}
|
|
63
|
+
// 6. 构建返回结果
|
|
64
|
+
const result = {
|
|
65
|
+
metadata,
|
|
66
|
+
content: body,
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
content: JSON.stringify(result, null, 2),
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
73
|
+
return {
|
|
74
|
+
content: `读取技能文件失败 [${skillPath}]: ${error instanceof Error ? error.message : String(error)}`,
|
|
75
|
+
isError: true,
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=loader.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loader.js","sourceRoot":"","sources":["../../src/skills/loader.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,aAAa,CAAC;AACjC,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAC5C,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAIjC;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CAAC,QAAuB;IACzD,OAAO;QACL,IAAI,EAAE,YAAY;QAClB,WAAW,EACT,qFAAqF;QACvF,WAAW,EAAE;YACX,IAAI,EAAE,QAAQ;YACd,UAAU,EAAE;gBACV,IAAI,EAAE;oBACJ,IAAI,EAAE,QAAQ;oBACd,WAAW,EAAE,+BAA+B;iBAC7C;aACF;YACD,QAAQ,EAAE,CAAC,MAAM,CAAC;SACnB;QACD,OAAO,EAAE,KAAK,EAAE,IAA6B,EAAuB,EAAE;YACpE,MAAM,IAAI,GAAG,IAAI,CAAC,IAAc,CAAC;YAEjC,aAAa;YACb,MAAM,SAAS,GAAG,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;YAC3C,IAAI,CAAC,SAAS,EAAE,CAAC;gBACf,OAAO;oBACL,OAAO,EAAE,UAAU,IAAI,kCAAkC;oBACzD,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YAED,sBAAsB;YACtB,MAAM,WAAW,GAAG,QAAQ,CAAC,cAAc,EAAE,CAAC;YAC9C,MAAM,SAAS,GAAG,IAAI,CAAC,WAAW,EAAE,SAAS,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;YAEhE,cAAc;YACd,IAAI,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC3B,OAAO;oBACL,OAAO,EAAE,WAAW,SAAS,EAAE;oBAC/B,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;YAED,WAAW;YACX,IAAI,CAAC;gBACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;gBAE/C,mCAAmC;gBACnC,IAAI,QAAQ,GAA4B,EAAE,CAAC;gBAC3C,IAAI,IAAY,CAAC;gBAEjB,IAAI,CAAC;oBACH,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC;oBAC3B,QAAQ,GAAG;wBACT,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC,IAAI,IAAI,SAAS,CAAC,IAAI;wBACxC,WAAW,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,IAAI,SAAS,CAAC,WAAW;wBAC7D,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,MAAM,IAAI,SAAS,CAAC,MAAM;wBAC9C,GAAG,MAAM,CAAC,IAAI;qBACf,CAAC;oBACF,IAAI,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC;gBAC/B,CAAC;gBAAC,MAAM,CAAC;oBACP,4BAA4B;oBAC5B,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC;gBACpB,CAAC;gBAED,YAAY;gBACZ,MAAM,MAAM,GAAG;oBACb,QAAQ;oBACR,OAAO,EAAE,IAAI;iBACd,CAAC;gBAEF,OAAO;oBACL,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC;iBACzC,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,OAAO;oBACL,OAAO,EAAE,aAAa,SAAS,MAAM,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAAE;oBAC7F,OAAO,EAAE,IAAI;iBACd,CAAC;YACJ,CAAC;QACH,CAAC;KACF,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { SkillMeta, SkillDomain } from './types.js';
|
|
2
|
+
import type { Tool } from '../tools/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* 搜索选项
|
|
5
|
+
*/
|
|
6
|
+
interface SearchOptions {
|
|
7
|
+
keyword?: string;
|
|
8
|
+
domain?: SkillDomain;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Skill 注册表
|
|
12
|
+
* 管理 Skill 索引,提供搜索和列表能力
|
|
13
|
+
*/
|
|
14
|
+
export declare class SkillRegistry {
|
|
15
|
+
private skills;
|
|
16
|
+
private projectRoot;
|
|
17
|
+
/**
|
|
18
|
+
* 从 skills/index.json 加载技能索引
|
|
19
|
+
*/
|
|
20
|
+
load(indexPath: string): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* 按名称精确查找技能
|
|
23
|
+
*/
|
|
24
|
+
getByName(name: string): SkillMeta | undefined;
|
|
25
|
+
/**
|
|
26
|
+
* 获取项目根目录(从 load 时的 indexPath 推导)
|
|
27
|
+
*/
|
|
28
|
+
getProjectRoot(): string;
|
|
29
|
+
/**
|
|
30
|
+
* 搜索技能
|
|
31
|
+
* - keyword: 关键词搜索(匹配 name、description、tags)
|
|
32
|
+
* - domain: 按能力域过滤
|
|
33
|
+
*/
|
|
34
|
+
search(options: SearchOptions): SkillMeta[];
|
|
35
|
+
/**
|
|
36
|
+
* 获取所有技能
|
|
37
|
+
*/
|
|
38
|
+
getAll(): SkillMeta[];
|
|
39
|
+
/**
|
|
40
|
+
* 已加载的技能数量
|
|
41
|
+
*/
|
|
42
|
+
get size(): number;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* 创建 list_skills 工具
|
|
46
|
+
*/
|
|
47
|
+
export declare function createListSkillsTool(registry: SkillRegistry): Tool;
|
|
48
|
+
export {};
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
/**
|
|
4
|
+
* Skill 注册表
|
|
5
|
+
* 管理 Skill 索引,提供搜索和列表能力
|
|
6
|
+
*/
|
|
7
|
+
export class SkillRegistry {
|
|
8
|
+
skills = [];
|
|
9
|
+
projectRoot = '';
|
|
10
|
+
/**
|
|
11
|
+
* 从 skills/index.json 加载技能索引
|
|
12
|
+
*/
|
|
13
|
+
async load(indexPath) {
|
|
14
|
+
const content = await readFile(indexPath, 'utf-8');
|
|
15
|
+
const index = JSON.parse(content);
|
|
16
|
+
this.skills = index.skills;
|
|
17
|
+
// 从 indexPath 推导项目根目录
|
|
18
|
+
// indexPath 形如 /path/to/project/skills/index.json
|
|
19
|
+
this.projectRoot = join(dirname(indexPath), '..');
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* 按名称精确查找技能
|
|
23
|
+
*/
|
|
24
|
+
getByName(name) {
|
|
25
|
+
return this.skills.find((s) => s.name === name);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* 获取项目根目录(从 load 时的 indexPath 推导)
|
|
29
|
+
*/
|
|
30
|
+
getProjectRoot() {
|
|
31
|
+
return this.projectRoot;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* 搜索技能
|
|
35
|
+
* - keyword: 关键词搜索(匹配 name、description、tags)
|
|
36
|
+
* - domain: 按能力域过滤
|
|
37
|
+
*/
|
|
38
|
+
search(options) {
|
|
39
|
+
let results = this.skills;
|
|
40
|
+
// 按 domain 过滤
|
|
41
|
+
if (options.domain) {
|
|
42
|
+
results = results.filter((s) => s.domain === options.domain);
|
|
43
|
+
}
|
|
44
|
+
// 关键词匹配
|
|
45
|
+
if (options.keyword) {
|
|
46
|
+
const lower = options.keyword.toLowerCase();
|
|
47
|
+
results = results.filter((s) => s.name.toLowerCase().includes(lower) ||
|
|
48
|
+
s.description.toLowerCase().includes(lower) ||
|
|
49
|
+
s.tags.some((tag) => tag.toLowerCase().includes(lower)));
|
|
50
|
+
}
|
|
51
|
+
return results;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* 获取所有技能
|
|
55
|
+
*/
|
|
56
|
+
getAll() {
|
|
57
|
+
return [...this.skills];
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* 已加载的技能数量
|
|
61
|
+
*/
|
|
62
|
+
get size() {
|
|
63
|
+
return this.skills.length;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* 创建 list_skills 工具
|
|
68
|
+
*/
|
|
69
|
+
export function createListSkillsTool(registry) {
|
|
70
|
+
return {
|
|
71
|
+
name: 'list_skills',
|
|
72
|
+
description: '搜索可用的技能列表。可按能力域(devops/qa/plugin)过滤,或通过关键词搜索匹配的技能。返回技能名称、描述和能力域。',
|
|
73
|
+
inputSchema: {
|
|
74
|
+
type: 'object',
|
|
75
|
+
properties: {
|
|
76
|
+
keyword: {
|
|
77
|
+
type: 'string',
|
|
78
|
+
description: '搜索关键词,匹配技能名称、描述和标签',
|
|
79
|
+
},
|
|
80
|
+
domain: {
|
|
81
|
+
type: 'string',
|
|
82
|
+
enum: ['devops', 'qa', 'plugin'],
|
|
83
|
+
description: '按能力域过滤',
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
},
|
|
87
|
+
execute: async (args) => {
|
|
88
|
+
const results = registry.search({
|
|
89
|
+
keyword: args.keyword,
|
|
90
|
+
domain: args.domain,
|
|
91
|
+
});
|
|
92
|
+
// 返回精简的技能列表(不暴露 path、tags 等内部信息)
|
|
93
|
+
const simplified = results.map((s) => ({
|
|
94
|
+
name: s.name,
|
|
95
|
+
description: s.description,
|
|
96
|
+
domain: s.domain,
|
|
97
|
+
}));
|
|
98
|
+
return {
|
|
99
|
+
content: JSON.stringify(simplified, null, 2),
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=registry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"registry.js","sourceRoot":"","sources":["../../src/skills/registry.ts"],"names":[],"mappings":"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"}
|