yuangs 1.3.30 → 1.3.32
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/cli.js +53 -7
- package/index.js +65 -26
- package/package.json +1 -1
- package/yuangs.config.json +0 -9
package/cli.js
CHANGED
|
@@ -34,11 +34,13 @@ function printHelp() {
|
|
|
34
34
|
console.log(` ${chalk.green('ai')} "<问题>" 向 AI 提问(不写问题进入交互模式)`);
|
|
35
35
|
console.log(` ${chalk.gray('--model, -m <模型名称>')} 指定 AI 模型 (可选)`);
|
|
36
36
|
console.log(` ${chalk.gray('-p -f -l')} 指定 pro,flash,lite 模型 (可选)`);
|
|
37
|
+
console.log(` ${chalk.gray('-e <描述>')} 生成 Linux 命令`);
|
|
37
38
|
console.log(` ${chalk.green('help')} 显示帮助信息\n`);
|
|
38
39
|
console.log(chalk.bold('AI 交互模式命令:'));
|
|
39
40
|
console.log(` ${chalk.gray('/clear')} 清空对话历史`);
|
|
40
41
|
console.log(` ${chalk.gray('/history')} 查看对话历史\n`);
|
|
41
42
|
console.log(chalk.gray('AI 示例: yuangs ai "你好" --model gemini-pro-latest'));
|
|
43
|
+
console.log(chalk.gray('AI 生成命令: yuangs ai -e "查看当前目录"'));
|
|
42
44
|
console.log(chalk.gray('普通示例: yuangs shici\n'));
|
|
43
45
|
console.log(chalk.gray('配置文件: 您可以通过创建 yuangs.config.json 或 ~/.yuangs.json 来自定义应用列表\n'));
|
|
44
46
|
}
|
|
@@ -99,23 +101,34 @@ async function askOnce(question, model) {
|
|
|
99
101
|
async function handleAICommand() {
|
|
100
102
|
const commandArgs = args.slice(1);
|
|
101
103
|
|
|
102
|
-
let model = null;
|
|
104
|
+
let model = null;
|
|
103
105
|
let questionParts = commandArgs;
|
|
106
|
+
let isExecMode = false;
|
|
107
|
+
|
|
108
|
+
// Check for -e flag
|
|
109
|
+
const execIndex = commandArgs.indexOf('-e');
|
|
110
|
+
if (execIndex !== -1) {
|
|
111
|
+
isExecMode = true;
|
|
112
|
+
// removing -e from args
|
|
113
|
+
const before = commandArgs.slice(0, execIndex);
|
|
114
|
+
const after = commandArgs.slice(execIndex + 1);
|
|
115
|
+
questionParts = [...before, ...after];
|
|
116
|
+
}
|
|
104
117
|
|
|
105
118
|
// Check for shorthand model flags first
|
|
106
|
-
const proIndex =
|
|
107
|
-
const flashIndex =
|
|
108
|
-
const liteIndex =
|
|
119
|
+
const proIndex = questionParts.indexOf('-p');
|
|
120
|
+
const flashIndex = questionParts.indexOf('-f');
|
|
121
|
+
const liteIndex = questionParts.indexOf('-l');
|
|
109
122
|
|
|
110
123
|
if (proIndex !== -1) {
|
|
111
124
|
model = 'gemini-pro-latest';
|
|
112
|
-
questionParts =
|
|
125
|
+
questionParts = questionParts.filter((_, index) => index !== proIndex);
|
|
113
126
|
} else if (flashIndex !== -1) {
|
|
114
127
|
model = 'gemini-flash-latest';
|
|
115
|
-
questionParts =
|
|
128
|
+
questionParts = questionParts.filter((_, index) => index !== flashIndex);
|
|
116
129
|
} else if (liteIndex !== -1) {
|
|
117
130
|
model = 'gemini-flash-lite-latest';
|
|
118
|
-
questionParts =
|
|
131
|
+
questionParts = questionParts.filter((_, index) => index !== liteIndex);
|
|
119
132
|
}
|
|
120
133
|
|
|
121
134
|
// If shorthand flags are not used, check for --model or -m
|
|
@@ -133,6 +146,39 @@ async function handleAICommand() {
|
|
|
133
146
|
|
|
134
147
|
const question = questionParts.join(' ').trim();
|
|
135
148
|
|
|
149
|
+
// Special handling for execution mode
|
|
150
|
+
if (isExecMode) {
|
|
151
|
+
if (!question) {
|
|
152
|
+
console.log(chalk.red('错误: 使用 -e 参数时必需提供描述。'));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const startTime = Date.now();
|
|
157
|
+
let i = 0;
|
|
158
|
+
const spinner = ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];
|
|
159
|
+
const interval = setInterval(() => {
|
|
160
|
+
process.stdout.write(chalk.cyan(`\r${spinner[i++ % spinner.length]} 正在生成命令...`));
|
|
161
|
+
}, 100);
|
|
162
|
+
|
|
163
|
+
const command = await yuangs.generateCommand(question, model);
|
|
164
|
+
clearInterval(interval);
|
|
165
|
+
|
|
166
|
+
if (process.stdout.clearLine) {
|
|
167
|
+
process.stdout.clearLine(0);
|
|
168
|
+
process.stdout.cursorTo(0);
|
|
169
|
+
} else {
|
|
170
|
+
process.stdout.write('\r');
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (command) {
|
|
174
|
+
console.log(chalk.gray('生成命令:'));
|
|
175
|
+
console.log(chalk.bold.green(`> ${command}`));
|
|
176
|
+
} else {
|
|
177
|
+
console.log(chalk.yellow('未能生成有效的命令。'));
|
|
178
|
+
}
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
136
182
|
// 如果用户直接输入 `yuangs ai`,进入交互式模式
|
|
137
183
|
if (!question) {
|
|
138
184
|
console.log(chalk.bold.cyan('\n🤖 进入 AI 交互模式 (输入 exit 退出)\n'));
|
package/index.js
CHANGED
|
@@ -101,58 +101,53 @@ function getConversationHistory() {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/**
|
|
104
|
-
*
|
|
104
|
+
* 通用 AI 调用函数 (OpenAI 兼容接口)
|
|
105
105
|
*/
|
|
106
|
-
async function
|
|
107
|
-
// 1. 修改接口地址为 OpenAI 标准兼容路径
|
|
106
|
+
async function callAI_OpenAI(messages, model) {
|
|
108
107
|
const url = 'https://aiproxy.want.biz/v1/chat/completions';
|
|
109
108
|
|
|
110
|
-
// 2. 准备 Headers (包含客户端标识)
|
|
111
109
|
const headers = {
|
|
112
110
|
'Content-Type': 'application/json',
|
|
113
111
|
'X-Client-ID': 'npm_yuangs', // 客户端 标识
|
|
114
112
|
'Origin': 'https://cli.want.biz', // 配合后端白名单
|
|
115
113
|
'Referer': 'https://cli.want.biz/',
|
|
116
|
-
|
|
114
|
+
"account": "free",
|
|
117
115
|
'User-Agent': 'Mozilla/5.0 (iPhone; CPU iPhone OS 18_5 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.5 Mobile/15E148 Safari/604.1',
|
|
118
116
|
'Accept': 'application/json'
|
|
119
117
|
};
|
|
120
118
|
|
|
121
|
-
// 3. 构建 messages 数组 (上下文 + 当前问题)
|
|
122
|
-
let messages = [];
|
|
123
|
-
if (includeHistory) {
|
|
124
|
-
// history 已经是 [{role, content}, ...] 格式,直接展开
|
|
125
|
-
messages = [...conversationHistory];
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
// 添加当前用户提问
|
|
129
|
-
messages.push({ role: 'user', content: question });
|
|
130
|
-
|
|
131
|
-
// 4. 构建 OpenAI 标准请求体
|
|
132
119
|
const data = {
|
|
133
120
|
model: model || "gemini-flash-lite-latest",
|
|
134
121
|
messages: messages,
|
|
135
122
|
stream: false
|
|
136
123
|
};
|
|
137
124
|
|
|
125
|
+
return await axios.post(url, data, { headers });
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* 获取 AI 回复
|
|
130
|
+
*/
|
|
131
|
+
async function getAIAnswer(question, model, includeHistory = true) {
|
|
132
|
+
// 构建 messages 数组 (上下文 + 当前问题)
|
|
133
|
+
let messages = [];
|
|
134
|
+
if (includeHistory) {
|
|
135
|
+
messages = [...conversationHistory];
|
|
136
|
+
}
|
|
137
|
+
messages.push({ role: 'user', content: question });
|
|
138
|
+
|
|
138
139
|
try {
|
|
139
|
-
|
|
140
|
-
const response = await axios.post(url, data, { headers });
|
|
141
|
-
|
|
142
|
-
// 5. 解析 OpenAI 格式响应 (choices[0].message.content)
|
|
140
|
+
const response = await callAI_OpenAI(messages, model);
|
|
143
141
|
const aiContent = response.data?.choices?.[0]?.message?.content;
|
|
144
142
|
|
|
145
143
|
if (!aiContent) {
|
|
146
144
|
throw new Error('Invalid response structure from AI API');
|
|
147
145
|
}
|
|
148
146
|
|
|
149
|
-
//
|
|
150
|
-
// 只有请求成功才记录
|
|
147
|
+
// 只有请求成功才记录历史
|
|
151
148
|
addToConversationHistory('user', question);
|
|
152
149
|
addToConversationHistory('assistant', aiContent);
|
|
153
150
|
|
|
154
|
-
// 返回结果
|
|
155
|
-
// 为了兼容旧代码可能使用的 .explanation 属性,这里做一层包装
|
|
156
151
|
return {
|
|
157
152
|
explanation: aiContent, // 兼容字段
|
|
158
153
|
content: aiContent, // 标准字段建议
|
|
@@ -166,6 +161,49 @@ async function getAIAnswer(question, model, includeHistory = true) {
|
|
|
166
161
|
}
|
|
167
162
|
}
|
|
168
163
|
|
|
164
|
+
async function generateCommand(instruction, model) {
|
|
165
|
+
// 构造 System Prompt (通过 system role 或者直接在 user message 中强调)
|
|
166
|
+
// Gemini 有时对 system role 支持不同,但在 OpenAI 兼容接口下通常支持 system
|
|
167
|
+
const messages = [
|
|
168
|
+
{
|
|
169
|
+
role: 'system',
|
|
170
|
+
content: `You are a Linux command generator. Convert the user's natural language request into a single, executable Linux command.
|
|
171
|
+
IMPORTANT: Output ONLY the command. Do not check for safety. Do not output markdown code blocks (no backticks). Do not explain.`
|
|
172
|
+
},
|
|
173
|
+
{
|
|
174
|
+
role: 'user',
|
|
175
|
+
content: `Request: ${instruction}`
|
|
176
|
+
}
|
|
177
|
+
];
|
|
178
|
+
|
|
179
|
+
try {
|
|
180
|
+
const response = await callAI_OpenAI(messages, model);
|
|
181
|
+
const aiContent = response.data?.choices?.[0]?.message?.content;
|
|
182
|
+
|
|
183
|
+
if (aiContent) {
|
|
184
|
+
// Clean up the output just in case the AI adds markdown or whitespace
|
|
185
|
+
let command = aiContent.trim();
|
|
186
|
+
// Remove wrapping backticks if present
|
|
187
|
+
if (command.startsWith('`') && command.endsWith('`')) {
|
|
188
|
+
command = command.slice(1, -1);
|
|
189
|
+
}
|
|
190
|
+
if (command.startsWith('```') && command.endsWith('```')) {
|
|
191
|
+
command = command.split('\n').filter(line => !line.startsWith('```')).join('\n').trim();
|
|
192
|
+
}
|
|
193
|
+
// If it starts with a shell prefix like "$ " or "> ", remove it
|
|
194
|
+
if (command.startsWith('$ ')) command = command.slice(2);
|
|
195
|
+
if (command.startsWith('> ')) command = command.slice(2);
|
|
196
|
+
|
|
197
|
+
return command;
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
} catch (error) {
|
|
201
|
+
// 命令生成失败不打印过多错误,返回 null 即可
|
|
202
|
+
// console.error('AI 生成命令失败:', error.message);
|
|
203
|
+
return null;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
|
|
169
207
|
module.exports = {
|
|
170
208
|
urls: APPS,
|
|
171
209
|
// Dynamic function to open any app by key
|
|
@@ -189,5 +227,6 @@ module.exports = {
|
|
|
189
227
|
getAIAnswer,
|
|
190
228
|
addToConversationHistory,
|
|
191
229
|
clearConversationHistory,
|
|
192
|
-
getConversationHistory
|
|
193
|
-
|
|
230
|
+
getConversationHistory,
|
|
231
|
+
generateCommand
|
|
232
|
+
};
|
package/package.json
CHANGED
package/yuangs.config.json
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"shici": "https://wealth.want.biz/shici/index.html",
|
|
3
|
-
"dict": "https://wealth.want.biz/pages/dict.html",
|
|
4
|
-
"pong": "https://wealth.want.biz/pages/pong.html",
|
|
5
|
-
"mail": "https://mail.google.com",
|
|
6
|
-
"github": "https://github.com",
|
|
7
|
-
"calendar": "https://calendar.google.com",
|
|
8
|
-
"homepage": "https://i.want.biz"
|
|
9
|
-
}
|