deepfish-ai 1.0.8
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/LICENSE +21 -0
- package/README.md +344 -0
- package/README_CN.md +344 -0
- package/package.json +56 -0
- package/src/cli.js +650 -0
- package/src/core/AICLI.js +93 -0
- package/src/core/DefaultConfig.js +14 -0
- package/src/core/GlobalVariable.js +10 -0
- package/src/core/ai-services/AIService.js +25 -0
- package/src/core/ai-services/AiWorker/AIMessageManager.js +155 -0
- package/src/core/ai-services/AiWorker/AiAgent.js +151 -0
- package/src/core/ai-services/AiWorker/AiPrompt.js +37 -0
- package/src/core/ai-services/AiWorker/AiRecorder.js +120 -0
- package/src/core/ai-services/AiWorker/AiTools.js +219 -0
- package/src/core/ai-services/AiWorker/index.js +88 -0
- package/src/core/extension/BaseExtension.js +7 -0
- package/src/core/extension/DefaultExtension.js +696 -0
- package/src/core/extension/ExtensionManager.js +172 -0
- package/src/core/utils.js +261 -0
- package/src/index.js +7 -0
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "deepfish-ai",
|
|
3
|
+
"version": "1.0.8",
|
|
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
|
+
"main": "src/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ai": "src/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
11
|
+
},
|
|
12
|
+
"keywords": [
|
|
13
|
+
"ai",
|
|
14
|
+
"deepseek",
|
|
15
|
+
"cli",
|
|
16
|
+
"command-line",
|
|
17
|
+
"cmd",
|
|
18
|
+
"openai",
|
|
19
|
+
"ollama",
|
|
20
|
+
"deepfish"
|
|
21
|
+
],
|
|
22
|
+
"author": "Roman",
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/qq306863030/deepfish.git"
|
|
27
|
+
},
|
|
28
|
+
"bugs": {
|
|
29
|
+
"url": "https://github.com/qq306863030/deepfish/issues"
|
|
30
|
+
},
|
|
31
|
+
"homepage": "https://github.com/qq306863030/deepfish#readme",
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"axios": "^1.13.5",
|
|
34
|
+
"chalk": "^4.1.0",
|
|
35
|
+
"commander": "^11.0.0",
|
|
36
|
+
"dayjs": "^1.11.19",
|
|
37
|
+
"fs-extra": "^11.3.3",
|
|
38
|
+
"iconv-lite": "^0.7.2",
|
|
39
|
+
"inquirer": "^9.0.0",
|
|
40
|
+
"lodash": "^4.17.23",
|
|
41
|
+
"openai": "^6.18.0",
|
|
42
|
+
"shelljs": "^0.10.0"
|
|
43
|
+
},
|
|
44
|
+
"devDependencies": {
|
|
45
|
+
"@eslint/js": "^10.0.1",
|
|
46
|
+
"@eslint/json": "^1.0.1",
|
|
47
|
+
"eslint": "^10.0.3",
|
|
48
|
+
"globals": "^17.4.0"
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"src",
|
|
52
|
+
"README.md",
|
|
53
|
+
"README_CN.md",
|
|
54
|
+
"LICENSE"
|
|
55
|
+
]
|
|
56
|
+
}
|
package/src/cli.js
ADDED
|
@@ -0,0 +1,650 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
const { program } = require("commander");
|
|
3
|
+
const inquirer = require("inquirer");
|
|
4
|
+
const fs = require("fs");
|
|
5
|
+
const AICLI = require("./core/AICLI");
|
|
6
|
+
const { logSuccess, logError, addExtensionToConfig, removeExtensionFromConfig, viewExtensionsFromConfig, getConfigPath } = require("./core/utils");
|
|
7
|
+
const userConfigPath = getConfigPath()
|
|
8
|
+
const getDefaultConfig = require("./core/DefaultConfig");
|
|
9
|
+
|
|
10
|
+
async function handleMissingConfig() {
|
|
11
|
+
logError("Configuration file not initialized");
|
|
12
|
+
|
|
13
|
+
// Create new configuration file with empty ai array
|
|
14
|
+
console.log("Creating new configuration file:", userConfigPath);
|
|
15
|
+
const configContent = `module.exports = ${JSON.stringify(getDefaultConfig(), null, 2)}`;
|
|
16
|
+
fs.writeFileSync(userConfigPath, configContent);
|
|
17
|
+
console.log("Configuration file created with empty AI configurations.");
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async function runSetupCommand(isAdd = false) {
|
|
21
|
+
console.log("AI Service Configuration");
|
|
22
|
+
console.log("=".repeat(50));
|
|
23
|
+
|
|
24
|
+
let currentConfig = {};
|
|
25
|
+
if (fs.existsSync(userConfigPath)) {
|
|
26
|
+
try {
|
|
27
|
+
currentConfig = require(userConfigPath);
|
|
28
|
+
} catch (error) {
|
|
29
|
+
logError(
|
|
30
|
+
"Warning: Could not load existing configuration:",
|
|
31
|
+
error.message,
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
currentConfig = getDefaultConfig();
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const questions = [
|
|
39
|
+
{
|
|
40
|
+
type: "input",
|
|
41
|
+
name: "name",
|
|
42
|
+
message: "Enter AI configuration name:",
|
|
43
|
+
when: () => isAdd,
|
|
44
|
+
validate: (value) => {
|
|
45
|
+
if (value.trim() === "") {
|
|
46
|
+
return "Configuration name cannot be empty";
|
|
47
|
+
}
|
|
48
|
+
// Check if configuration with the same name already exists
|
|
49
|
+
if (fs.existsSync(userConfigPath)) {
|
|
50
|
+
try {
|
|
51
|
+
const existingConfig = require(userConfigPath);
|
|
52
|
+
if (existingConfig.ai && Array.isArray(existingConfig.ai)) {
|
|
53
|
+
const existingIndex = existingConfig.ai.findIndex(config => config.name === value.trim());
|
|
54
|
+
if (existingIndex !== -1) {
|
|
55
|
+
return "Configuration with this name already exists. Please enter a different name.";
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
// Ignore error if config file cannot be loaded
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return true;
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
{
|
|
66
|
+
type: "list",
|
|
67
|
+
name: "type",
|
|
68
|
+
message: "Select AI service type:",
|
|
69
|
+
choices: [
|
|
70
|
+
{ name: "Ollama (Local)", value: "ollama" },
|
|
71
|
+
{ name: "DeepSeek (Online)", value: "deepseek" },
|
|
72
|
+
{ name: "OpenAI (Online)", value: "openai" },
|
|
73
|
+
],
|
|
74
|
+
default: currentConfig.ai?.[0]?.type || "ollama",
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: "input",
|
|
78
|
+
name: "otherType",
|
|
79
|
+
message: "Enter custom AI service type:",
|
|
80
|
+
when: (answers) => answers.type === "other",
|
|
81
|
+
default: currentConfig.ai?.[0]?.type || "custom",
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
type: "input",
|
|
85
|
+
name: "baseUrl",
|
|
86
|
+
message: "Enter API base URL:",
|
|
87
|
+
when: (answers) => answers.type !== "other",
|
|
88
|
+
default: (answers) => {
|
|
89
|
+
switch (answers.type) {
|
|
90
|
+
case "ollama":
|
|
91
|
+
return "http://localhost:11434/v1";
|
|
92
|
+
case "deepseek":
|
|
93
|
+
return "https://api.deepseek.com";
|
|
94
|
+
case "openai":
|
|
95
|
+
return "https://api.openai.com/v1";
|
|
96
|
+
default:
|
|
97
|
+
return currentConfig.ai?.[0]?.baseUrl || "";
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
},
|
|
101
|
+
{
|
|
102
|
+
type: "input",
|
|
103
|
+
name: "otherBaseUrl",
|
|
104
|
+
message: "Enter API base URL:",
|
|
105
|
+
when: (answers) => answers.type === "other",
|
|
106
|
+
default: currentConfig.ai?.[0]?.baseUrl || "",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
type: "list",
|
|
110
|
+
name: "model",
|
|
111
|
+
message: "Select DeepSeek model:",
|
|
112
|
+
when: (answers) => answers.type === "deepseek",
|
|
113
|
+
choices: [
|
|
114
|
+
{ name: "deepseek-chat", value: "deepseek-chat" },
|
|
115
|
+
{ name: "deepseek-reasoner", value: "deepseek-reasoner" },
|
|
116
|
+
{ name: "Other", value: "other" },
|
|
117
|
+
],
|
|
118
|
+
default: "deepseek-reasoner",
|
|
119
|
+
},
|
|
120
|
+
{
|
|
121
|
+
type: "input",
|
|
122
|
+
name: "model",
|
|
123
|
+
message: "Enter model name:",
|
|
124
|
+
when: (answers) => answers.type !== "other" && answers.type !== "deepseek",
|
|
125
|
+
default: (answers) => {
|
|
126
|
+
switch (answers.type) {
|
|
127
|
+
case "ollama":
|
|
128
|
+
return "deepseek-v3.2:cloud";
|
|
129
|
+
case "openai":
|
|
130
|
+
return "gpt-4";
|
|
131
|
+
default:
|
|
132
|
+
return currentConfig.ai?.[0]?.model || "";
|
|
133
|
+
}
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
type: "input",
|
|
138
|
+
name: "deepseekOtherModel",
|
|
139
|
+
message: "Enter DeepSeek model name:",
|
|
140
|
+
when: (answers) => answers.type === "deepseek" && answers.model === "other",
|
|
141
|
+
default: currentConfig.ai?.[0]?.model || "",
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
type: "input",
|
|
145
|
+
name: "otherModel",
|
|
146
|
+
message: "Enter model name:",
|
|
147
|
+
when: (answers) => answers.type === "other",
|
|
148
|
+
default: currentConfig.ai?.[0]?.model || "",
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
type: "input",
|
|
152
|
+
name: "apiKey",
|
|
153
|
+
message: "Enter API key:",
|
|
154
|
+
when: (answers) =>
|
|
155
|
+
answers.type === "deepseek" || answers.type === "openai",
|
|
156
|
+
default: "",
|
|
157
|
+
},
|
|
158
|
+
{
|
|
159
|
+
type: "input",
|
|
160
|
+
name: "otherApiKey",
|
|
161
|
+
message: "Enter API key:",
|
|
162
|
+
when: (answers) => answers.type === "other",
|
|
163
|
+
default: currentConfig.ai?.[0]?.apiKey || "",
|
|
164
|
+
},
|
|
165
|
+
{
|
|
166
|
+
type: "number",
|
|
167
|
+
name: "temperature",
|
|
168
|
+
message: "Enter temperature (0-2):",
|
|
169
|
+
default: 0.7,
|
|
170
|
+
validate: (value) =>
|
|
171
|
+
(value >= 0 && value <= 2) || "Temperature must be between 0 and 2",
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
type: "number",
|
|
175
|
+
name: "maxTokens",
|
|
176
|
+
message: "Enter max tokens:",
|
|
177
|
+
default: 8192,
|
|
178
|
+
validate: (value) => value > 0 || "Max tokens must be greater than 0",
|
|
179
|
+
},
|
|
180
|
+
{
|
|
181
|
+
type: "confirm",
|
|
182
|
+
name: "stream",
|
|
183
|
+
message: "Enable streaming output:",
|
|
184
|
+
default: true,
|
|
185
|
+
},
|
|
186
|
+
];
|
|
187
|
+
|
|
188
|
+
const answers = await inquirer.default.prompt(questions);
|
|
189
|
+
|
|
190
|
+
// Check if name is empty when adding new configuration
|
|
191
|
+
if (isAdd && (!answers.name || answers.name.trim() === "")) {
|
|
192
|
+
console.log("Configuration name cannot be empty");
|
|
193
|
+
process.exit(1);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Check if configuration with the same name already exists
|
|
197
|
+
if (isAdd && fs.existsSync(userConfigPath)) {
|
|
198
|
+
try {
|
|
199
|
+
const existingConfig = require(userConfigPath);
|
|
200
|
+
if (existingConfig.ai && Array.isArray(existingConfig.ai)) {
|
|
201
|
+
const existingIndex = existingConfig.ai.findIndex(config => config.name === answers.name.trim());
|
|
202
|
+
if (existingIndex !== -1) {
|
|
203
|
+
console.log(`Configuration with name "${answers.name}" already exists. Please enter a different name.`);
|
|
204
|
+
await runSetupCommand(isAdd);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
} catch (error) {
|
|
209
|
+
// Ignore error if config file cannot be loaded
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const aiConfig = {
|
|
214
|
+
name: answers.name || "default",
|
|
215
|
+
type: answers.type === "other" ? answers.otherType : answers.type,
|
|
216
|
+
baseUrl: answers.type === "other" ? answers.otherBaseUrl : answers.baseUrl,
|
|
217
|
+
model: answers.type === "other" ? answers.otherModel : (answers.type === "deepseek" && answers.model === "other" ? answers.deepseekOtherModel : answers.model),
|
|
218
|
+
apiKey: answers.type === "ollama" ? 'ollama' : answers.apiKey,
|
|
219
|
+
temperature: answers.temperature,
|
|
220
|
+
maxTokens: answers.maxTokens,
|
|
221
|
+
stream: answers.stream,
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
if (isAdd) {
|
|
225
|
+
// Add new AI configuration
|
|
226
|
+
const existingConfig = fs.existsSync(userConfigPath) ? require(userConfigPath) : getDefaultConfig();
|
|
227
|
+
|
|
228
|
+
// Check if configuration with the same name already exists
|
|
229
|
+
const existingIndex = existingConfig.ai.findIndex(config => config.name === aiConfig.name);
|
|
230
|
+
if (existingIndex !== -1) {
|
|
231
|
+
logError(`Configuration with name "${aiConfig.name}" already exists.`);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
existingConfig.ai.push(aiConfig);
|
|
236
|
+
const configContent = `module.exports = ${JSON.stringify(existingConfig, null, 2)}`;
|
|
237
|
+
fs.writeFileSync(userConfigPath, configContent);
|
|
238
|
+
|
|
239
|
+
logSuccess(`AI configuration "${aiConfig.name}" added successfully!`);
|
|
240
|
+
} else {
|
|
241
|
+
// Update default configuration
|
|
242
|
+
const existingConfig = fs.existsSync(userConfigPath) ? require(userConfigPath) : getDefaultConfig();
|
|
243
|
+
|
|
244
|
+
// Add the new configuration to the array
|
|
245
|
+
existingConfig.ai.push(aiConfig);
|
|
246
|
+
existingConfig.currentAi = aiConfig.name;
|
|
247
|
+
|
|
248
|
+
const configContent = `module.exports = ${JSON.stringify(existingConfig, null, 2)}`;
|
|
249
|
+
fs.writeFileSync(userConfigPath, configContent);
|
|
250
|
+
|
|
251
|
+
logSuccess("\nConfiguration saved successfully to:", userConfigPath);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
console.log("=".repeat(50));
|
|
255
|
+
console.log("AI configuration details:");
|
|
256
|
+
console.log(`Name: ${aiConfig.name}`);
|
|
257
|
+
console.log(`Type: ${aiConfig.type}`);
|
|
258
|
+
console.log(`API Base URL: ${aiConfig.baseUrl}`);
|
|
259
|
+
console.log(`Model: ${aiConfig.model}`);
|
|
260
|
+
if (aiConfig.apiKey) {
|
|
261
|
+
console.log(`API Key: ${aiConfig.apiKey.substring(0, 8)}...`);
|
|
262
|
+
}
|
|
263
|
+
console.log(`Temperature: ${aiConfig.temperature}`);
|
|
264
|
+
console.log(`Max Tokens: ${aiConfig.maxTokens}`);
|
|
265
|
+
console.log(`Streaming Output: ${aiConfig.stream ? 'Enabled' : 'Disabled'}`);
|
|
266
|
+
console.log("=".repeat(50));
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
program
|
|
270
|
+
.version("1.0.0")
|
|
271
|
+
.description(
|
|
272
|
+
"A command-line tool that uses AI to execute commands and manipulate files",
|
|
273
|
+
)
|
|
274
|
+
.option("-p, --prompt <prompt>", "The prompt to send to the AI")
|
|
275
|
+
.option("-i, --interactive", "Start interactive mode")
|
|
276
|
+
.arguments("[prompt...]")
|
|
277
|
+
.action((prompt) => {
|
|
278
|
+
program.prompt = Array.isArray(prompt) ? prompt.join(" ") : prompt || "";
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
const extCommand = program
|
|
282
|
+
.command("ext")
|
|
283
|
+
.description("Extension management commands");
|
|
284
|
+
|
|
285
|
+
extCommand
|
|
286
|
+
.command("add <filename>")
|
|
287
|
+
.description("Add extension tool to the configuration")
|
|
288
|
+
.action((filename) => {
|
|
289
|
+
addExtensionToConfig(filename);
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
extCommand
|
|
293
|
+
.command("del <filename>")
|
|
294
|
+
.description("Remove extension tool from the configuration")
|
|
295
|
+
.action((filename) => {
|
|
296
|
+
removeExtensionFromConfig(filename);
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
extCommand
|
|
300
|
+
.command("ls")
|
|
301
|
+
.description("List all extension tools in the configuration")
|
|
302
|
+
.action(() => {
|
|
303
|
+
viewExtensionsFromConfig();
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
const configCommand = program
|
|
307
|
+
.command("config")
|
|
308
|
+
.description("Configure AI service settings");
|
|
309
|
+
|
|
310
|
+
|
|
311
|
+
configCommand
|
|
312
|
+
.command("edit")
|
|
313
|
+
.description("Edit configuration file with notepad")
|
|
314
|
+
.action(async () => {
|
|
315
|
+
if (fs.existsSync(userConfigPath)) {
|
|
316
|
+
// File exists, open for editing
|
|
317
|
+
const { exec } = require("child_process");
|
|
318
|
+
exec(`notepad "${userConfigPath}"`, (error) => {
|
|
319
|
+
if (error) {
|
|
320
|
+
logError("Error opening configuration file:", error.message);
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
} else {
|
|
324
|
+
// File doesn't exist, prompt to create
|
|
325
|
+
logError("Configuration file not initialized");
|
|
326
|
+
|
|
327
|
+
const { createConfig } = await inquirer.default.prompt([
|
|
328
|
+
{
|
|
329
|
+
type: "confirm",
|
|
330
|
+
name: "createConfig",
|
|
331
|
+
message: "Would you like to create a configuration file now?",
|
|
332
|
+
default: true,
|
|
333
|
+
},
|
|
334
|
+
]);
|
|
335
|
+
|
|
336
|
+
if (createConfig) {
|
|
337
|
+
// Create new configuration file with empty ai array
|
|
338
|
+
console.log("Creating new configuration file:", userConfigPath);
|
|
339
|
+
const newConfig = getDefaultConfig();
|
|
340
|
+
const configContent = `module.exports = ${JSON.stringify(newConfig, null, 2)}`;
|
|
341
|
+
fs.writeFileSync(userConfigPath, configContent);
|
|
342
|
+
console.log("Configuration file created with empty AI configurations.");
|
|
343
|
+
}
|
|
344
|
+
// Don't open the file after creating it
|
|
345
|
+
}
|
|
346
|
+
});
|
|
347
|
+
|
|
348
|
+
configCommand
|
|
349
|
+
.command("clear")
|
|
350
|
+
.description("Delete the configuration file")
|
|
351
|
+
.action(async () => {
|
|
352
|
+
if (!fs.existsSync(userConfigPath)) {
|
|
353
|
+
console.log("Configuration file does not exist");
|
|
354
|
+
return;
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
const { confirm } = await inquirer.default.prompt([
|
|
358
|
+
{
|
|
359
|
+
type: "confirm",
|
|
360
|
+
name: "confirm",
|
|
361
|
+
message: "Are you sure you want to delete the configuration file?",
|
|
362
|
+
default: false,
|
|
363
|
+
},
|
|
364
|
+
]);
|
|
365
|
+
|
|
366
|
+
if (confirm) {
|
|
367
|
+
fs.unlinkSync(userConfigPath);
|
|
368
|
+
logSuccess("Configuration file deleted successfully:", userConfigPath);
|
|
369
|
+
} else {
|
|
370
|
+
console.log("Operation cancelled");
|
|
371
|
+
}
|
|
372
|
+
});
|
|
373
|
+
|
|
374
|
+
configCommand
|
|
375
|
+
.command("reset")
|
|
376
|
+
.description("Reset configuration file")
|
|
377
|
+
.action(async () => {
|
|
378
|
+
if (fs.existsSync(userConfigPath)) {
|
|
379
|
+
const { confirm } = await inquirer.default.prompt([
|
|
380
|
+
{
|
|
381
|
+
type: "confirm",
|
|
382
|
+
name: "confirm",
|
|
383
|
+
message: "Are you sure you want to reset the configuration file?",
|
|
384
|
+
default: false,
|
|
385
|
+
},
|
|
386
|
+
]);
|
|
387
|
+
|
|
388
|
+
if (confirm) {
|
|
389
|
+
console.log("Resetting configuration file:", userConfigPath);
|
|
390
|
+
// Create new default configuration and overwrite existing file
|
|
391
|
+
const configContent = `module.exports = ${JSON.stringify(getDefaultConfig(), null, 2)}`;
|
|
392
|
+
fs.writeFileSync(userConfigPath, configContent);
|
|
393
|
+
console.log("Configuration file has been reset to default settings.");
|
|
394
|
+
} else {
|
|
395
|
+
console.log("Operation cancelled");
|
|
396
|
+
process.exit(0);
|
|
397
|
+
}
|
|
398
|
+
} else {
|
|
399
|
+
// Create new configuration file with empty ai array
|
|
400
|
+
const newConfig = getDefaultConfig();
|
|
401
|
+
const configContent = `module.exports = ${JSON.stringify(newConfig, null, 2)}`;
|
|
402
|
+
fs.writeFileSync(userConfigPath, configContent);
|
|
403
|
+
console.log("Configuration file created with empty AI configurations.");
|
|
404
|
+
}
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
configCommand
|
|
408
|
+
.command("add")
|
|
409
|
+
.description("Add a new AI configuration")
|
|
410
|
+
.action(async () => {
|
|
411
|
+
await runSetupCommand(true);
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
configCommand
|
|
415
|
+
.command("ls")
|
|
416
|
+
.description("List all AI configurations")
|
|
417
|
+
.action(async () => {
|
|
418
|
+
if (!fs.existsSync(userConfigPath)) {
|
|
419
|
+
await handleMissingConfig();
|
|
420
|
+
return;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
try {
|
|
424
|
+
const currentConfig = require(userConfigPath);
|
|
425
|
+
console.log("AI Configurations");
|
|
426
|
+
console.log("=".repeat(50));
|
|
427
|
+
|
|
428
|
+
if (currentConfig.ai && Array.isArray(currentConfig.ai)) {
|
|
429
|
+
if (currentConfig.ai.length === 0) {
|
|
430
|
+
logError("No AI configurations found.");
|
|
431
|
+
} else {
|
|
432
|
+
currentConfig.ai.forEach((config, index) => {
|
|
433
|
+
const isCurrent = currentConfig.currentAi === config.name;
|
|
434
|
+
console.log(`${config.name} ${isCurrent ? '(current)' : ''}`);
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
} else {
|
|
438
|
+
logError("No AI configurations found.");
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
console.log("=".repeat(50));
|
|
442
|
+
} catch (error) {
|
|
443
|
+
logError("Error loading configuration:", error.message);
|
|
444
|
+
}
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
configCommand
|
|
448
|
+
.command("use <name>")
|
|
449
|
+
.description("Set the specified AI configuration as current")
|
|
450
|
+
.action(async (name) => {
|
|
451
|
+
if (!fs.existsSync(userConfigPath)) {
|
|
452
|
+
await handleMissingConfig();
|
|
453
|
+
return;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
try {
|
|
457
|
+
const currentConfig = require(userConfigPath);
|
|
458
|
+
|
|
459
|
+
// Check if configuration with the specified name exists
|
|
460
|
+
const aiConfig = currentConfig.ai.find(config => config.name === name);
|
|
461
|
+
if (!aiConfig) {
|
|
462
|
+
logError(`Configuration with name "${name}" not found.`);
|
|
463
|
+
return;
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
// Update current AI configuration
|
|
467
|
+
currentConfig.currentAi = name;
|
|
468
|
+
const configContent = `module.exports = ${JSON.stringify(currentConfig, null, 2)}`;
|
|
469
|
+
fs.writeFileSync(userConfigPath, configContent);
|
|
470
|
+
|
|
471
|
+
logSuccess(`Current AI configuration set to "${name}" successfully.`);
|
|
472
|
+
} catch (error) {
|
|
473
|
+
logError("Error loading configuration:", error.message);
|
|
474
|
+
}
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
configCommand
|
|
478
|
+
.command("del <name>")
|
|
479
|
+
.description("Delete the specified AI configuration")
|
|
480
|
+
.action(async (name) => {
|
|
481
|
+
if (!fs.existsSync(userConfigPath)) {
|
|
482
|
+
await handleMissingConfig();
|
|
483
|
+
return;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
try {
|
|
487
|
+
const currentConfig = require(userConfigPath);
|
|
488
|
+
|
|
489
|
+
// Check if configuration with the specified name exists
|
|
490
|
+
const existingIndex = currentConfig.ai.findIndex(config => config.name === name);
|
|
491
|
+
if (existingIndex === -1) {
|
|
492
|
+
console.log(`Configuration with name "${name}" not found.`);
|
|
493
|
+
return;
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
// Check if it's the current configuration
|
|
497
|
+
if (currentConfig.currentAi === name) {
|
|
498
|
+
console.log(`Cannot delete current configuration "${name}".`);
|
|
499
|
+
return;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
// Remove the configuration
|
|
503
|
+
currentConfig.ai.splice(existingIndex, 1);
|
|
504
|
+
const configContent = `module.exports = ${JSON.stringify(currentConfig, null, 2)}`;
|
|
505
|
+
fs.writeFileSync(userConfigPath, configContent);
|
|
506
|
+
|
|
507
|
+
logSuccess(`AI configuration "${name}" deleted successfully!`);
|
|
508
|
+
} catch (error) {
|
|
509
|
+
logError("Error loading configuration:", error.message);
|
|
510
|
+
}
|
|
511
|
+
});
|
|
512
|
+
|
|
513
|
+
configCommand
|
|
514
|
+
.command("view [name]")
|
|
515
|
+
.description("View details of the specified AI configuration")
|
|
516
|
+
.action(async (name) => {
|
|
517
|
+
if (!fs.existsSync(userConfigPath)) {
|
|
518
|
+
logError("Configuration file not initialized");
|
|
519
|
+
|
|
520
|
+
const { createConfig } = await inquirer.default.prompt([
|
|
521
|
+
{
|
|
522
|
+
type: "confirm",
|
|
523
|
+
name: "createConfig",
|
|
524
|
+
message: "Would you like to create a configuration file now?",
|
|
525
|
+
default: true,
|
|
526
|
+
},
|
|
527
|
+
]);
|
|
528
|
+
|
|
529
|
+
if (createConfig) {
|
|
530
|
+
// Create new configuration file with empty ai array
|
|
531
|
+
console.log("Creating new configuration file:", userConfigPath);
|
|
532
|
+
const newConfig = getDefaultConfig();
|
|
533
|
+
const configContent = `module.exports = ${JSON.stringify(newConfig, null, 2)}`;
|
|
534
|
+
fs.writeFileSync(userConfigPath, configContent);
|
|
535
|
+
console.log("Configuration file created with empty AI configurations.");
|
|
536
|
+
}
|
|
537
|
+
return;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
try {
|
|
541
|
+
const currentConfig = require(userConfigPath);
|
|
542
|
+
|
|
543
|
+
let aiConfig;
|
|
544
|
+
if (name) {
|
|
545
|
+
// View specified configuration
|
|
546
|
+
aiConfig = currentConfig.ai.find(config => config.name === name);
|
|
547
|
+
if (!aiConfig) {
|
|
548
|
+
logError(`Configuration with name "${name}" not found.`);
|
|
549
|
+
return;
|
|
550
|
+
}
|
|
551
|
+
} else {
|
|
552
|
+
// 检查ai列表是否为空
|
|
553
|
+
if (!currentConfig.ai || !Array.isArray(currentConfig.ai) || currentConfig.ai.length === 0) {
|
|
554
|
+
logError("No AI configurations found.");
|
|
555
|
+
logError("Please use 'ai config add' to add a new AI configuration.");
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
// View current configuration
|
|
559
|
+
const currentName = currentConfig.currentAi;
|
|
560
|
+
if (!currentName || currentName.trim() === "") {
|
|
561
|
+
logError("No current AI configuration set.");
|
|
562
|
+
logError("Please use 'ai config use <name>' to set a current configuration.");
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
// Check if ai array exists and is not empty
|
|
566
|
+
if (!currentConfig.ai || !Array.isArray(currentConfig.ai) || currentConfig.ai.length === 0) {
|
|
567
|
+
logError("No AI configurations found.");
|
|
568
|
+
logError("Please use 'ai config add' to add a new AI configuration.");
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
aiConfig = currentConfig.ai.find(config => config.name === currentName);
|
|
572
|
+
if (!aiConfig) {
|
|
573
|
+
logError(`Current AI configuration "${currentName}" not found.`);
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
console.log("AI Configuration Details");
|
|
579
|
+
console.log("=".repeat(50));
|
|
580
|
+
console.log(`Name: ${aiConfig.name}`);
|
|
581
|
+
console.log(`Type: ${aiConfig.type}`);
|
|
582
|
+
console.log(`API Base URL: ${aiConfig.baseUrl}`);
|
|
583
|
+
console.log(`Model: ${aiConfig.model}`);
|
|
584
|
+
if (aiConfig.apiKey) {
|
|
585
|
+
console.log(`API Key: ${aiConfig.apiKey.substring(0, 8)}...`);
|
|
586
|
+
}
|
|
587
|
+
console.log(`Temperature: ${aiConfig.temperature}`);
|
|
588
|
+
console.log(`Max Tokens: ${aiConfig.maxTokens}`);
|
|
589
|
+
console.log(`Streaming Output: ${aiConfig.stream ? 'Enabled' : 'Disabled'}`);
|
|
590
|
+
console.log(`Is Current: ${currentConfig.currentAi === aiConfig.name ? 'Yes' : 'No'}`);
|
|
591
|
+
console.log(`File Path: ${userConfigPath}`);
|
|
592
|
+
console.log("=".repeat(50));
|
|
593
|
+
} catch (error) {
|
|
594
|
+
logError("Error loading configuration:", error.message);
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
|
|
598
|
+
|
|
599
|
+
|
|
600
|
+
async function main() {
|
|
601
|
+
try {
|
|
602
|
+
if (program.args && (program.args[0] === "config" || program.args[0] === "ext")) {
|
|
603
|
+
return;
|
|
604
|
+
}
|
|
605
|
+
const options = program.opts();
|
|
606
|
+
let prompt;
|
|
607
|
+
|
|
608
|
+
if (program.prompt) {
|
|
609
|
+
prompt = program.prompt;
|
|
610
|
+
} else if (options.prompt) {
|
|
611
|
+
prompt = options.prompt;
|
|
612
|
+
} else if (!program.args || program.args.length === 0) {
|
|
613
|
+
options.interactive = true;
|
|
614
|
+
} else {
|
|
615
|
+
prompt = program.args.join(" ");
|
|
616
|
+
}
|
|
617
|
+
if (!fs.existsSync(userConfigPath)) {
|
|
618
|
+
await handleMissingConfig();
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
const config = require(userConfigPath);
|
|
622
|
+
// 判断当前列表是否为空
|
|
623
|
+
if (!config.ai || !Array.isArray(config.ai) || config.ai.length === 0) {
|
|
624
|
+
logError("No AI configurations found.");
|
|
625
|
+
logError("Please use 'ai config add' to add a new AI configuration.");
|
|
626
|
+
return;
|
|
627
|
+
}
|
|
628
|
+
// 判断当前是否有设置当前配置
|
|
629
|
+
if (!config.currentAi || config.currentAi.trim() === "") {
|
|
630
|
+
logError("No current AI configuration set.");
|
|
631
|
+
logError("Please use 'ai config use <name>' to set a current configuration.");
|
|
632
|
+
return;
|
|
633
|
+
}
|
|
634
|
+
const cli = new AICLI(config);
|
|
635
|
+
if (options.interactive) {
|
|
636
|
+
cli.startInteractive();
|
|
637
|
+
return;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
if (prompt) {
|
|
641
|
+
cli.run(prompt);
|
|
642
|
+
}
|
|
643
|
+
} catch (error) {
|
|
644
|
+
logError(error.stack);
|
|
645
|
+
}
|
|
646
|
+
}
|
|
647
|
+
|
|
648
|
+
program.parse(process.argv);
|
|
649
|
+
|
|
650
|
+
main();
|