deepfish-ai 1.0.8 → 1.0.11

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 CHANGED
@@ -1,6 +1,5 @@
1
1
  <div align="center" style="display:flex;align-items: center;justify-content: center;">
2
- <img src="./images/logo2.png" alt="DeepFish" style="width:55px;" />
3
- <span style="font-size: 30px;font-weight: bold;color:#3386FE">DeepFish</span>
2
+ <img src="./images/title-img.png" alt="DeepFish" width="300" />
4
3
  </div>
5
4
 
6
5
  ---
@@ -25,7 +24,7 @@
25
24
  />
26
25
  </div>
27
26
 
28
- <img src="./images/banner.png" alt="AI Command Line Tool Screenshot" style="width:100%;text-align:center;" />
27
+ <img src="./images/banner.png" alt="banner" style="width:100%;text-align:center;" />
29
28
 
30
29
 
31
30
 
package/README_CN.md CHANGED
@@ -1,6 +1,5 @@
1
1
  <div align="center" style="display:flex;align-items: center;justify-content: center;">
2
- <img src="./images/logo2.png" alt="DeepFish" style="width:55px;" />
3
- <span style="font-size: 30px;font-weight: bold;color:#3386FE">DeepFish</span>
2
+ <img src="./images/title-img.png" alt="DeepFish" width="300" />
4
3
  </div>
5
4
 
6
5
  ---
@@ -25,7 +24,7 @@
25
24
  />
26
25
  </div>
27
26
 
28
- <img src="./images/banner.png" alt="AI 命令行工具截图" style="width:100%;text-align:center;" />
27
+ <img src="./images/banner.png" alt="banner" style="width:100%;text-align:center;" />
29
28
 
30
29
 
31
30
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deepfish-ai",
3
- "version": "1.0.8",
3
+ "version": "1.0.11",
4
4
  "description": "An AI command-line tool that converts natural language instructions into operating system commands and file operations, supporting Ollama, DeepSeek, and other models compatible with the OpenAI API specification.",
5
5
  "main": "src/index.js",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -3,9 +3,9 @@ const { program } = require("commander");
3
3
  const inquirer = require("inquirer");
4
4
  const fs = require("fs");
5
5
  const AICLI = require("./core/AICLI");
6
- const { logSuccess, logError, addExtensionToConfig, removeExtensionFromConfig, viewExtensionsFromConfig, getConfigPath } = require("./core/utils");
6
+ const { logSuccess, logError } = require("./core/utils");
7
+ const { getDefaultConfig, addExtensionToConfig, removeExtensionFromConfig, viewExtensionsFromConfig, getConfigPath } = require("./core/config");
7
8
  const userConfigPath = getConfigPath()
8
- const getDefaultConfig = require("./core/DefaultConfig");
9
9
 
