deepfish-ai 1.0.28 → 2.0.2
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 +126 -368
- package/dist/index.js +23320 -0
- package/dist/serve/client/assets/index-DUHYC91l.js +1 -0
- package/dist/serve/client/assets/index-zcrdNLZs.css +1 -0
- package/dist/serve/client/index.html +14 -0
- package/dist/serve/pm2-server.js +21015 -0
- package/dist/serve/server/entry-server.mjs +224 -0
- package/package.json +50 -70
- package/LICENSE +0 -21
- package/README_CN.md +0 -460
- package/src/AgentRobot/AgentRobotFactory/MainAgentRobot.js +0 -88
- package/src/AgentRobot/AgentRobotFactory/SubAgentRobot.js +0 -45
- package/src/AgentRobot/AgentRobotFactory/SubSkillAgentRobot.js +0 -45
- package/src/AgentRobot/AgentRobotFactory/index.js +0 -48
- package/src/AgentRobot/BaseAgentRobot/Brain.js +0 -210
- package/src/AgentRobot/BaseAgentRobot/BrainEvent.js +0 -17
- package/src/AgentRobot/BaseAgentRobot/Hand.js +0 -119
- package/src/AgentRobot/BaseAgentRobot/Heart.js +0 -0
- package/src/AgentRobot/BaseAgentRobot/Logger.js +0 -114
- package/src/AgentRobot/BaseAgentRobot/ScreenPrinter.js +0 -49
- package/src/AgentRobot/BaseAgentRobot/index.js +0 -266
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/AliBailian.js +0 -997
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/doc-transform.js +0 -204
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/docx.js +0 -1286
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/img.js +0 -765
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/pdf.js +0 -736
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/pptx.js +0 -567
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/video.js +0 -36
- package/src/AgentRobot/BaseAgentRobot/lazy-tools/xlsx.js +0 -611
- package/src/AgentRobot/BaseAgentRobot/tools/BaseTools.js +0 -13
- package/src/AgentRobot/BaseAgentRobot/tools/CreateAgentTools.js +0 -158
- package/src/AgentRobot/BaseAgentRobot/tools/FileTools.js +0 -768
- package/src/AgentRobot/BaseAgentRobot/tools/GenerateTools.js +0 -441
- package/src/AgentRobot/BaseAgentRobot/tools/InquirerTools.js +0 -244
- package/src/AgentRobot/BaseAgentRobot/tools/SystemTools.js +0 -263
- package/src/AgentRobot/BaseAgentRobot/tools/TaskTools.js +0 -211
- package/src/AgentRobot/BaseAgentRobot/tools/TestTools.js +0 -100
- package/src/AgentRobot/BaseAgentRobot/tools/UserTool.js +0 -87
- package/src/AgentRobot/BaseAgentRobot/tools/WebTools.js +0 -257
- package/src/AgentRobot/BaseAgentRobot/utils/AIRequest.js +0 -297
- package/src/AgentRobot/BaseAgentRobot/utils/AIToolManager.js +0 -131
- package/src/AgentRobot/BaseAgentRobot/utils/AgentTree.js +0 -66
- package/src/AgentRobot/BaseAgentRobot/utils/AttachmentToolScanner.js +0 -235
- package/src/AgentRobot/BaseAgentRobot/utils/MessageCompresser.js +0 -236
- package/src/AgentRobot/BaseAgentRobot/utils/aiConsole.js +0 -74
- package/src/AgentRobot/BaseAgentRobot/utils/aiInquirer.js +0 -78
- package/src/AgentRobot/BaseAgentRobot/utils/analyzeReturn.test.js +0 -104
- package/src/AgentRobot/BaseAgentRobot/utils/copilot.js +0 -117
- package/src/AgentRobot/BaseAgentRobot/utils/node-root.js +0 -138
- package/src/AgentRobot/BaseAgentRobot/utils/normal.js +0 -399
- package/src/cli/ConfigManager.js +0 -283
- package/src/cli/DefaultConfig.js +0 -98
- package/src/cli/GlobalVariable.js +0 -8
- package/src/cli/MemoryManager.js +0 -53
- package/src/cli/SkillConfigManager.js +0 -343
- package/src/cli/SkillParser.js +0 -58
- package/src/cli/ai-auth.js +0 -231
- package/src/cli/ai-config.js +0 -225
- package/src/cli/ai-memory.js +0 -22
- package/src/cli/ai-skill.js +0 -65
- package/src/cli/index.js +0 -77
- package/src/index.js +0 -54
package/src/cli/DefaultConfig.js
DELETED
|
@@ -1,98 +0,0 @@
|
|
|
1
|
-
const defaultConfig = {
|
|
2
|
-
ai: [],
|
|
3
|
-
currentAi: '',
|
|
4
|
-
maxIterations: -1, // ai完成工作流的最大迭代次数,-1表示无限制
|
|
5
|
-
maxMemoryExpireTime: 30, // 整个会话的最大过期时间,单位天,-1表示无限制, 0表示不记录
|
|
6
|
-
maxLogExpireTime: 3, // 日志过期时间,单位天,-1表示无限制,0表示不记录
|
|
7
|
-
maxBlockFileSize: 20, // 最大分块文件大小,单位KB;超过该大小的文件需要分块处理
|
|
8
|
-
encoding: 'auto', // 命令行编码格式, 可设置为utf-8、gbk等, 也可以设置成auto或空值自动判断
|
|
9
|
-
isThinkPrint: true, // 是否打印思考过程
|
|
10
|
-
EMBEDDING_API: '', // 向量化接口地址
|
|
11
|
-
EMBEDDING_API_KEY: '' // 向量化接口密钥
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const aiCliConfig = {
|
|
15
|
-
DeepSeek: {
|
|
16
|
-
baseUrl: 'https://api.deepseek.com',
|
|
17
|
-
model: {
|
|
18
|
-
list: ['deepseek-chat', 'deepseek-reasoner', 'deepseek-v4-flash', 'deepseek-v4-pro', 'other'],
|
|
19
|
-
defaultValue: '',
|
|
20
|
-
},
|
|
21
|
-
type: 'deepseek',
|
|
22
|
-
apiKey: '',
|
|
23
|
-
temperature: 0.7,
|
|
24
|
-
maxTokens: -1, // 单位KB
|
|
25
|
-
maxContextLength: 64, // 单位KB
|
|
26
|
-
stream: true,
|
|
27
|
-
},
|
|
28
|
-
MiniMax: {
|
|
29
|
-
baseUrl: 'https://api.minimaxi.com/v1',
|
|
30
|
-
model: {
|
|
31
|
-
list: ['MiniMax-M2.5', 'MiniMax-M2.5-highspeed', 'MiniMax-M2.7', 'MiniMax-M2.7-highspeed'],
|
|
32
|
-
defaultValue: 'MiniMax-M2.5',
|
|
33
|
-
},
|
|
34
|
-
type: 'minimax',
|
|
35
|
-
apiKey: '',
|
|
36
|
-
temperature: 0.7,
|
|
37
|
-
maxTokens: -1, // 单位KB
|
|
38
|
-
maxContextLength: 64, // 单位KB
|
|
39
|
-
stream: true,
|
|
40
|
-
},
|
|
41
|
-
Qwen: {
|
|
42
|
-
baseUrl: 'https://dashscope.aliyuncs.com/compatible-mode/v1',
|
|
43
|
-
model: {
|
|
44
|
-
list: ['qwen3.6-plus'],
|
|
45
|
-
defaultValue: 'qwen3.6-plus',
|
|
46
|
-
},
|
|
47
|
-
type: 'qwen',
|
|
48
|
-
apiKey: '',
|
|
49
|
-
temperature: 0.7,
|
|
50
|
-
maxTokens: -1, // 单位KB
|
|
51
|
-
maxContextLength: 64, // 单位KB
|
|
52
|
-
stream: true,
|
|
53
|
-
},
|
|
54
|
-
Ollama: {
|
|
55
|
-
baseUrl: 'http://localhost:11434/v1',
|
|
56
|
-
model: {
|
|
57
|
-
list: [],
|
|
58
|
-
defaultValue: 'deepseek-v3.2:cloud',
|
|
59
|
-
},
|
|
60
|
-
type: 'ollama',
|
|
61
|
-
apiKey: 'ollama',
|
|
62
|
-
temperature: 0.7,
|
|
63
|
-
maxTokens: -1, // 单位KB
|
|
64
|
-
maxContextLength: 64, // 单位KB
|
|
65
|
-
stream: true,
|
|
66
|
-
},
|
|
67
|
-
OpenAI: {
|
|
68
|
-
baseUrl: 'https://api.openai.com/v1',
|
|
69
|
-
model: {
|
|
70
|
-
list: [],
|
|
71
|
-
defaultValue: 'gpt-4',
|
|
72
|
-
},
|
|
73
|
-
type: 'openai',
|
|
74
|
-
apiKey: '',
|
|
75
|
-
temperature: 0.7,
|
|
76
|
-
maxTokens: -1, // 单位KB
|
|
77
|
-
maxContextLength: 64, // 单位KB
|
|
78
|
-
stream: true,
|
|
79
|
-
},
|
|
80
|
-
Copilot: {
|
|
81
|
-
baseUrl: 'https://api.githubcopilot.com',
|
|
82
|
-
model: {
|
|
83
|
-
list: [
|
|
84
|
-
'gpt-4o',
|
|
85
|
-
'gpt-4o-mini',
|
|
86
|
-
],
|
|
87
|
-
defaultValue: 'gpt-4o',
|
|
88
|
-
},
|
|
89
|
-
type: 'copilot',
|
|
90
|
-
apiKey: '',
|
|
91
|
-
temperature: 0.7,
|
|
92
|
-
maxTokens: -1, // 单位KB
|
|
93
|
-
maxContextLength: 400, // 单位KB
|
|
94
|
-
stream: true,
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
module.exports = { aiCliConfig, defaultConfig }
|
package/src/cli/MemoryManager.js
DELETED
|
@@ -1,53 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @Author: Roman 306863030@qq.com
|
|
3
|
-
* @Date: 2026-03-16 09:18:05
|
|
4
|
-
* @LastEditors: Roman 306863030@qq.com
|
|
5
|
-
* @LastEditTime: 2026-04-08 18:31:55
|
|
6
|
-
* @FilePath: \deepfish\src\cli\MemoryManager.js
|
|
7
|
-
* @Description: 对话历史记录、恢复
|
|
8
|
-
* @
|
|
9
|
-
*/
|
|
10
|
-
const fs = require('fs-extra')
|
|
11
|
-
const path = require('path')
|
|
12
|
-
const { GlobalVariable } = require('./GlobalVariable.js')
|
|
13
|
-
const aiConsole = require('../AgentRobot/BaseAgentRobot/utils/aiConsole.js')
|
|
14
|
-
const { openDirectory } = require('../AgentRobot/BaseAgentRobot/utils/normal.js')
|
|
15
|
-
class MemoryManager {
|
|
16
|
-
constructor() {
|
|
17
|
-
this.configManager = GlobalVariable.configManager
|
|
18
|
-
this.memoryDir = path.join(this.configManager.configDir, 'memory')
|
|
19
|
-
this.agentRecordFilePath = path.join(this.memoryDir, 'agentRecord.json')
|
|
20
|
-
this.workspace = process.cwd()
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
openDirectory() {
|
|
24
|
-
// 根据路径查看id
|
|
25
|
-
const agentRecord = fs.readJSONSync(this.agentRecordFilePath, { throws: false }) || []
|
|
26
|
-
const agent = agentRecord?.find(item => {
|
|
27
|
-
return item.workspace === this.workspace
|
|
28
|
-
})
|
|
29
|
-
if (!agent) {
|
|
30
|
-
return aiConsole.logError('No memory found for the current directory.')
|
|
31
|
-
}
|
|
32
|
-
const agentId = agent.agentId
|
|
33
|
-
const dir = path.join(this.memoryDir, agentId)
|
|
34
|
-
openDirectory(dir)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
clearMessage() {
|
|
38
|
-
// 根据路径查看id
|
|
39
|
-
const agentRecord = fs.readJSONSync(this.agentRecordFilePath, { throws: false }) || []
|
|
40
|
-
const agentIndex = agentRecord?.findIndex(item => {
|
|
41
|
-
return item.workspace === this.workspace
|
|
42
|
-
})
|
|
43
|
-
if (!~agentIndex) {
|
|
44
|
-
return aiConsole.logError('No memory found for the current directory.')
|
|
45
|
-
}
|
|
46
|
-
fs.removeSync(path.join(this.memoryDir, agentRecord[agentIndex].agentId))
|
|
47
|
-
agentRecord.splice(agentIndex, 1)
|
|
48
|
-
fs.writeJSONSync(this.agentRecordFilePath, agentRecord, { spaces: 2 })
|
|
49
|
-
aiConsole.logSuccess('Memory cleared.')
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
module.exports = MemoryManager
|
|
@@ -1,343 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @Author: Roman 306863030@qq.com
|
|
3
|
-
* @Date: 2026-03-23 15:23:42
|
|
4
|
-
* @LastEditors: Roman 306863030@qq.com
|
|
5
|
-
* @LastEditTime: 2026-05-06 14:53:07
|
|
6
|
-
* @FilePath: \deepfish\src\cli\SkillConfigManager.js
|
|
7
|
-
* @Description: Skill configuration manager
|
|
8
|
-
*/
|
|
9
|
-
const path = require('path')
|
|
10
|
-
const fs = require('fs-extra')
|
|
11
|
-
const axios = require('axios')
|
|
12
|
-
const cheerio = require('cheerio')
|
|
13
|
-
const extract = require('extract-zip')
|
|
14
|
-
const { GlobalVariable } = require('./GlobalVariable.js')
|
|
15
|
-
const { parseSkillMetadataYaml } = require('./SkillParser.js')
|
|
16
|
-
const aiConsole = require('../AgentRobot/BaseAgentRobot/utils/aiConsole.js')
|
|
17
|
-
const { openDirectory } = require('../AgentRobot/BaseAgentRobot/utils/normal.js')
|
|
18
|
-
|
|
19
|
-
// skill的数据结构: {name: string, enable: boolean, description: string, baseDir: string, skillDirName: string, location: string, skillFilePath: string, homepage: string, metadata: object}
|
|
20
|
-
class SkillConfigManager {
|
|
21
|
-
constructor() {
|
|
22
|
-
this.configManager = GlobalVariable.configManager
|
|
23
|
-
// skill目录
|
|
24
|
-
this.skillDir = path.join(this.configManager.configDir, './clawSkills')
|
|
25
|
-
this.skillFilePath = path.join( this.skillDir, './clawSkills.json')
|
|
26
|
-
this.init()
|
|
27
|
-
GlobalVariable.skillConfigManager = this
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
init() {
|
|
31
|
-
// 自动创建skill目录
|
|
32
|
-
fs.ensureDirSync(this.skillDir)
|
|
33
|
-
// 判断是否存在clawSkills.json,如果不存在则创建
|
|
34
|
-
if (!fs.existsSync(this.skillFilePath)) {
|
|
35
|
-
fs.writeJsonSync(this.skillFilePath, { skills: [] }, { spaces: 2 })
|
|
36
|
-
}
|
|
37
|
-
this._check()
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
// 读取skill文件
|
|
41
|
-
readSkills() {
|
|
42
|
-
const skillsObj = fs.readJSONSync(this.skillFilePath, { throws: false }) || { skills: [] }
|
|
43
|
-
return skillsObj.skills
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// 写入skill文件
|
|
47
|
-
writeSkills(skills) {
|
|
48
|
-
fs.writeJSONSync(this.skillFilePath, { skills }, { spaces: 2 })
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
openDirectory() {
|
|
53
|
-
// 打开目录
|
|
54
|
-
openDirectory(this.skillDir)
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
// 解析skill文件,写入到json中,获取名称、版本、作者、元数据、描述等信息
|
|
58
|
-
_parseSkill(skillDirPath) {
|
|
59
|
-
const skillMdPath = ['SKILL.md', 'skill.md']
|
|
60
|
-
.map((name) => path.join(skillDirPath, name))
|
|
61
|
-
.find((filePath) => fs.existsSync(filePath))
|
|
62
|
-
if (!skillMdPath) {
|
|
63
|
-
return {}
|
|
64
|
-
}
|
|
65
|
-
const parsed = parseSkillMetadataYaml(skillMdPath)
|
|
66
|
-
return parsed
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// 查看skills列表
|
|
70
|
-
viewList() {
|
|
71
|
-
const skills = this.readSkills()
|
|
72
|
-
if (skills && Array.isArray(skills)) {
|
|
73
|
-
console.log('='.repeat(50))
|
|
74
|
-
// 打印扩展列表,并加上索引
|
|
75
|
-
if (skills.length === 0) {
|
|
76
|
-
console.log(`No skills in config.`)
|
|
77
|
-
} else {
|
|
78
|
-
console.log('Skills in config:')
|
|
79
|
-
skills.forEach((skill, index) => {
|
|
80
|
-
console.log(`[${index}] ${skill.name} (${skill.enable ? 'Enabled' : 'Disabled'})`)
|
|
81
|
-
})
|
|
82
|
-
}
|
|
83
|
-
console.log('='.repeat(50))
|
|
84
|
-
} else {
|
|
85
|
-
aiConsole.logError(`No skills in config.`)
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
_check() {
|
|
89
|
-
// 如果数组的数量与目录中的数量不一致,则自动同步
|
|
90
|
-
const skills = this.readSkills()
|
|
91
|
-
const skillDirs = fs.readdirSync(this.skillDir).filter((file) => {
|
|
92
|
-
const fullPath = path.join(this.skillDir, file)
|
|
93
|
-
return fs.statSync(fullPath).isDirectory() && fs.existsSync(path.join(fullPath, 'SKILL.md'))
|
|
94
|
-
})
|
|
95
|
-
if (skills.length === skillDirs.length) {
|
|
96
|
-
return
|
|
97
|
-
}
|
|
98
|
-
if (skills.length !== skillDirs.length) {
|
|
99
|
-
// 查询未被注册的skill,自动注册
|
|
100
|
-
skillDirs.forEach((skillDir) => {
|
|
101
|
-
if (
|
|
102
|
-
!skills.some(
|
|
103
|
-
(skill) => skill.skillDirName === skillDir || skill.name === skillDir,
|
|
104
|
-
)
|
|
105
|
-
) {
|
|
106
|
-
this._registerSkill(skillDir, false)
|
|
107
|
-
}
|
|
108
|
-
})
|
|
109
|
-
// 查询已注册但目录不存在的skill,自动从列表中删除
|
|
110
|
-
skills.forEach((skill) => {
|
|
111
|
-
if (!skillDirs.includes(skill.skillDirName)) {
|
|
112
|
-
this.remove(skill.name)
|
|
113
|
-
}
|
|
114
|
-
})
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// 添加skills
|
|
119
|
-
async add(skillName) {
|
|
120
|
-
// 从当前目录process.pwd()查询是否存在同名的skill
|
|
121
|
-
// 如果存在则判断是否是目录=>1.如果是目录则拷贝到skills目录下,并添加到config中 2.如果是zip文件则解压到skills目录下,并添加到config中
|
|
122
|
-
// 如果不存在则提示从ClawHub中下载https://clawhub.ai/
|
|
123
|
-
const baseName = path.basename(skillName, '.zip')
|
|
124
|
-
const fileNames = fs.readdirSync(process.cwd())
|
|
125
|
-
const file = fileNames.find(
|
|
126
|
-
(name) => name === baseName || name === `${baseName}.zip`,
|
|
127
|
-
)
|
|
128
|
-
if (file) {
|
|
129
|
-
// 如果存在同名文件,则判断是否是目录
|
|
130
|
-
const baseDir = path.join(process.cwd(), file)
|
|
131
|
-
if (fs.statSync(baseDir).isDirectory()) {
|
|
132
|
-
// 如果是目录,则拷贝到skills目录下,并添加到config中
|
|
133
|
-
fs.copySync(baseDir, path.join(this.skillDir, file))
|
|
134
|
-
this._registerSkill(baseName)
|
|
135
|
-
aiConsole.logSuccess(`Skill "${baseName}" added successfully!`)
|
|
136
|
-
} else if (path.extname(file) === '.zip') {
|
|
137
|
-
// 如果是zip文件,则解压到skills目录下,并添加到config中
|
|
138
|
-
const extractPath = path.join(this.skillDir, baseName)
|
|
139
|
-
await extract(baseDir, { dir: extractPath })
|
|
140
|
-
this._registerSkill(baseName)
|
|
141
|
-
aiConsole.logSuccess(`Skill "${baseName}" added successfully!`)
|
|
142
|
-
} else {
|
|
143
|
-
aiConsole.logError(`File "${file}" is not a directory or a zip file.`)
|
|
144
|
-
}
|
|
145
|
-
} else {
|
|
146
|
-
aiConsole.logError(
|
|
147
|
-
`No skill named "${skillName}" found in current directory. Please download it from ClawHub (https://clawhub.ai/) and place it in the current directory.`,
|
|
148
|
-
)
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// install('https://clawhub.ai/TheSethRose/agent-browser')
|
|
153
|
-
async install(skillUrl) {
|
|
154
|
-
// 从ClawHub下载zip并解压到skills目录下,并添加到config中
|
|
155
|
-
if (!skillUrl || typeof skillUrl !== 'string') {
|
|
156
|
-
aiConsole.logError('Invalid skill URL. Please provide a valid ClawHub URL.')
|
|
157
|
-
return
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
let parsedUrl
|
|
161
|
-
try {
|
|
162
|
-
parsedUrl = new URL(skillUrl)
|
|
163
|
-
} catch (error) {
|
|
164
|
-
aiConsole.logError('Invalid skill URL format.')
|
|
165
|
-
return
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
const host = parsedUrl.hostname.toLowerCase()
|
|
169
|
-
if (host !== 'clawhub.ai' && host !== 'www.clawhub.ai') {
|
|
170
|
-
aiConsole.logError(
|
|
171
|
-
'Only ClawHub URLs are supported, e.g. https://clawhub.ai/author/skill-name',
|
|
172
|
-
)
|
|
173
|
-
return
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
const segments = parsedUrl.pathname.split('/').filter(Boolean)
|
|
177
|
-
if (segments.length < 2) {
|
|
178
|
-
aiConsole.logError(
|
|
179
|
-
'Invalid ClawHub URL. Expected format: https://clawhub.ai/<author>/<skill-name>',
|
|
180
|
-
)
|
|
181
|
-
return
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
const skillName = path.basename(segments[1], '.zip')
|
|
185
|
-
const skills = this.readSkills()
|
|
186
|
-
if (skills.some((skill) => skill.name === skillName)) {
|
|
187
|
-
aiConsole.logError(`Skill with name "${skillName}" already exists in config.`)
|
|
188
|
-
return
|
|
189
|
-
}
|
|
190
|
-
// 查看目录是否存在当前的skill
|
|
191
|
-
const skillPath = path.join(this.skillDir, skillName)
|
|
192
|
-
if (fs.existsSync(skillPath)) {
|
|
193
|
-
aiConsole.logError(`Skill "${skillName}" already exists in the skills directory.`)
|
|
194
|
-
return
|
|
195
|
-
}
|
|
196
|
-
const zipFilePath = path.join(this.skillDir, `${skillName}.zip`)
|
|
197
|
-
const extractPath = path.join(this.skillDir, skillName)
|
|
198
|
-
|
|
199
|
-
try {
|
|
200
|
-
// 自动获取download地址
|
|
201
|
-
const response = await axios({
|
|
202
|
-
method: 'get',
|
|
203
|
-
url: skillUrl,
|
|
204
|
-
responseType: 'text',
|
|
205
|
-
timeout: 30000,
|
|
206
|
-
maxRedirects: 5,
|
|
207
|
-
validateStatus: (status) => status >= 200 && status < 300,
|
|
208
|
-
})
|
|
209
|
-
|
|
210
|
-
// 解析HTML获取下载链接
|
|
211
|
-
const html = response.data
|
|
212
|
-
const $ = cheerio.load(html)
|
|
213
|
-
const downloadHref = $('a').filter((i, el) => {
|
|
214
|
-
const href = $(el).attr('href')
|
|
215
|
-
return href && (href.startsWith('https://wry-manatee-359.convex.site/api/v1/download') || href.endsWith('.zip'))
|
|
216
|
-
}).attr('href')
|
|
217
|
-
|
|
218
|
-
if (!downloadHref) {
|
|
219
|
-
aiConsole.logError(`No download link found for skill "${skillName}".`)
|
|
220
|
-
return
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
const downloadUrl = new URL(downloadHref, parsedUrl.origin).toString()
|
|
224
|
-
const zipResponse = await axios({
|
|
225
|
-
method: 'get',
|
|
226
|
-
url: downloadUrl,
|
|
227
|
-
responseType: 'arraybuffer',
|
|
228
|
-
timeout: 60000,
|
|
229
|
-
maxRedirects: 5,
|
|
230
|
-
validateStatus: (status) => status >= 200 && status < 300,
|
|
231
|
-
})
|
|
232
|
-
fs.writeFileSync(zipFilePath, Buffer.from(zipResponse.data))
|
|
233
|
-
await extract(zipFilePath, { dir: extractPath })
|
|
234
|
-
this._registerSkill(skillName)
|
|
235
|
-
aiConsole.logSuccess(`Skill "${skillName}" installed successfully!`)
|
|
236
|
-
} catch (error) {
|
|
237
|
-
aiConsole.logError(`Failed to install skill "${skillName}": ${error.message}`)
|
|
238
|
-
} finally {
|
|
239
|
-
fs.removeSync(zipFilePath)
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
// 根据名称或索引 删除skills
|
|
244
|
-
remove(skillName) {
|
|
245
|
-
const skills = this.readSkills()
|
|
246
|
-
const skillObj = this._getSkill(skills, skillName)
|
|
247
|
-
if (!skillObj) {
|
|
248
|
-
return
|
|
249
|
-
}
|
|
250
|
-
const { skill, index } = skillObj
|
|
251
|
-
let skillPath = skill.location
|
|
252
|
-
if (!skillPath) {
|
|
253
|
-
skillPath = path.join(this.skillDir, skill.skillDirName)
|
|
254
|
-
}
|
|
255
|
-
skills.splice(index, 1)
|
|
256
|
-
this.writeSkills(skills)
|
|
257
|
-
if (fs.existsSync(skillPath)) {
|
|
258
|
-
fs.removeSync(skillPath)
|
|
259
|
-
}
|
|
260
|
-
aiConsole.logSuccess(`Skill "${skill.name}" removed successfully!`)
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
// 根据名称或索引 启用skill-限制最大启用100个
|
|
264
|
-
enable(skillName) {
|
|
265
|
-
const skills = this.readSkills()
|
|
266
|
-
const enabledCount = skills.filter((skill) => skill.enable).length
|
|
267
|
-
if (enabledCount >= 100) {
|
|
268
|
-
aiConsole.logError('Cannot enable more than 100 skills.')
|
|
269
|
-
return
|
|
270
|
-
}
|
|
271
|
-
const skillObj = this._getSkill(skills, skillName)
|
|
272
|
-
if (!skillObj) {
|
|
273
|
-
return
|
|
274
|
-
}
|
|
275
|
-
const { skill } = skillObj
|
|
276
|
-
skill.enable = true
|
|
277
|
-
this.writeSkills(skills)
|
|
278
|
-
aiConsole.logSuccess(`Skill "${skill.name}" enabled successfully!`)
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
// 根据名称或索引 禁用skill
|
|
282
|
-
disable(skillName) {
|
|
283
|
-
const skills = this.readSkills()
|
|
284
|
-
const skillObj = this._getSkill(skills, skillName)
|
|
285
|
-
if (!skillObj) {
|
|
286
|
-
return
|
|
287
|
-
}
|
|
288
|
-
const { skill } = skillObj
|
|
289
|
-
skill.enable = false
|
|
290
|
-
this.writeSkills(skills)
|
|
291
|
-
aiConsole.logSuccess(`Skill "${skill.name}" disabled successfully!`)
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
// 解析skill的描述文件,获取name、description
|
|
295
|
-
_registerSkill(skillDirName, enable = true) {
|
|
296
|
-
const skills = this.readSkills()
|
|
297
|
-
// 同名检测
|
|
298
|
-
if (skills.some((skill) => skill.name === skillDirName)) {
|
|
299
|
-
throw new Error(
|
|
300
|
-
`Skill with name "${skillDirName}" already exists in config.`,
|
|
301
|
-
)
|
|
302
|
-
}
|
|
303
|
-
const skillDirPath = path.join(this.skillDir, skillDirName)
|
|
304
|
-
// 获取name、description
|
|
305
|
-
const skillInfo = this._parseSkill(skillDirPath)
|
|
306
|
-
const name = skillInfo.name || skillDirName
|
|
307
|
-
const description = skillInfo.description || ''
|
|
308
|
-
skills.push({
|
|
309
|
-
name,
|
|
310
|
-
description,
|
|
311
|
-
enable,
|
|
312
|
-
baseDir: this.skillDir,
|
|
313
|
-
skillDirName: skillDirName,
|
|
314
|
-
...skillInfo,
|
|
315
|
-
})
|
|
316
|
-
this.writeSkills(skills)
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
_getSkill(skills, skillName) {
|
|
320
|
-
let index = parseInt(skillName, 10)
|
|
321
|
-
let skill = null
|
|
322
|
-
if (!isNaN(index)) {
|
|
323
|
-
if (index < 0 || index >= skills.length) {
|
|
324
|
-
aiConsole.logError(`Skill index "${index}" is out of range.`)
|
|
325
|
-
} else {
|
|
326
|
-
skill = skills[index]
|
|
327
|
-
}
|
|
328
|
-
} else {
|
|
329
|
-
index = skills.findIndex((skill) => skill.name === skillName)
|
|
330
|
-
if (index === -1) {
|
|
331
|
-
aiConsole.logError(`Skill with name "${skillName}" not found in config.`)
|
|
332
|
-
return
|
|
333
|
-
}
|
|
334
|
-
skill = skills[index]
|
|
335
|
-
}
|
|
336
|
-
return {
|
|
337
|
-
skill,
|
|
338
|
-
index,
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
|
|
343
|
-
module.exports = SkillConfigManager
|
package/src/cli/SkillParser.js
DELETED
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
const fs = require('fs-extra')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
const yaml = require('js-yaml')
|
|
4
|
-
|
|
5
|
-
function extractFrontmatter(content, skillPath) {
|
|
6
|
-
// Support UTF-8 BOM and both LF/CRLF line endings.
|
|
7
|
-
const normalizedContent = content.replace(/^\uFEFF/, '');
|
|
8
|
-
const frontmatterMatch = normalizedContent.match(/^---\s*\r?\n([\s\S]*?)\r?\n---\s*(?:\r?\n|$)/);
|
|
9
|
-
if (!frontmatterMatch) {
|
|
10
|
-
throw new Error(`No frontmatter found in ${skillPath}`);
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
return frontmatterMatch[1];
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function parseSkillMetadata(skillPath) {
|
|
17
|
-
const content = fs.readFileSync(skillPath, 'utf-8');
|
|
18
|
-
|
|
19
|
-
const frontmatter = extractFrontmatter(content, skillPath);
|
|
20
|
-
const metadata = {};
|
|
21
|
-
|
|
22
|
-
// 解析 key: value 或 key: "quoted value"
|
|
23
|
-
const lines = frontmatter.split(/\r?\n/);
|
|
24
|
-
for (const line of lines) {
|
|
25
|
-
const match = line.match(/^(\w+):\s*(.+)$/);
|
|
26
|
-
if (match) {
|
|
27
|
-
const [, key, value] = match;
|
|
28
|
-
// 去除引号
|
|
29
|
-
metadata[key] = value.replace(/^["']|["']$/g, '');
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
return {
|
|
34
|
-
name: metadata.name,
|
|
35
|
-
description: metadata.description,
|
|
36
|
-
homepage: metadata.homepage,
|
|
37
|
-
location: path.dirname(skillPath),
|
|
38
|
-
skillFilePath: skillPath,
|
|
39
|
-
metadata: metadata.metadata || {}
|
|
40
|
-
};
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function parseSkillMetadataYaml(skillPath) {
|
|
44
|
-
const content = fs.readFileSync(skillPath, 'utf-8');
|
|
45
|
-
const frontmatterContent = extractFrontmatter(content, skillPath);
|
|
46
|
-
const frontmatter = yaml.load(frontmatterContent);
|
|
47
|
-
|
|
48
|
-
return {
|
|
49
|
-
name: frontmatter.name,
|
|
50
|
-
description: frontmatter.description,
|
|
51
|
-
homepage: frontmatter.homepage,
|
|
52
|
-
location: path.dirname(skillPath),
|
|
53
|
-
metadata: frontmatter.metadata || {},
|
|
54
|
-
skillFilePath: skillPath,
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
module.exports = { parseSkillMetadata, parseSkillMetadataYaml }
|