deepfish-ai 1.0.13 → 1.0.15
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 +44 -5
- package/README_CN.md +43 -5
- package/package.json +7 -2
- package/src/cli/ConfigManager.js +24 -14
- package/src/cli/DefaultConfig.js +58 -0
- package/src/cli/ExtConfigManager.js +7 -7
- package/src/cli/HistoryManager.js +217 -0
- package/src/cli/SkillConfigManager.js +362 -0
- package/src/cli/SkillParser.js +61 -0
- package/src/cli/ai-config.js +23 -5
- package/src/cli/ai-history.js +34 -0
- package/src/cli/ai-skill.js +65 -0
- package/src/cli/index.js +3 -6
- package/src/core/AICLI.js +4 -6
- package/src/core/{globalVariable.js → GlobalVariable.js} +2 -3
- package/src/core/ai-services/AiWorker/AIMessageManager.js +71 -54
- package/src/core/ai-services/AiWorker/AiAgent.js +0 -4
- package/src/core/ai-services/AiWorker/AiPrompt.js +53 -6
- package/src/core/ai-services/AiWorker/AiTools.js +48 -6
- package/src/core/ai-services/AiWorker/index.js +87 -38
- package/src/core/ai-services/{AIService.js → index.js} +8 -0
- package/src/core/extension/BaseExtension.js +2 -0
- package/src/core/extension/ExtensionManager.js +35 -50
- package/src/core/extension/FileExtension.js +439 -0
- package/src/core/extension/InquirerExtension.js +167 -0
- package/src/core/extension/SystemExtension.js +370 -0
- package/src/core/extension/TestExtension.js +67 -0
- package/src/core/utils/log.js +10 -0
- package/src/core/utils/normal.js +86 -0
- package/src/cli/configTools.js +0 -90
- package/src/core/ai-services/AiWorker/AiRecorder.js +0 -119
- package/src/core/extension/DefaultExtension.js +0 -759
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
const { default: inquirer } = require('inquirer')
|
|
2
|
+
|
|
3
|
+
// 判断问答
|
|
4
|
+
async function askConfirm(name, message, defaultVal = true, opt = {}) {
|
|
5
|
+
const questions = [
|
|
6
|
+
{
|
|
7
|
+
type: 'confirm',
|
|
8
|
+
name,
|
|
9
|
+
message,
|
|
10
|
+
default: defaultVal,
|
|
11
|
+
...opt
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
const answers = await inquirer.prompt(questions)
|
|
15
|
+
return answers[name]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// 选择问答
|
|
19
|
+
function askList(name, message, choices, defaultVal = 0, opt = {}) {
|
|
20
|
+
const questions = [
|
|
21
|
+
{
|
|
22
|
+
type: 'list',
|
|
23
|
+
name,
|
|
24
|
+
message,
|
|
25
|
+
choices,
|
|
26
|
+
default: defaultVal,
|
|
27
|
+
...opt
|
|
28
|
+
}
|
|
29
|
+
]
|
|
30
|
+
return inquirer.prompt(questions)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 输入问答
|
|
34
|
+
async function askInput(name, message, defaultVal = '', opt = {}) {
|
|
35
|
+
const questions = [
|
|
36
|
+
{
|
|
37
|
+
type: 'input',
|
|
38
|
+
name,
|
|
39
|
+
message,
|
|
40
|
+
default: defaultVal,
|
|
41
|
+
...opt
|
|
42
|
+
},
|
|
43
|
+
]
|
|
44
|
+
const answers = await inquirer.prompt(questions)
|
|
45
|
+
return answers[name]
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 输入数字
|
|
49
|
+
async function askNumber(name, message, defaultVal = 0, opt = {}) {
|
|
50
|
+
const questions = [
|
|
51
|
+
{
|
|
52
|
+
type: 'number',
|
|
53
|
+
name,
|
|
54
|
+
message,
|
|
55
|
+
default: defaultVal,
|
|
56
|
+
...opt
|
|
57
|
+
},
|
|
58
|
+
]
|
|
59
|
+
const answers = await inquirer.prompt(questions)
|
|
60
|
+
return answers[name]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// 输入任何
|
|
64
|
+
function askAny(questions) {
|
|
65
|
+
return inquirer.prompt(questions)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const descriptions = [
|
|
69
|
+
{
|
|
70
|
+
type: "function",
|
|
71
|
+
function: {
|
|
72
|
+
name: "askConfirm",
|
|
73
|
+
description: "用户交互:提示用户确认问题,返回布尔值。通过message参数显示问题文本,defaultVal参数设置默认值(默认为true),opt参数可传递inquirer的其他选项。",
|
|
74
|
+
parameters: {
|
|
75
|
+
type: "object",
|
|
76
|
+
properties: {
|
|
77
|
+
name: { type: "string", description: "问题的标识名称" },
|
|
78
|
+
message: { type: "string", description: "显示给用户的问题文本" },
|
|
79
|
+
defaultVal: { type: "boolean", description: "默认值,默认为true" },
|
|
80
|
+
opt: { type: "object", description: "inquirer的其他选项对象,可选" },
|
|
81
|
+
},
|
|
82
|
+
required: ["name", "message"],
|
|
83
|
+
},
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
type: "function",
|
|
88
|
+
function: {
|
|
89
|
+
name: "askList",
|
|
90
|
+
description: "用户交互:提示用户从列表中选择一项,返回选择项对象。通过choices参数传递选项数组,defaultVal参数设置默认选择的索引(默认为0),opt参数可传递inquirer的其他选项。",
|
|
91
|
+
parameters: {
|
|
92
|
+
type: "object",
|
|
93
|
+
properties: {
|
|
94
|
+
name: { type: "string", description: "问题的标识名称" },
|
|
95
|
+
message: { type: "string", description: "显示给用户的问题文本" },
|
|
96
|
+
choices: { type: "array", description: "选项数组" },
|
|
97
|
+
defaultVal: { type: "number", description: "默认选择索引,默认为0" },
|
|
98
|
+
opt: { type: "object", description: "inquirer的其他选项对象,可选" },
|
|
99
|
+
},
|
|
100
|
+
required: ["name", "message", "choices"],
|
|
101
|
+
},
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
{
|
|
105
|
+
type: "function",
|
|
106
|
+
function: {
|
|
107
|
+
name: "askInput",
|
|
108
|
+
description: "用户交互:提示用户输入文本,返回输入的字符串。通过message参数显示问题文本,defaultVal参数设置默认值(默认为空字符串),opt参数可传递inquirer的其他选项。",
|
|
109
|
+
parameters: {
|
|
110
|
+
type: "object",
|
|
111
|
+
properties: {
|
|
112
|
+
name: { type: "string", description: "问题的标识名称" },
|
|
113
|
+
message: { type: "string", description: "显示给用户的问题文本" },
|
|
114
|
+
defaultVal: { type: "string", description: "默认值,默认为空字符串" },
|
|
115
|
+
opt: { type: "object", description: "inquirer的其他选项对象,可选" },
|
|
116
|
+
},
|
|
117
|
+
required: ["name", "message"],
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
type: "function",
|
|
123
|
+
function: {
|
|
124
|
+
name: "askNumber",
|
|
125
|
+
description: "用户交互:提示用户输入数字,返回输入的数字。通过message参数显示问题文本,defaultVal参数设置默认值(默认为0),opt参数可传递inquirer的其他选项。",
|
|
126
|
+
parameters: {
|
|
127
|
+
type: "object",
|
|
128
|
+
properties: {
|
|
129
|
+
name: { type: "string", description: "问题的标识名称" },
|
|
130
|
+
message: { type: "string", description: "显示给用户的问题文本" },
|
|
131
|
+
defaultVal: { type: "number", description: "默认值,默认为0" },
|
|
132
|
+
opt: { type: "object", description: "inquirer的其他选项对象,可选" },
|
|
133
|
+
},
|
|
134
|
+
required: ["name", "message"],
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
type: "function",
|
|
140
|
+
function: {
|
|
141
|
+
name: "askAny",
|
|
142
|
+
description: "用户交互:提示用户根据自定义问题对象数组进行交互,返回答案对象。通过questions参数传递inquirer格式的问题数组,支持多种问题类型和复杂配置。",
|
|
143
|
+
parameters: {
|
|
144
|
+
type: "object",
|
|
145
|
+
properties: {
|
|
146
|
+
questions: { type: "array", description: "inquirer的问题对象数组" },
|
|
147
|
+
},
|
|
148
|
+
required: ["questions"],
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
},
|
|
152
|
+
]
|
|
153
|
+
|
|
154
|
+
const functions = {
|
|
155
|
+
askConfirm,
|
|
156
|
+
askList,
|
|
157
|
+
askInput,
|
|
158
|
+
askNumber,
|
|
159
|
+
askAny,
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
module.exports = {
|
|
163
|
+
name: 'InquirerExtension',
|
|
164
|
+
extensionDescription: "提供用户交互功能,支持确认、列表选择、文本输入、数字输入等多种交互方式",
|
|
165
|
+
descriptions,
|
|
166
|
+
functions,
|
|
167
|
+
}
|
|
@@ -0,0 +1,370 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @Author: Roman 306863030@qq.com
|
|
3
|
+
* @Date: 2026-03-17 11:59:19
|
|
4
|
+
* @LastEditors: Roman 306863030@qq.com
|
|
5
|
+
* @LastEditTime: 2026-03-25 20:05:23
|
|
6
|
+
* @FilePath: \deepfish\src\core\extension\SystemExtension.js
|
|
7
|
+
* @Description: 默认扩展函数
|
|
8
|
+
* @
|
|
9
|
+
*/
|
|
10
|
+
const path = require('path')
|
|
11
|
+
const { logError, logSuccess, logInfo, getConfigPath } = require('../utils/log')
|
|
12
|
+
const iconv = require('iconv-lite') // 用于编码转换
|
|
13
|
+
const { cloneDeep } = require('lodash')
|
|
14
|
+
const { aiRequestSingle } = require('../ai-services/AiWorker/AiTools')
|
|
15
|
+
const { spawnSync } = require('child_process')
|
|
16
|
+
const { detectEncoding } = require('../utils/normal')
|
|
17
|
+
|
|
18
|
+
// 执行系统命令
|
|
19
|
+
// 执行系统命令(全平台兼容:Windows/PowerShell/CentOS)
|
|
20
|
+
function executeCommand(command, timeout = -1) {
|
|
21
|
+
logSuccess(`Executing system command: ${command}; ${timeout > 0 ? `Timeout: ${timeout}ms` : 'No timeout limit'}`)
|
|
22
|
+
try {
|
|
23
|
+
const result = spawnSync(command, {
|
|
24
|
+
cwd: process.cwd(),
|
|
25
|
+
stdio: 'pipe',
|
|
26
|
+
shell: true,
|
|
27
|
+
encoding: 'buffer',
|
|
28
|
+
windowsHide: true,
|
|
29
|
+
argv0: 'deepfish-shell',
|
|
30
|
+
timeout: timeout > 0 ? timeout : undefined,
|
|
31
|
+
})
|
|
32
|
+
let targetEncoding = this.config?.encoding
|
|
33
|
+
if (!targetEncoding || targetEncoding === 'auto') {
|
|
34
|
+
targetEncoding = detectEncoding(result.stdout || result.stderr)
|
|
35
|
+
}
|
|
36
|
+
const stdout = iconv.decode(result.stdout, targetEncoding)
|
|
37
|
+
const stderr = iconv.decode(result.stderr, targetEncoding)
|
|
38
|
+
const code = result.status
|
|
39
|
+
if (stderr && !stderr.trim().startsWith('WARNING')) {
|
|
40
|
+
const error = new Error(`Command failed (code ${code}): ${stderr.trim()}`)
|
|
41
|
+
logError(`Execute error: ${error.message}`)
|
|
42
|
+
return `Execute error: ${error.message}`
|
|
43
|
+
}
|
|
44
|
+
logSuccess(`${stdout}\nCommand executed successfully`)
|
|
45
|
+
return stdout || 'Command executed successfully'
|
|
46
|
+
} catch (decodeError) {
|
|
47
|
+
logError(`Encoding convert error: ${decodeError.message}`)
|
|
48
|
+
return `Failed to parse command output: ${decodeError.message}`
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// 请求ai服务
|
|
53
|
+
async function requestAI(
|
|
54
|
+
systemDescription,
|
|
55
|
+
prompt,
|
|
56
|
+
temperature = this.aiConfig.temperature,
|
|
57
|
+
) {
|
|
58
|
+
logSuccess(`Requesting AI`)
|
|
59
|
+
if (
|
|
60
|
+
typeof systemDescription === 'object' &&
|
|
61
|
+
systemDescription.systemDescription
|
|
62
|
+
) {
|
|
63
|
+
prompt = systemDescription.prompt || prompt || ''
|
|
64
|
+
systemDescription = systemDescription.systemDescription || ''
|
|
65
|
+
}
|
|
66
|
+
try {
|
|
67
|
+
logInfo(`aiSystem: ${systemDescription}`)
|
|
68
|
+
logInfo(`aiPrompt: ${prompt}`)
|
|
69
|
+
let aiConfig = this.aiConfig
|
|
70
|
+
if (temperature !== aiConfig.temperature) {
|
|
71
|
+
aiConfig = cloneDeep(aiConfig)
|
|
72
|
+
aiConfig.temperature = temperature
|
|
73
|
+
}
|
|
74
|
+
const response = await aiRequestSingle(
|
|
75
|
+
this.aiService.client,
|
|
76
|
+
aiConfig,
|
|
77
|
+
systemDescription,
|
|
78
|
+
prompt,
|
|
79
|
+
)
|
|
80
|
+
logInfo(`aiResponse: ${response}`)
|
|
81
|
+
return response
|
|
82
|
+
} catch (error) {
|
|
83
|
+
logError(`Error executing AI function: ${error.message}`)
|
|
84
|
+
throw error
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// 执行js代码
|
|
89
|
+
async function executeJSCode(code) {
|
|
90
|
+
logSuccess('Executing JavaScript code: ')
|
|
91
|
+
logSuccess(code)
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
const { functions } = this.extensionManager.extensions
|
|
95
|
+
const Func = new Function(
|
|
96
|
+
'Tools',
|
|
97
|
+
'require',
|
|
98
|
+
`return (async () => {
|
|
99
|
+
this.logMessages = []
|
|
100
|
+
const originalLog = console.log
|
|
101
|
+
const newLog = function () {
|
|
102
|
+
originalLog.apply(console, arguments)
|
|
103
|
+
this.logMessages.push(Array.from(arguments).join(' '))
|
|
104
|
+
}
|
|
105
|
+
console.log = newLog.bind(this)
|
|
106
|
+
${code}
|
|
107
|
+
console.log = originalLog
|
|
108
|
+
return this.logMessages.join('\\n')
|
|
109
|
+
})()`,
|
|
110
|
+
)
|
|
111
|
+
const originalRequire = require
|
|
112
|
+
const newRequire = (modulePath) => {
|
|
113
|
+
if (modulePath.startsWith('./')) {
|
|
114
|
+
const resolvedPath = path.resolve('.', modulePath)
|
|
115
|
+
return originalRequire(resolvedPath)
|
|
116
|
+
}
|
|
117
|
+
return originalRequire(modulePath)
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const result = await Func(functions, newRequire)
|
|
121
|
+
|
|
122
|
+
return result || ''
|
|
123
|
+
} catch (error) {
|
|
124
|
+
logError(`Error executing code: ${error.stack}`)
|
|
125
|
+
throw error
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
// 生成一个扩展函数文件 关键字:内置函数、扩展工具
|
|
129
|
+
async function getExtensionFileRule(goal) {
|
|
130
|
+
const newGoal = `
|
|
131
|
+
### 任务目标
|
|
132
|
+
基于指定规则创建一个标准化的Node.js NPM项目,实现用户目标:${goal},最终输出符合AI工作流调用规范的函数模块,并配套中英文说明文档。
|
|
133
|
+
|
|
134
|
+
### 第一步:项目初始化
|
|
135
|
+
1. 目录创建:新建目录,目录名称为「项目功能名称」,作为NPM项目根目录
|
|
136
|
+
2. package.json配置:
|
|
137
|
+
- name字段值:@deepfish-ai/项目功能名称(替换「项目功能名称」为实际功能名)
|
|
138
|
+
- git仓库地址:固定为 https://github.com/qq306863030/deepfish-extensions.git
|
|
139
|
+
- author设置为"DeepFish AI",
|
|
140
|
+
3. 主文件:项目入口文件必须命名为index.js
|
|
141
|
+
4. 文档文件:项目根目录需新增2个文档文件:
|
|
142
|
+
- README_CN.md(中文说明文档)
|
|
143
|
+
- README.md(英文说明文档)
|
|
144
|
+
|
|
145
|
+
### 第二步:index.js 完整开发规范
|
|
146
|
+
#### 2.1 核心输出要求
|
|
147
|
+
文件需输出四个核心字段,且代码逻辑清晰、可运行:
|
|
148
|
+
- name:字符串类型,扩展的名称标识
|
|
149
|
+
- extensionDescription:字符串类型,扩展功能的简要描述,说明该扩展提供的核心能力
|
|
150
|
+
- descriptions:数组类型,每个元素为OpenAI可识别的函数描述对象
|
|
151
|
+
- functions:对象类型,key为函数名称,value为函数方法体
|
|
152
|
+
|
|
153
|
+
#### 2.2 开发强制规则
|
|
154
|
+
1. 参数一致性:functions中函数的入参,必须与descriptions中对应函数声明的parameters完全一致
|
|
155
|
+
2. 命名规范:
|
|
156
|
+
- 函数名称前缀:「领域用途+分隔符」(如systemFileManagement_)
|
|
157
|
+
- 函数描述开头:统一格式「领域用途+分隔符+功能描述」(如系统文件管理:重命名文件)
|
|
158
|
+
3. 内置工具调用:函数内部可直接使用this.Tools下的内置方法,示例:
|
|
159
|
+
- this.Tools.requestAI(systemDescription, prompt, temperature)
|
|
160
|
+
- this.Tools.readFile(filePath)
|
|
161
|
+
- 其他文件处理类内置函数(运行时自动注入)
|
|
162
|
+
4. 函数数量:至少包含1个可被AI工作流调用的函数
|
|
163
|
+
|
|
164
|
+
#### 2.3 基础代码模板(必须遵循)
|
|
165
|
+
const descriptions = []
|
|
166
|
+
const functions = {}
|
|
167
|
+
module.exports = {
|
|
168
|
+
name: '扩展名称',
|
|
169
|
+
extensionDescription: '扩展功能的简要描述',
|
|
170
|
+
descriptions,
|
|
171
|
+
functions,
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
#### 2.4 参考示例(可参考格式)
|
|
175
|
+
const descriptions = [
|
|
176
|
+
{
|
|
177
|
+
name: 'systemFileManagement_renameFile',
|
|
178
|
+
description: '系统文件管理:重命名文件',
|
|
179
|
+
parameters: {
|
|
180
|
+
type: 'object',
|
|
181
|
+
properties: {
|
|
182
|
+
oldPath: { type: 'string', description: '旧文件路径' },
|
|
183
|
+
newPath: { type: 'string', description: '新文件路径' },
|
|
184
|
+
},
|
|
185
|
+
},
|
|
186
|
+
},
|
|
187
|
+
]
|
|
188
|
+
const functions = {
|
|
189
|
+
systemFileManagement_renameFile: (oldPath, newPath) => {
|
|
190
|
+
return this.Tools.rename(oldPath, newPath)
|
|
191
|
+
},
|
|
192
|
+
}
|
|
193
|
+
module.exports = {
|
|
194
|
+
name: 'systemFileManagement',
|
|
195
|
+
extensionDescription: '提供文件管理相关功能,包括文件重命名等操作',
|
|
196
|
+
descriptions,
|
|
197
|
+
functions,
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
### 第三步:README文档规范
|
|
201
|
+
#### 3.1 通用要求
|
|
202
|
+
- 两个文档需在标题下方包含「中英文切换标签」(如文档顶部标注「English | 中文」/「中文 | English」)
|
|
203
|
+
- 结构保持一致,仅语言不同,核心模块顺序不可调整
|
|
204
|
+
- 文件名称README_CN.md(中文)、README.md(英文)
|
|
205
|
+
- 链接使用相对路径,如[中文](./README_CN.md)
|
|
206
|
+
|
|
207
|
+
#### 3.2 核心模块
|
|
208
|
+
1. 总体功能描述:
|
|
209
|
+
- 清晰说明当前NPM包的核心定位、整体功能价值、适用场景
|
|
210
|
+
- 语言简洁易懂,无需技术细节,聚焦「做什么」而非「怎么做」
|
|
211
|
+
2. 快速开始:
|
|
212
|
+
- 明确说明安装步骤,顺序不可颠倒:
|
|
213
|
+
① 先安装deepfish-ai全局库:npm install deepfish-ai -g
|
|
214
|
+
② 再安装当前项目库:npm install @deepfish-ai/项目功能名称 -g
|
|
215
|
+
3. 函数列表及功能描述:
|
|
216
|
+
- 列出当前项目中所有函数名称
|
|
217
|
+
- 对应说明每个函数的核心功能
|
|
218
|
+
- 无需编写各个函数的具体使用方法
|
|
219
|
+
`
|
|
220
|
+
return newGoal
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// 获取ai的配置
|
|
224
|
+
function getAiConfig() {
|
|
225
|
+
return cloneDeep(this.aiConfig)
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// 获取ai的配置文件所在目录
|
|
229
|
+
function getAiConfigPath() {
|
|
230
|
+
return getConfigPath()
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// 加载skill
|
|
234
|
+
function executeSkill(skillFilePath, subGoalPrompt = '') {
|
|
235
|
+
const skillContent = this.skillConfigManager.loadSkill(skillFilePath)
|
|
236
|
+
if (!subGoalPrompt) {
|
|
237
|
+
return skillContent
|
|
238
|
+
}
|
|
239
|
+
// 调用子工作流完成目标
|
|
240
|
+
return this.aiService.subSkillWorkflow(skillContent, subGoalPrompt)
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const descriptions = [
|
|
244
|
+
{
|
|
245
|
+
type: 'function',
|
|
246
|
+
function: {
|
|
247
|
+
name: 'executeCommand',
|
|
248
|
+
description:
|
|
249
|
+
'执行系统命令,返回执行结果。适用于运行shell命令、系统工具等。command为需要执行的系统命令字符串,如"ls -l"; timeout为命令执行的超时时间,单位为毫秒,-1表示不限制超时时间, 默认值为-1。注意:如果执行多条命令,且需要保持会话,使用命令链的方式执行。命令执行失败时会抛出错误,成功时返回命令执行结果字符串或"System command executed successfully"。',
|
|
250
|
+
parameters: {
|
|
251
|
+
type: 'object',
|
|
252
|
+
properties: {
|
|
253
|
+
command: { type: 'string' },
|
|
254
|
+
timeout: { type: 'number' },
|
|
255
|
+
},
|
|
256
|
+
required: ['command'],
|
|
257
|
+
},
|
|
258
|
+
},
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
type: 'function',
|
|
262
|
+
function: {
|
|
263
|
+
name: 'requestAI',
|
|
264
|
+
description:
|
|
265
|
+
'请求AI服务处理简单任务,如随机生成一段话、翻译文本、数学计算、代码分析、知识检索等。通过systemDescription参数指定AI的行为和限制,prompt参数输入任务描述, temperature参数指定AI的温度(0-2之间的浮点数,默认为用户配置)。返回AI处理后的结果字符串,执行失败时会抛出错误。',
|
|
266
|
+
parameters: {
|
|
267
|
+
type: 'object',
|
|
268
|
+
properties: {
|
|
269
|
+
systemDescription: { type: 'string' },
|
|
270
|
+
prompt: { type: 'string' },
|
|
271
|
+
temperature: { type: 'number' },
|
|
272
|
+
},
|
|
273
|
+
required: ['systemDescription', 'prompt'],
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
type: 'function',
|
|
279
|
+
function: {
|
|
280
|
+
name: 'executeJSCode',
|
|
281
|
+
description:
|
|
282
|
+
'执行JavaScript代码,返回代码执行结果。代码中可通过Tools命名空间直接调用其他工具函数(如await Tools.createFile(),注意:不需要使用require引入),Tools中引入了一些常用库可直接调用(Tools.fs="fs-extra", Tools.dayjs="dayjs", Tools.axios="axios", Tools.lodash="lodash"),支持引入自定义模块(需使用绝对路径)。注意:1.代码中不要使用__dirname获取当前目录,请使用path.resolve(".")来获取当前目录。2.执行失败时会抛出错误,成功时返回代码执行结果或空字符串。',
|
|
283
|
+
parameters: {
|
|
284
|
+
type: 'object',
|
|
285
|
+
properties: {
|
|
286
|
+
code: { type: 'string' },
|
|
287
|
+
},
|
|
288
|
+
required: ['code'],
|
|
289
|
+
},
|
|
290
|
+
},
|
|
291
|
+
},
|
|
292
|
+
|
|
293
|
+
{
|
|
294
|
+
type: 'function',
|
|
295
|
+
function: {
|
|
296
|
+
name: 'getExtensionFileRule',
|
|
297
|
+
description:
|
|
298
|
+
'如果用户需要为本程序ai工作流生成一个或多个工具函数作为一个工作流执行过程中调用的扩展工具,则需要先调用此函数获取生成扩展文件的规则;示例:生成一个扩展工具: 能够产生一个随机数的函数;',
|
|
299
|
+
parameters: {
|
|
300
|
+
type: 'object',
|
|
301
|
+
properties: {
|
|
302
|
+
goal: { type: 'string' },
|
|
303
|
+
},
|
|
304
|
+
required: ['goal'],
|
|
305
|
+
},
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
{
|
|
309
|
+
type: 'function',
|
|
310
|
+
function: {
|
|
311
|
+
name: 'getAiConfig',
|
|
312
|
+
description: '获取ai请求接口的配置参数',
|
|
313
|
+
parameters: {
|
|
314
|
+
type: 'object',
|
|
315
|
+
properties: {},
|
|
316
|
+
required: [],
|
|
317
|
+
},
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
{
|
|
321
|
+
type: 'function',
|
|
322
|
+
function: {
|
|
323
|
+
name: 'getAiConfigPath',
|
|
324
|
+
description: '获取ai请求接口配置的文件地址',
|
|
325
|
+
parameters: {
|
|
326
|
+
type: 'object',
|
|
327
|
+
properties: {},
|
|
328
|
+
required: [],
|
|
329
|
+
},
|
|
330
|
+
},
|
|
331
|
+
},
|
|
332
|
+
{
|
|
333
|
+
type: 'function',
|
|
334
|
+
function: {
|
|
335
|
+
name: 'executeSkill',
|
|
336
|
+
description:
|
|
337
|
+
'加载并执行指定路径的skill,以该skill提供的工具函数为基础,启动子工作流完成目标任务。skillFilePath为SKILL.md文件的绝对路径,subGoalPrompt为子工作流需要完成的目标描述,留空则仅加载skill不执行子任务。',
|
|
338
|
+
parameters: {
|
|
339
|
+
type: 'object',
|
|
340
|
+
properties: {
|
|
341
|
+
skillFilePath: {
|
|
342
|
+
type: 'string',
|
|
343
|
+
description: 'SKILL.md文件的绝对路径',
|
|
344
|
+
},
|
|
345
|
+
subGoalPrompt: {
|
|
346
|
+
type: 'string',
|
|
347
|
+
description: '子工作流需要完成的目标描述,留空则仅加载skill',
|
|
348
|
+
},
|
|
349
|
+
},
|
|
350
|
+
required: ['skillFilePath'],
|
|
351
|
+
},
|
|
352
|
+
},
|
|
353
|
+
},
|
|
354
|
+
]
|
|
355
|
+
const functions = {
|
|
356
|
+
executeCommand,
|
|
357
|
+
requestAI,
|
|
358
|
+
executeJSCode,
|
|
359
|
+
getExtensionFileRule,
|
|
360
|
+
getAiConfig,
|
|
361
|
+
getAiConfigPath,
|
|
362
|
+
executeSkill,
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
module.exports = {
|
|
366
|
+
name: 'SystemExtension',
|
|
367
|
+
extensionDescription: "提供系统命令执行、AI请求、JS代码执行、扩展文件生成规则、AI配置管理、Skill加载执行等核心系统功能",
|
|
368
|
+
descriptions,
|
|
369
|
+
functions,
|
|
370
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// 执行测试任务
|
|
2
|
+
function executeTestTask(subGoalPrompt = "") {
|
|
3
|
+
// 调用子工作流完成目标
|
|
4
|
+
return this.aiService.subTestWorkflow(subGoalPrompt)
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
// 生成测试任务
|
|
8
|
+
function generateTestTaskRule(goal) {
|
|
9
|
+
return `
|
|
10
|
+
用户测试任务:${goal}。
|
|
11
|
+
请在当前目录下生成一个Markdown格式的程序功能测试说明文档,文档标题固定为“xxx测试任务”(其中“xxx”替换为具体的程序功能测试任务名称,需与我提供的程序功能测试目标完全一致)。生成文档前,请先仔细研读我给出的程序功能测试任务目标,明确程序待测试的功能模块、核心测试需求、测试范围及测试重点,精准理解每个功能的预期表现,确保完全掌握测试任务要求后再进行文档撰写。
|
|
12
|
+
|
|
13
|
+
文档核心内容需围绕程序功能测试任务目标,针对程序的每个待测试功能,制定**可逐条执行、无歧义、覆盖全面**的功能测试用例,每个测试用例需明确对应程序功能模块、具体测试场景、详细测试步骤(步骤需具体可操作,可直接供测试人员执行,明确操作路径、输入数据、操作顺序,避免模糊表述);同时,为每个测试用例对应明确、可验证的测试期望结果,期望结果需与测试步骤一一对应,清晰界定程序功能正常运行的标准,明确功能执行后应呈现的具体效果、输出结果或状态,无模糊不清的表述。
|
|
14
|
+
|
|
15
|
+
补充要求:1. 测试用例需按程序功能模块分类、按测试优先级排序,每个测试用例标注唯一编号,便于区分、执行和追溯;2. 文档格式需符合Markdown规范,标题层级清晰(文档标题为一级标题,功能模块分组用二级标题,测试用例用三级标题或有序列表),排版整洁,便于阅读、编辑和后续维护;3. 严格贴合程序功能测试任务目标,聚焦程序功能的正确性、完整性,不添加无关内容,不遗漏核心功能测试场景,不规避边界场景,确保测试用例的针对性、完整性和有效性,能充分验证程序各功能是否符合预期。
|
|
16
|
+
`}
|
|
17
|
+
|
|
18
|
+
const descriptions = [
|
|
19
|
+
{
|
|
20
|
+
type: "function",
|
|
21
|
+
function: {
|
|
22
|
+
name: "generateTestTaskRule",
|
|
23
|
+
description:
|
|
24
|
+
"根据用户提供的程序功能测试目标,生成一份标准化的Markdown格式功能测试说明文档的规则提示词。返回的提示词将指导AI生成包含完整测试用例(测试步骤、期望结果、用例编号)的测试文档。适用于用户需要对程序进行功能测试等场景。",
|
|
25
|
+
parameters: {
|
|
26
|
+
type: "object",
|
|
27
|
+
properties: {
|
|
28
|
+
goal: {
|
|
29
|
+
type: "string",
|
|
30
|
+
description: "程序功能测试目标描述,需明确待测试的功能模块、测试范围和测试重点",
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
required: ["goal"],
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
type: "function",
|
|
39
|
+
function: {
|
|
40
|
+
name: "executeTestTask",
|
|
41
|
+
description:
|
|
42
|
+
"执行测试任务,启动一个专用的AI子工作流来完成指定的某一项测试目标。子工作流将根据测试任务描述,自动执行测试步骤并验证测试结果。适用于需要AI自动化执行功能测试的场景。",
|
|
43
|
+
parameters: {
|
|
44
|
+
type: "object",
|
|
45
|
+
properties: {
|
|
46
|
+
subGoalPrompt: {
|
|
47
|
+
type: "string",
|
|
48
|
+
description: "测试子工作流需要完成的目标描述,描述具体的测试任务内容",
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
required: ["subGoalPrompt"],
|
|
52
|
+
},
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const functions = {
|
|
58
|
+
executeTestTask,
|
|
59
|
+
generateTestTaskRule,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
module.exports = {
|
|
63
|
+
name: 'TestExtension',
|
|
64
|
+
extensionDescription: "提供程序功能测试任务的生成和执行功能,支持自动化测试工作流",
|
|
65
|
+
descriptions,
|
|
66
|
+
functions,
|
|
67
|
+
};
|
package/src/core/utils/log.js
CHANGED
|
@@ -15,6 +15,14 @@ function logError(message) {
|
|
|
15
15
|
log(message, '#ed7f7f')
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
function logWarning(message) {
|
|
19
|
+
log(message, '#f2c94c')
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function logDisabled(message) {
|
|
23
|
+
log(message, '#999999')
|
|
24
|
+
}
|
|
25
|
+
|
|
18
26
|
function writeLine(msg1, msg2 = '', color = 'blue') {
|
|
19
27
|
if (color === 'blue') {
|
|
20
28
|
process.stdout.write('\r' + chalk.hex('#6dd2ea')(msg1) + ' ' + msg2)
|
|
@@ -134,6 +142,8 @@ module.exports = {
|
|
|
134
142
|
logInfo,
|
|
135
143
|
logSuccess,
|
|
136
144
|
logError,
|
|
145
|
+
logWarning,
|
|
146
|
+
logDisabled,
|
|
137
147
|
loading,
|
|
138
148
|
writeLine,
|
|
139
149
|
streamOutput,
|