10
10
  async function handleMissingConfig() {
11
11
  logError("Configuration file not initialized");
@@ -2,8 +2,8 @@
2
2
  * @Author: Roman 306863030@qq.com
3
3
  * @Date: 2026-03-17 09:12:22
4
4
  * @LastEditors: Roman 306863030@qq.com
5
- * @LastEditTime: 2026-03-17 10:27:12
6
- * @FilePath: \cmd\src\core\ai-services\AiWorker\AiPrompt.js
5
+ * @LastEditTime: 2026-03-17 17:04:43
6
+ * @FilePath: \deepfish\src\core\ai-services\AiWorker\AiPrompt.js
7
7
  * @Description: AI请求提示词
8
8
  * @
9
9
  */
@@ -19,7 +19,7 @@ const AiAgentSystemPrompt = `
19
19
  ### 工具使用规则
20
20
  优先使用工具完成任务:可调用 executeJSCode 运行 Node.js 代码处理复杂逻辑;可调用 executeCommand 运行系统命令行工具(如 git、npm 等),工具调用需确保语法/指令符合当前操作系统规范(Windows/macOS/Linux 区分)。
21
21
 
22
- ### 大文件处理规则(分步执行)
22
+ ### 大文本文件处理规则(分步执行)
23
23
  处理长文档等大文件(单文件>20KB)时,必须按以下步骤分块处理:
24
24
  1. 预处理:先执行文件大小/结构检查(如通过命令行/JS 代码获取文件大小、判断文件格式),输出检查结果;
25
25
  2. 分块规则:按5KB-10KB/块拆分文件,拆分后每个块生成独立临时文件(命名格式:{原文件名}_chunk{序号}.tmp);
@@ -2,8 +2,8 @@
2
2
  * @Author: Roman 306863030@qq.com
3
3
  * @Date: 2026-03-16 09:18:05
4
4
  * @LastEditors: Roman 306863030@qq.com
5
- * @LastEditTime: 2026-03-17 10:22:46
6
- * @FilePath: \cmd\src\core\ai-services\AiWorker\index.js
5
+ * @LastEditTime: 2026-03-17 17:03:16
6
+ * @FilePath: \deepfish\src\core\ai-services\AiWorker\index.js
7
7
  * @Description: 工作流类
8
8
  * @
9
9
  */
@@ -40,18 +40,18 @@ class AiWorker {
40
40
  }
41
41
  this.messages = messages
42
42
  this.aiAgent.aiMessageManager.reLinkMsgs(this.messages)
43
- this._recoverHistory(goal, this.messages)
43
+ await this._recoverHistory(goal, this.messages)
44
44
  } else {
45
45
  if (!this.messages.length) {
46
46
  this.messages = getInitialMessages(goal)
47
- this.aiAgent.work(this.messages)
47
+ await this.aiAgent.work(this.messages)
48
48
  } else {
49
49
  this.aiAgent.aiMessageManager.reLinkMsgs(this.messages)
50
50
  this.aiAgent.aiMessageManager.addMsg({
51
51
  role: 'user',
52
52
  content: goal,
53
53
  })
54
- this.aiAgent.work(this.messages)
54
+ await this.aiAgent.work(this.messages)
55
55
  }
56
56
  }
57
57
  // this.aiRecorder.clear()
@@ -0,0 +1,187 @@
1
+ const path = require('path')
2
+ const os = require('os')
3
+ const fs = require('fs-extra');
4
+ const { logError, logSuccess } = require('./utils');
5
+
6
+ function getDefaultConfig() {
7
+ return {
8
+ ai: [],
9
+ currentAi: "",
10
+ maxIterations: -1, // ai完成工作流的最大迭代次数
11
+ maxMessagesLength: 50000, // 最大压缩长度
12
+ maxMessagesCount: 40, // 最大压缩数量
13
+ extensions: [],
14
+ isRecordHistory: false, // 是否创建工作流执行记录文件,用于因意外终止恢复工作流
15
+ isLog: false // 是否创建工作流执行日志
16
+ };
17
+ }
18
+
19
+ // 添加扩展
20
+ function addExtensionToConfig(fileName) {
21
+ // 检查 fileName 是否为空
22
+ if (!fileName) {
23
+ logError("Extension file name is required.");
24
+ return;
25
+ }
26
+ const filePath = path.resolve(process.cwd(), fileName);
27
+ // 判断是否路径是文件还是目录
28
+ if (fs.statSync(filePath).isDirectory()) {
29
+ // 扫描目录和子目录下所有js、cjs文件
30
+ const files = traverseFiles()
31
+ const jsFiles = files.filter((file) => file.endsWith(".js") || file.endsWith(".cjs"));
32
+ jsFiles.forEach((jsFile) => {
33
+ // 读取文件,查询文件内是否存在‘descriptions’和‘functions’
34
+ const fileContent = fs.readFileSync(jsFile, "utf-8");
35
+ if (fileContent.includes("descriptions") && fileContent.includes("functions")) {
36
+ addExtensionToConfig(jsFile);
37
+ }
38
+ });
39
+ return;
40
+ }
41
+ // 判断文件是否存在
42
+ if (!fs.existsSync(filePath)) {
43
+ logError(`File not found: ${filePath}`);
44
+ return;
45
+ }
46
+ const userConfigPath = path.join(os.homedir(), ".ai-cmd.config.js");
47
+ if (!fs.existsSync(userConfigPath)) {
48
+ logError(
49
+ `User config file not found: ${userConfigPath}. Please run 'ai config reset' first.`,
50
+ );
51
+ return;
52
+ }
53
+ const userConfig = require(userConfigPath);
54
+ if (userConfig.extensions && Array.isArray(userConfig.extensions)) {
55
+ userConfig.extensions.push(filePath);
56
+ } else {
57
+ userConfig.extensions = [filePath];
58
+ }
59
+ // 数组去重
60
+ userConfig.extensions = [...new Set(userConfig.extensions)];
61
+ fs.writeFileSync(
62
+ userConfigPath,
63
+ `module.exports = ${JSON.stringify(userConfig, null, 2)}`,
64
+ );
65
+ logSuccess(
66
+ `Extension added to config: ${filePath}.`,
67
+ );
68
+ }
69
+
70
+ // 移除扩展
71
+ function removeExtensionFromConfig(fileName) {
72
+ const userConfigPath = path.join(os.homedir(), ".ai-cmd.config.js");
73
+ if (!fs.existsSync(userConfigPath)) {
74
+ logError(
75
+ `User config file not found: ${userConfigPath}. Please run 'ai config reset' first.`,
76
+ );
77
+ return;
78
+ }
79
+ const userConfig = require(userConfigPath);
80
+ // 增加对数字索引的支持
81
+ if (!isNaN(Number(fileName))) {
82
+ const extIndex = Number(fileName);
83
+ if (extIndex < 0 || extIndex >= userConfig.extensions.length) {
84
+ logError(`Invalid extension index: ${extIndex}`);
85
+ return;
86
+ }
87
+ const filePath = userConfig.extensions.splice(extIndex, 1);
88
+ fs.writeFileSync(
89
+ userConfigPath,
90
+ `module.exports = ${JSON.stringify(userConfig, null, 2)}`,
91
+ );
92
+ logSuccess(
93
+ `Extension removed from config: ${filePath}.You can run 'ai ext ls' to view the changes.`,
94
+ );
95
+ return;
96
+ }
97
+ const filePath = path.resolve(process.cwd(), fileName);
98
+ // 判断文件是否存在
99
+ if (!fs.existsSync(filePath)) {
100
+ logError(`File not found: ${filePath}`);
101
+ return;
102
+ }
103
+ if (userConfig.extensions && Array.isArray(userConfig.extensions)) {
104
+ userConfig.extensions = userConfig.extensions.filter(
105
+ (ext) => ext !== filePath,
106
+ );
107
+ }
108
+ fs.writeFileSync(
109
+ userConfigPath,
110
+ `module.exports = ${JSON.stringify(userConfig, null, 2)}`,
111
+ );
112
+ logSuccess(
113
+ `Extension removed from config: ${filePath}.You can run 'ai ext ls' to view the changes.`,
114
+ );
115
+ }
116
+
117
+ // 查看扩展列表
118
+ function viewExtensionsFromConfig() {
119
+ const userConfigPath = path.join(os.homedir(), ".ai-cmd.config.js");
120
+ if (!fs.existsSync(userConfigPath)) {
121
+ logError(
122
+ `User config file not found: ${userConfigPath}. Please run 'ai config reset' first.`,
123
+ );
124
+ return;
125
+ }
126
+ const userConfig = require(userConfigPath);
127
+ if (userConfig.extensions && Array.isArray(userConfig.extensions)) {
128
+ console.log("=".repeat(50));
129
+ // 打印扩展列表,并加上索引
130
+ if (userConfig.extensions.length === 0) {
131
+ console.log(`No extensions in config.`);
132
+ } else {
133
+ console.log("Extensions in config:");
134
+ userConfig.extensions.forEach((ext, index) => {
135
+ console.log(`[${index}] ${ext}`);
136
+ });
137
+ }
138
+ console.log("=".repeat(50));
139
+ } else {
140
+ logSuccess(`No extensions in config.`);
141
+ }
142
+ }
143
+
144
+
145
+ function traverseFiles() {
146
+ try {
147
+ const currentDir = process.cwd();
148
+ const allFiles = [];
149
+ const currentItems = fs.readdirSync(currentDir, { withFileTypes: true });
150
+ for (const item of currentItems) {
151
+ const itemPath = path.join(currentDir, item.name);
152
+ if (item.isFile()) {
153
+ allFiles.push(itemPath);
154
+ continue;
155
+ }
156
+ if (item.isDirectory()) {
157
+ try {
158
+ const subItems = fs.readdirSync(itemPath, { withFileTypes: true });
159
+ for (const subItem of subItems) {
160
+ if (subItem.isFile()) {
161
+ allFiles.push(path.join(itemPath, subItem.name));
162
+ }
163
+ }
164
+ } catch (subErr) {
165
+ console.warn(`读取子目录失败 ${itemPath}:${subErr.message}`);
166
+ }
167
+ }
168
+ }
169
+ return allFiles;
170
+ } catch (err) {
171
+ console.error(`遍历目录失败:${err.message}`);
172
+ return [];
173
+ }
174
+ }
175
+
176
+ // 获取配置文件所在目录
177
+ function getConfigPath() {
178
+ return path.join(os.homedir(), ".ai-cmd.config.js");
179
+ }
180
+
181
+ module.exports = {
182
+ getDefaultConfig,
183
+ addExtensionToConfig,
184
+ removeExtensionFromConfig,
185
+ viewExtensionsFromConfig,
186
+ getConfigPath
187
+ };
@@ -103,7 +103,7 @@ async function executeJSCode(code) {
103
103
  const Func = new Function(
104
104
  "Tools",
105
105
  "require",
106
- "return (async () => { " + code + " })()",
106
+ `return (async () => { this.logMessages = [];const originalLog = console.log;const newLog = function(){ originalLog.apply(console, arguments); this.logMessages.push(Array.from(arguments).join(" "))};console.log = newLog.bind(this);${code };return this.logMessages.join('\\n'); })()`,
107
107
  );
108
108
  const originalRequire = require;
109
109
  const newRequire = (modulePath) => {
@@ -113,7 +113,14 @@ async function executeJSCode(code) {
113
113
  }
114
114
  return originalRequire(modulePath);
115
115
  };
116
+
117
+ const newLog = function () {
118
+ console.log.apply(console, arguments);
119
+ this.logMessages.push(Array.from(arguments).join(" "))
120
+ }
121
+
116
122
  const result = await Func(functions, newRequire);
123
+
117
124
  return result || "";
118
125
  } catch (error) {
119
126
  logError(`Error executing code: ${error.stack}`);
@@ -419,7 +426,7 @@ const descriptions = [
419
426
  function: {
420
427
  name: "executeJSCode",
421
428
  description:
422
- '执行JavaScript代码,返回代码执行结果。代码中可通过Tools命名空间直接调用其他工具函数(如await Tools.createFile(),注意:不需要使用require引入),Tools中引入了一些常用库可直接调用(Tools.fs="fs-extra", Tool.dayjs="dayjs", Tool.axios="axios", Tool.lodash="lodash"),支持引入自定义模块(需使用绝对路径)。注意:1.代码中不要使用__dirname获取当前目录,请使用path.resolve(".")来获取当前目录。2.console.log打印的结果不能被程序捕获,代码体需要使用return来返回结果,不能通过控制台中打印捕获结果。3.执行失败时会抛出错误,成功时返回代码执行结果或空字符串。',
429
+ '执行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.执行失败时会抛出错误,成功时返回代码执行结果或空字符串。',
423
430
  parameters: {
424
431
  type: "object",
425
432
  properties: {
@@ -8,6 +8,7 @@ const shelljs = require('shelljs')
8
8
  const iconv = require('iconv-lite') // 用于编码转换
9
9
  const os = require('os') // 用于判断系统类型
10
10
  const { logError } = require('../utils')
11
+ const { getGlobalNodeModulesPath } = require('../utils/node-root')
11
12
 
12
13
  class ExtensionManager {
13
14
  constructor(aiCli) {
@@ -76,7 +77,7 @@ class ExtensionManager {
76
77
  // 扫描本程序所在目录下node_modules目录
77
78
  const nodeModulesPath1 = path.resolve(__dirname, '../../../node_modules')
78
79
  // 扫描根node_modules目录
79
- const nodeModulesPath2 = this._executeCommand('npm root -g')
80
+ const nodeModulesPath2 = getGlobalNodeModulesPath()
80
81
  // 扫描命令执行目录下node_modules目录
81
82
  const nodeModulesPath3 = path.resolve(process.cwd(), 'node_modules')
82
83
  // 扫描命令执行目录
@@ -87,6 +88,9 @@ class ExtensionManager {
87
88
  nodeModulesPath3,
88
89
  nodeModulesPath4,
89
90
  ]) {
91
+ if (!dirPath) {
92
+ continue
93
+ }
90
94
  if (!fs.existsSync(dirPath)) {
91
95
  continue
92
96
  }
@@ -95,28 +99,23 @@ class ExtensionManager {
95
99
  // 如果是目录且目录名称前缀是"deepfish-",则认为是扩展模块
96
100
  const extensionDir = path.resolve(dirPath, fileName)
97
101
  if (
98
- fileName.startsWith('deepfish-') && fileName !== 'deepfish-ai' &&
102
+ fileName.startsWith('deepfish-') &&
103
+ fileName !== 'deepfish-ai' &&
99
104
  fs.statSync(extensionDir).isDirectory()
100
105
  ) {
101
106
  const subDirNames = fs.readdirSync(extensionDir)
102
- for (const subDirName of subDirNames) {
103
- const subDirPath = path.resolve(extensionDir, subDirName)
104
- if (fs.statSync(subDirPath).isDirectory()) {
105
- const extNames = fs.readdirSync(subDirPath)
106
- const jsFiles = extNames.filter(
107
- (file) => file.endsWith('.js') || file.endsWith('.cjs'),
108
- )
109
- jsFiles.forEach((jsFile) => {
110
- const jsFilePath = path.resolve(subDirPath, jsFile)
111
- // 读取文件,查询文件内是否存在‘descriptions’和‘functions
112
- const fileContent = fs.readFileSync(jsFilePath, 'utf-8')
113
- if (
114
- fileContent.includes('descriptions') &&
115
- fileContent.includes('functions')
116
- ) {
117
- result.push(jsFilePath)
118
- }
119
- })
107
+ const jsFiles = subDirNames.filter(
108
+ (file) => file.endsWith('.js') || file.endsWith('.cjs'),
109
+ )
110
+ for (const jsFile of jsFiles) {
111
+ const jsFilePath = path.resolve(extensionDir, jsFile)
112
+ // 读取文件,查询文件内是否存在‘descriptions’和‘functions’
113
+ const fileContent = fs.readFileSync(jsFilePath, 'utf-8')
114
+ if (
115
+ fileContent.includes('descriptions') &&
116
+ fileContent.includes('functions')
117
+ ) {
118
+ result.push(jsFilePath)
120
119
  }
121
120
  }
122
121
  }
@@ -0,0 +1,140 @@
1
+ const { execSync } = require('child_process');
2
+ const path = require('path');
3
+ const fs = require('fs');
4
+
5
+ /**
6
+ * 辅助函数:安全执行 shell 命令(避免执行失败导致程序崩溃)
7
+ * @param {string} cmd 要执行的命令
8
+ * @returns {string|null} 命令输出结果(失败返回 null)
9
+ */
10
+ function safeExec(cmd) {
11
+ try {
12
+ return execSync(cmd, { encoding: 'utf8', stdio: 'pipe' }).trim();
13
+ } catch (err) {
14
+ return null;
15
+ }
16
+ }
17
+
18
+ /**
19
+ * 辅助函数:解析路径(处理软链/快捷方式,验证路径存在性)
20
+ * @param {string} targetPath 待解析的路径
21
+ * @returns {string|null} 真实存在的路径(失败返回 null)
22
+ */
23
+ function resolveValidPath(targetPath) {
24
+ if (!targetPath) return null;
25
+ try {
26
+ // 解析软链/快捷方式的真实物理路径
27
+ const realPath = fs.realpathSync(targetPath);
28
+ // 验证路径是否存在
29
+ return fs.existsSync(realPath) ? realPath : null;
30
+ } catch (err) {
31
+ return null;
32
+ }
33
+ }
34
+
35
+ /**
36
+ * 从 NVM 环境中获取全局 node_modules 路径
37
+ * @returns {string|null} NVM 环境下的真实路径
38
+ */
39
+ function getNvmGlobalPath() {
40
+ // 1. 获取 NVM 根目录(优先读环境变量)
41
+ const nvmDir = process.env.NVM_DIR || (
42
+ process.platform === 'win32'
43
+ ? path.join(process.env.USERPROFILE, '.nvm')
44
+ : path.join(process.env.HOME, '.nvm')
45
+ );
46
+
47
+ // 2. 获取当前激活的 Node 版本
48
+ const nodeVersion = safeExec('nvm current');
49
+ if (!nodeVersion || nodeVersion.includes('N/A')) return null;
50
+
51
+ // 3. 拼接 NVM 下的全局 node_modules 路径
52
+ const nvmGlobalPath = path.join(
53
+ nvmDir,
54
+ 'versions',
55
+ 'node',
56
+ nodeVersion,
57
+ 'lib',
58
+ 'node_modules'
59
+ );
60
+
61
+ // 4. 解析并验证路径有效性
62
+ return resolveValidPath(nvmGlobalPath);
63
+ }
64
+
65
+ /**
66
+ * 从 NPM 命令获取全局 node_modules 路径
67
+ * @returns {string|null} NPM 返回的真实路径
68
+ */
69
+ function getNpmGlobalPath() {
70
+ // 1. 执行 npm root -g 获取路径
71
+ const npmPath = safeExec('npm root -g');
72
+ // 2. 解析并验证路径有效性
73
+ return resolveValidPath(npmPath);
74
+ }
75
+
76
+ /**
77
+ * 兜底方案:通过 Node 内置变量计算全局路径
78
+ * @returns {string|null} 计算出的路径(仅作为最后兜底)
79
+ */
80
+ function getFallbackGlobalPath() {
81
+ try {
82
+ const nodeExecPath = process.execPath;
83
+ let globalPrefix;
84
+
85
+ if (process.platform === 'win32') {
86
+ // Windows:node.exe 所在目录的上一级
87
+ globalPrefix = path.dirname(path.dirname(nodeExecPath));
88
+ } else {
89
+ // Mac/Linux:node 所在目录的上两级
90
+ globalPrefix = path.dirname(path.dirname(path.dirname(nodeExecPath)));
91
+ }
92
+
93
+ const fallbackPath = path.join(globalPrefix, 'lib', 'node_modules');
94
+ return resolveValidPath(fallbackPath);
95
+ } catch (err) {
96
+ return null;
97
+ }
98
+ }
99
+
100
+ /**
101
+ * 主函数:获取最正确的全局 node_modules 路径(自动适配所有场景)
102
+ * 优先级:NVM 路径 → NPM 命令路径 → 兜底计算路径
103
+ * @returns {string|null} 最准确的全局 node_modules 路径
104
+ */
105
+ function getGlobalNodeModulesPath() {
106
+ // 优先级 1:优先获取 NVM 环境下的路径(适配 NVM 场景)
107
+ const nvmPath = getNvmGlobalPath();
108
+ if (nvmPath) {
109
+ return nvmPath;
110
+ }
111
+
112
+ // 优先级 2:通过 npm 命令获取(普通环境最准确)
113
+ const npmPath = getNpmGlobalPath();
114
+ if (npmPath) {
115
+ return npmPath;
116
+ }
117
+
118
+ // 优先级 3:兜底计算(仅当以上都失败时使用)
119
+ const fallbackPath = getFallbackGlobalPath();
120
+ if (fallbackPath) {
121
+ return fallbackPath;
122
+ }
123
+
124
+ // 所有方案都失败
125
+ console.error('无法获取全局 node_modules 路径');
126
+ return null;
127
+ }
128
+
129
+ // 导出所有函数(方便单独使用),主函数作为默认导出
130
+ module.exports = {
131
+ safeExec,
132
+ resolveValidPath,
133
+ getNvmGlobalPath,
134
+ getNpmGlobalPath,
135
+ getFallbackGlobalPath,
136
+ getGlobalNodeModulesPath // 主函数
137
+ };
138
+
139
+ // 默认导出主函数(简化使用)
140
+ module.exports.default = getGlobalNodeModulesPath;
package/src/core/utils.js CHANGED
@@ -1,247 +1,81 @@
1
- const chalk = require("chalk");
2
- const path = require("path");
3
- const os = require("os");
4
- const fs = require("fs-extra");
1
+ const chalk = require('chalk')
2
+
5
3
 
6
4
  // 日志相关工具函数
7
5
  function logInfo(message) {
8
- log(message, "#6dd2ea");
6
+ log(message, '#6dd2ea')
9
7
  }
10
8
 
11
9
  function logSuccess(message) {
12
- log(message, "#9bed7f");
10
+ log(message, '#9bed7f')
13
11
  }
14
12
 
15
13
  function logError(message) {
16
- log(message, "#ed7f7f");
14
+ log(message, '#ed7f7f')
17
15
  }
18
16
 
19
- function writeLine(msg1, msg2 = "", color = "blue") {
20
- if (color === "blue") {
21
- process.stdout.write("\r" + chalk.hex("#6dd2ea")(msg1) + " " + msg2);
22
- } else if (color === "green") {
23
- process.stdout.write("\r" + chalk.hex("#9bed7f")(msg1) + " " + msg2);
24
- } else if (color === "red") {
25
- process.stdout.write("\r" + chalk.hex("#ed7f7f")(msg1) + " " + msg2);
17
+ function writeLine(msg1, msg2 = '', color = 'blue') {
18
+ if (color === 'blue') {
19
+ process.stdout.write('\r' + chalk.hex('#6dd2ea')(msg1) + ' ' + msg2)
20
+ } else if (color === 'green') {
21
+ process.stdout.write('\r' + chalk.hex('#9bed7f')(msg1) + ' ' + msg2)
22
+ } else if (color === 'red') {
23
+ process.stdout.write('\r' + chalk.hex('#ed7f7f')(msg1) + ' ' + msg2)
26
24
  } else {
27
- process.stdout.write("\r" + chalk.hex(color)(msg1) + " " + msg2);
25
+ process.stdout.write('\r' + chalk.hex(color)(msg1) + ' ' + msg2)
28
26
  }
29
27
  }
30
28
 
31
29
  // 流式输出
32
- async function streamOutput(text, color = "#9bed7f") {
33
- process.stdout.write(chalk.hex(color)(text));
30
+ async function streamOutput(text, color = '#9bed7f') {
31
+ process.stdout.write(chalk.hex(color)(text))
34
32
  }
35
33
 
36
34
  // 流式换行
37
35
  async function streamLineBreak() {
38
- process.stdout.write("\n");
36
+ process.stdout.write('\n')
39
37
  }
40
38
 
41
39
  function objStrToObj(str) {
42
40
  try {
43
41
  if (typeof str === 'string') {
44
- return eval(`(${str})`);
42
+ return eval(`(${str})`)
45
43
  } else {
46
44
  return str
47
45
  }
48
46
  } catch (error) {
49
- throw new Error(`对象转换失败:${error.message}`);
47
+ throw new Error(`对象转换失败:${error.message}`)
50
48
  }
51
49
  }
52
50
 
53
-
54
-
55
51
  function delay(ms) {
56
- return new Promise((resolve) => setTimeout(resolve, ms));
52
+ return new Promise((resolve) => setTimeout(resolve, ms))
57
53
  }
58
54
 
59
- function loading(label = "Thinking...") {
60
- let animationInterval;
61
- const spinners = ["|", "/", "-", "\\"];
62
- let spinnerIndex = 0;
63
- process.stdout.write("\r");
55
+ function loading(label = 'Thinking...') {
56
+ let animationInterval
57
+ const spinners = ['|', '/', '-', '\\']
58
+ let spinnerIndex = 0
59
+ process.stdout.write('\r')
64
60
  animationInterval = setInterval(() => {
65
- writeLine(spinners[spinnerIndex], label);
66
- spinnerIndex = (spinnerIndex + 1) % spinners.length;
67
- }, 200);
61
+ writeLine(spinners[spinnerIndex], label)
62
+ spinnerIndex = (spinnerIndex + 1) % spinners.length
63
+ }, 200)
68
64
  return (endLabel, isError = false) => {
69
- clearInterval(animationInterval);
65
+ clearInterval(animationInterval)
70
66
  if (endLabel) {
71
- writeLine(endLabel, "", isError ? "red" : "green");
67
+ writeLine(endLabel, '', isError ? 'red' : 'green')
72
68
  }
73
- process.stdout.write("\r\n");
74
- };
69
+ process.stdout.write('\r\n')
70
+ }
75
71
  }
76
72
 
77
73
  function log(msg, color) {
78
74
  if (!color) {
79
- console.log(msg);
80
- } else {
81
- console.log(chalk.hex(color)(msg));
82
- }
83
- }
84
-
85
- // 添加扩展
86
- function addExtensionToConfig(fileName) {
87
- // 检查 fileName 是否为空
88
- if (!fileName) {
89
- logError("Extension file name is required.");
90
- return;
91
- }
92
- const filePath = path.resolve(process.cwd(), fileName);
93
- // 判断是否路径是文件还是目录
94
- if (fs.statSync(filePath).isDirectory()) {
95
- // 扫描目录和子目录下所有js、cjs文件
96
- const files = traverseFiles()
97
- const jsFiles = files.filter((file) => file.endsWith(".js") || file.endsWith(".cjs"));
98
- jsFiles.forEach((jsFile) => {
99
- // 读取文件,查询文件内是否存在‘descriptions’和‘functions’
100
- const fileContent = fs.readFileSync(jsFile, "utf-8");
101
- if (fileContent.includes("descriptions") && fileContent.includes("functions")) {
102
- addExtensionToConfig(jsFile);
103
- }
104
- });
105
- return;
106
- }
107
- // 判断文件是否存在
108
- if (!fs.existsSync(filePath)) {
109
- logError(`File not found: ${filePath}`);
110
- return;
111
- }
112
- const userConfigPath = path.join(os.homedir(), ".ai-cmd.config.js");
113
- if (!fs.existsSync(userConfigPath)) {
114
- logError(
115
- `User config file not found: ${userConfigPath}. Please run 'ai config reset' first.`,
116
- );
117
- return;
118
- }
119
- const userConfig = require(userConfigPath);
120
- if (userConfig.extensions && Array.isArray(userConfig.extensions)) {
121
- userConfig.extensions.push(filePath);
75
+ console.log(msg)
122
76
  } else {
123
- userConfig.extensions = [filePath];
77
+ console.log(chalk.hex(color)(msg))
124
78
  }
125
- // 数组去重
126
- userConfig.extensions = [...new Set(userConfig.extensions)];
127
- fs.writeFileSync(
128
- userConfigPath,
129
- `module.exports = ${JSON.stringify(userConfig, null, 2)}`,
130
- );
131
- logSuccess(
132
- `Extension added to config: ${filePath}.`,
133
- );
134
- }
135
-
136
- // 移除扩展
137
- function removeExtensionFromConfig(fileName) {
138
- const userConfigPath = path.join(os.homedir(), ".ai-cmd.config.js");
139
- if (!fs.existsSync(userConfigPath)) {
140
- logError(
141
- `User config file not found: ${userConfigPath}. Please run 'ai config reset' first.`,
142
- );
143
- return;
144
- }
145
- const userConfig = require(userConfigPath);
146
- // 增加对数字索引的支持
147
- if (!isNaN(Number(fileName))) {
148
- const extIndex = Number(fileName);
149
- if (extIndex < 0 || extIndex >= userConfig.extensions.length) {
150
- logError(`Invalid extension index: ${extIndex}`);
151
- return;
152
- }
153
- const filePath = userConfig.extensions.splice(extIndex, 1);
154
- fs.writeFileSync(
155
- userConfigPath,
156
- `module.exports = ${JSON.stringify(userConfig, null, 2)}`,
157
- );
158
- logSuccess(
159
- `Extension removed from config: ${filePath}.You can run 'ai ext ls' to view the changes.`,
160
- );
161
- return;
162
- }
163
- const filePath = path.resolve(process.cwd(), fileName);
164
- // 判断文件是否存在
165
- if (!fs.existsSync(filePath)) {
166
- logError(`File not found: ${filePath}`);
167
- return;
168
- }
169
- if (userConfig.extensions && Array.isArray(userConfig.extensions)) {
170
- userConfig.extensions = userConfig.extensions.filter(
171
- (ext) => ext !== filePath,
172
- );
173
- }
174
- fs.writeFileSync(
175
- userConfigPath,
176
- `module.exports = ${JSON.stringify(userConfig, null, 2)}`,
177
- );
178
- logSuccess(
179
- `Extension removed from config: ${filePath}.You can run 'ai ext ls' to view the changes.`,
180
- );
181
- }
182
-
183
- // 查看扩展列表
184
- function viewExtensionsFromConfig() {
185
- const userConfigPath = path.join(os.homedir(), ".ai-cmd.config.js");
186
- if (!fs.existsSync(userConfigPath)) {
187
- logError(
188
- `User config file not found: ${userConfigPath}. Please run 'ai config reset' first.`,
189
- );
190
- return;
191
- }
192
- const userConfig = require(userConfigPath);
193
- if (userConfig.extensions && Array.isArray(userConfig.extensions)) {
194
- console.log("=".repeat(50));
195
- // 打印扩展列表,并加上索引
196
- if (userConfig.extensions.length === 0) {
197
- console.log(`No extensions in config.`);
198
- } else {
199
- console.log("Extensions in config:");
200
- userConfig.extensions.forEach((ext, index) => {
201
- console.log(`[${index}] ${ext}`);
202
- });
203
- }
204
- console.log("=".repeat(50));
205
- } else {
206
- logSuccess(`No extensions in config.`);
207
- }
208
- }
209
-
210
-
211
- function traverseFiles() {
212
- try {
213
- const currentDir = process.cwd();
214
- const allFiles = [];
215
- const currentItems = fs.readdirSync(currentDir, { withFileTypes: true });
216
- for (const item of currentItems) {
217
- const itemPath = path.join(currentDir, item.name);
218
- if (item.isFile()) {
219
- allFiles.push(itemPath);
220
- continue;
221
- }
222
- if (item.isDirectory()) {
223
- try {
224
- const subItems = fs.readdirSync(itemPath, { withFileTypes: true });
225
- for (const subItem of subItems) {
226
- if (subItem.isFile()) {
227
- allFiles.push(path.join(itemPath, subItem.name));
228
- }
229
- }
230
- } catch (subErr) {
231
- console.warn(`读取子目录失败 ${itemPath}:${subErr.message}`);
232
- }
233
- }
234
- }
235
- return allFiles;
236
- } catch (err) {
237
- console.error(`遍历目录失败:${err.message}`);
238
- return [];
239
- }
240
- }
241
-
242
- // 获取配置文件所在目录
243
- function getConfigPath() {
244
- return path.join(os.homedir(), ".ai-cmd.config.js");
245
79
  }
246
80
 
247
81
  module.exports = {
@@ -252,10 +86,6 @@ module.exports = {
252
86
  writeLine,
253
87
  streamOutput,
254
88
  streamLineBreak,
255
- addExtensionToConfig,
256
- removeExtensionFromConfig,
257
- viewExtensionsFromConfig,
258
89
  objStrToObj,
259
90
  delay,
260
- getConfigPath
261
- };
91
+ }
@@ -1,14 +0,0 @@
1
- function getDefaultConfig() {
2
- return {
3
- ai: [],
4
- currentAi: "",
5
- maxIterations: -1, // ai完成工作流的最大迭代次数
6
- maxMessagesLength: 50000, // 最大压缩长度
7
- maxMessagesCount: 40, // 最大压缩数量
8
- extensions: [],
9
- isRecordHistory: false, // 是否创建工作流执行记录文件,用于因意外终止恢复工作流
10
- isLog: false // 是否创建工作流执行日志
11
- };
12
- }
13
-
14
- module.exports = getDefaultConfig;