yuangs 1.3.23 → 1.3.29
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 +54 -0
- package/cli.js +33 -4
- package/index.js +120 -33
- package/package.json +4 -3
- package/test/index.test.js +10 -0
- package/yuangs.config.example.json +8 -0
- package/yuangs.config.example.yaml +18 -0
- package/yuangs.config.json +9 -0
package/README.md
CHANGED
|
@@ -63,6 +63,60 @@ yuangs ai
|
|
|
63
63
|
|
|
64
64
|
Pong 游戏: https://wealth.want.biz/pages/pong.html
|
|
65
65
|
|
|
66
|
+
## 配置自定义应用 (v1.3.24)
|
|
67
|
+
|
|
68
|
+
从 v1.3.24 版本开始,您可以自定义应用列表,而无需修改源代码。
|
|
69
|
+
|
|
70
|
+
### 配置文件格式
|
|
71
|
+
|
|
72
|
+
创建一个 JSON 或 YAML 配置文件,支持以下格式之一:
|
|
73
|
+
|
|
74
|
+
1. `yuangs.config.json` - 项目级别的 JSON 配置文件
|
|
75
|
+
2. `.yuangs.json` - 项目级或用户主目录的 JSON 隐藏配置文件
|
|
76
|
+
3. `yuangs.config.yaml` - 项目级别的 YAML 配置文件
|
|
77
|
+
4. `yuangs.config.yml` - 项目级别的 YAML 配置文件
|
|
78
|
+
5. `.yuangs.yaml` - 项目级或用户主目录的 YAML 隐藏配置文件
|
|
79
|
+
6. `.yuangs.yml` - 项目级或用户主目录的 YAML 隐藏配置文件
|
|
80
|
+
7. `~/.yuangs.json` - 全局用户 JSON 配置文件
|
|
81
|
+
8. `~/.yuangs.yaml` - 全局用户 YAML 配置文件
|
|
82
|
+
9. `~/.yuangs.yml` - 全局用户 YAML 配置文件
|
|
83
|
+
|
|
84
|
+
配置文件示例:
|
|
85
|
+
|
|
86
|
+
```json
|
|
87
|
+
{
|
|
88
|
+
"shici": "https://wealth.want.biz/shici/index.html",
|
|
89
|
+
"dict": "https://wealth.want.biz/pages/dict.html",
|
|
90
|
+
"pong": "https://wealth.want.biz/pages/pong.html",
|
|
91
|
+
"github": "https://github.com",
|
|
92
|
+
"calendar": "https://calendar.google.com",
|
|
93
|
+
"mail": "https://mail.google.com"
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 配置文件优先级
|
|
98
|
+
|
|
99
|
+
配置文件按以下优先级顺序查找(高到低):
|
|
100
|
+
|
|
101
|
+
1. 当前工作目录下的 `yuangs.config.json`
|
|
102
|
+
2. 当前工作目录下的 `.yuangs.json`
|
|
103
|
+
3. 用户主目录下的 `.yuangs.json`
|
|
104
|
+
4. 项目目录下的 `yuangs.config.json`
|
|
105
|
+
5. 项目目录下的 `.yuangs.json`
|
|
106
|
+
6. 如果没有配置文件,则使用默认应用列表
|
|
107
|
+
|
|
108
|
+
### 使用自定义应用
|
|
109
|
+
|
|
110
|
+
创建配置文件后,您可以使用任何定义的应用名称作为命令:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
yuangs github # 打开 GitHub
|
|
114
|
+
yuangs calendar # 打开日历
|
|
115
|
+
yuangs mail # 打开邮箱
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
使用 `yuangs list` 命令查看当前加载的所有应用。
|
|
119
|
+
|
|
66
120
|
## 近期主要更新日志
|
|
67
121
|
|
|
68
122
|
### v1.3.22 (2025-11-30)
|
package/cli.js
CHANGED
|
@@ -13,10 +13,24 @@ function printHelp() {
|
|
|
13
13
|
console.log(chalk.gray('仓库地址: https://www.npmjs.com/package/yuangs?activeTab=readme\n'));
|
|
14
14
|
console.log(chalk.white('使用方法:') + chalk.gray(' yuangs <命令> [参数]\n'));
|
|
15
15
|
console.log(chalk.bold('命令列表:'));
|
|
16
|
+
|
|
17
|
+
// Show default commands
|
|
16
18
|
console.log(` ${chalk.green('shici')} 打开古诗词 PWA`);
|
|
17
19
|
console.log(` ${chalk.green('dict')} 打开英语词典`);
|
|
18
20
|
console.log(` ${chalk.green('pong')} 打开 Pong 游戏`);
|
|
19
21
|
console.log(` ${chalk.green('list')} 列出所有应用链接`);
|
|
22
|
+
|
|
23
|
+
// Show dynamically configured apps
|
|
24
|
+
const dynamicApps = Object.keys(yuangs.urls).filter(key =>
|
|
25
|
+
!['shici', 'dict', 'pong'].includes(key)
|
|
26
|
+
);
|
|
27
|
+
if (dynamicApps.length > 0) {
|
|
28
|
+
console.log(chalk.bold('\n自定义应用:'));
|
|
29
|
+
dynamicApps.forEach(app => {
|
|
30
|
+
console.log(` ${chalk.green(app)} 打开 ${app} 应用`);
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
20
34
|
console.log(` ${chalk.green('ai')} "<问题>" 向 AI 提问(不写问题进入交互模式)`);
|
|
21
35
|
console.log(` ${chalk.gray('--model, -m <模型名称>')} 指定 AI 模型 (可选)`);
|
|
22
36
|
console.log(` ${chalk.gray('-p -f -l')} 指定 pro,flash,lite 模型 (可选)`);
|
|
@@ -26,6 +40,7 @@ function printHelp() {
|
|
|
26
40
|
console.log(` ${chalk.gray('/history')} 查看对话历史\n`);
|
|
27
41
|
console.log(chalk.gray('AI 示例: yuangs ai "你好" --model gemini-pro-latest'));
|
|
28
42
|
console.log(chalk.gray('普通示例: yuangs shici\n'));
|
|
43
|
+
console.log(chalk.gray('配置文件: 您可以通过创建 yuangs.config.json 或 ~/.yuangs.json 来自定义应用列表\n'));
|
|
29
44
|
}
|
|
30
45
|
|
|
31
46
|
function printSuccess(app, url) {
|
|
@@ -185,17 +200,20 @@ async function handleAICommand() {
|
|
|
185
200
|
await askOnce(question, model);
|
|
186
201
|
}
|
|
187
202
|
|
|
203
|
+
// Check if the command matches one of the configured apps
|
|
204
|
+
const isAppCommand = Object.keys(yuangs.urls).includes(command);
|
|
205
|
+
|
|
188
206
|
switch (command) {
|
|
189
207
|
case 'shici':
|
|
190
|
-
printSuccess('古诗词应用', yuangs.urls.shici);
|
|
208
|
+
printSuccess('古诗词应用', yuangs.urls.shici || 'N/A');
|
|
191
209
|
yuangs.openShici();
|
|
192
210
|
break;
|
|
193
211
|
case 'dict':
|
|
194
|
-
printSuccess('英语词典', yuangs.urls.dict);
|
|
212
|
+
printSuccess('英语词典', yuangs.urls.dict || 'N/A');
|
|
195
213
|
yuangs.openDict();
|
|
196
214
|
break;
|
|
197
215
|
case 'pong':
|
|
198
|
-
printSuccess('Pong 游戏', yuangs.urls.pong);
|
|
216
|
+
printSuccess('Pong 游戏', yuangs.urls.pong || 'N/A');
|
|
199
217
|
yuangs.openPong();
|
|
200
218
|
break;
|
|
201
219
|
case 'list':
|
|
@@ -212,7 +230,18 @@ switch (command) {
|
|
|
212
230
|
case 'help':
|
|
213
231
|
case '--help':
|
|
214
232
|
case '-h':
|
|
215
|
-
default:
|
|
216
233
|
printHelp();
|
|
217
234
|
break;
|
|
235
|
+
default:
|
|
236
|
+
// If it's an app command but not one of the named ones, handle it with the dynamic function
|
|
237
|
+
if (isAppCommand) {
|
|
238
|
+
printSuccess(command, yuangs.urls[command]);
|
|
239
|
+
yuangs.openApp(command);
|
|
240
|
+
} else if (command) {
|
|
241
|
+
console.log(chalk.red(`\n错误: 未知命令 '${command}'\n`));
|
|
242
|
+
printHelp();
|
|
243
|
+
} else {
|
|
244
|
+
printHelp();
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
218
247
|
}
|
package/index.js
CHANGED
|
@@ -1,15 +1,75 @@
|
|
|
1
1
|
const { exec } = require('child_process');
|
|
2
2
|
const axios = require('axios');
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
3
5
|
|
|
4
6
|
// Store conversation history
|
|
7
|
+
// 存储结构标准为: [{ role: 'user', content: '...' }, { role: 'assistant', content: '...' }]
|
|
5
8
|
let conversationHistory = [];
|
|
6
9
|
|
|
7
|
-
|
|
10
|
+
// Default apps (fallback if no config file exists)
|
|
11
|
+
const DEFAULT_APPS = {
|
|
8
12
|
shici: 'https://wealth.want.biz/shici/index.html',
|
|
9
13
|
dict: 'https://wealth.want.biz/pages/dict.html',
|
|
10
14
|
pong: 'https://wealth.want.biz/pages/pong.html'
|
|
11
15
|
};
|
|
12
16
|
|
|
17
|
+
// Load apps from configuration file
|
|
18
|
+
function loadAppsConfig() {
|
|
19
|
+
// Define possible config file locations (JSON and YAML)
|
|
20
|
+
const configPaths = [
|
|
21
|
+
path.join(process.cwd(), 'yuangs.config.json'), // Current working directory
|
|
22
|
+
path.join(process.cwd(), '.yuangs.json'), // Current working directory dot file
|
|
23
|
+
path.join(process.cwd(), 'yuangs.config.yaml'), // Current working directory YAML
|
|
24
|
+
path.join(process.cwd(), 'yuangs.config.yml'), // Current working directory YAML
|
|
25
|
+
path.join(process.cwd(), '.yuangs.yaml'), // Current working directory dot YAML
|
|
26
|
+
path.join(process.cwd(), '.yuangs.yml'), // Current working directory dot YAML
|
|
27
|
+
path.join(require('os').homedir(), '.yuangs.json'), // User home directory
|
|
28
|
+
path.join(require('os').homedir(), '.yuangs.yaml'), // User home directory YAML
|
|
29
|
+
path.join(require('os').homedir(), '.yuangs.yml'), // User home directory YAML
|
|
30
|
+
path.join(__dirname, 'yuangs.config.json'), // Project directory
|
|
31
|
+
path.join(__dirname, '.yuangs.json'), // Project directory dot file
|
|
32
|
+
path.join(__dirname, 'yuangs.config.yaml'), // Project directory YAML
|
|
33
|
+
path.join(__dirname, 'yuangs.config.yml') // Project directory YAML
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
for (const configPath of configPaths) {
|
|
37
|
+
if (fs.existsSync(configPath)) {
|
|
38
|
+
try {
|
|
39
|
+
const configContent = fs.readFileSync(configPath, 'utf8');
|
|
40
|
+
|
|
41
|
+
// Determine if it's JSON or YAML based on file extension
|
|
42
|
+
let config;
|
|
43
|
+
if (configPath.endsWith('.json')) {
|
|
44
|
+
config = JSON.parse(configContent);
|
|
45
|
+
} else {
|
|
46
|
+
// For YAML files, we need to require the yaml parser
|
|
47
|
+
let yaml;
|
|
48
|
+
try {
|
|
49
|
+
yaml = require('js-yaml');
|
|
50
|
+
} catch (yamlError) {
|
|
51
|
+
console.warn(`Warning: js-yaml not installed, skipping YAML file ${configPath}`);
|
|
52
|
+
console.warn('Install js-yaml with: npm install js-yaml');
|
|
53
|
+
continue; // Skip this file and try the next config file
|
|
54
|
+
}
|
|
55
|
+
config = yaml.load(configContent);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// If config has an 'apps' property, use it, otherwise use the whole config as apps
|
|
59
|
+
return config.apps || config;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
console.warn(`Warning: Could not parse config file at ${configPath}:`, error.message);
|
|
62
|
+
// Continue to next config file
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// If no config file is found, use default apps
|
|
68
|
+
return DEFAULT_APPS;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
const APPS = loadAppsConfig();
|
|
72
|
+
|
|
13
73
|
function openUrl(url) {
|
|
14
74
|
let command;
|
|
15
75
|
switch (process.platform) {
|
|
@@ -40,60 +100,87 @@ function getConversationHistory() {
|
|
|
40
100
|
return conversationHistory;
|
|
41
101
|
}
|
|
42
102
|
|
|
103
|
+
/**
|
|
104
|
+
* 获取 AI 回复 (OpenAI 兼容接口版)
|
|
105
|
+
*/
|
|
43
106
|
async function getAIAnswer(question, model, includeHistory = true) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
// Prepare the prompt with conversation history if enabled
|
|
47
|
-
let prompt;
|
|
48
|
-
if (includeHistory) {
|
|
49
|
-
prompt = JSON.stringify({
|
|
50
|
-
history: conversationHistory,
|
|
51
|
-
query: question
|
|
52
|
-
}, null, 2);
|
|
53
|
-
} else {
|
|
54
|
-
// If not including history, still use JSON format for consistency but with empty history
|
|
55
|
-
prompt = JSON.stringify({
|
|
56
|
-
history: [],
|
|
57
|
-
query: question
|
|
58
|
-
}, null, 2);
|
|
59
|
-
}
|
|
107
|
+
// 1. 修改接口地址为 OpenAI 标准兼容路径
|
|
108
|
+
const url = 'https://aiproxy.want.biz/v1/chat/completions';
|
|
60
109
|
|
|
110
|
+
// 2. 准备 Headers (包含客户端标识)
|
|
61
111
|
const headers = {
|
|
112
|
+
'Content-Type': 'application/json',
|
|
113
|
+
'X-Client-ID': 'npm_yuangs', // 客户端 标识
|
|
114
|
+
'Origin': 'https://cli.want.biz', // 配合后端白名单
|
|
62
115
|
'Referer': 'https://cli.want.biz/',
|
|
63
116
|
'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',
|
|
64
|
-
'Accept': 'application/json'
|
|
65
|
-
'Content-Type': 'application/json'
|
|
117
|
+
'Accept': 'application/json'
|
|
66
118
|
};
|
|
67
119
|
|
|
120
|
+
// 3. 构建 messages 数组 (上下文 + 当前问题)
|
|
121
|
+
let messages = [];
|
|
122
|
+
if (includeHistory) {
|
|
123
|
+
// history 已经是 [{role, content}, ...] 格式,直接展开
|
|
124
|
+
messages = [...conversationHistory];
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// 添加当前用户提问
|
|
128
|
+
messages.push({ role: 'user', content: question });
|
|
129
|
+
|
|
130
|
+
// 4. 构建 OpenAI 标准请求体
|
|
68
131
|
const data = {
|
|
69
|
-
|
|
70
|
-
|
|
132
|
+
model: model || "gemini-flash-lite-latest",
|
|
133
|
+
messages: messages,
|
|
134
|
+
stream: false
|
|
71
135
|
};
|
|
72
136
|
|
|
73
137
|
try {
|
|
138
|
+
// 发送请求
|
|
74
139
|
const response = await axios.post(url, data, { headers });
|
|
75
|
-
|
|
140
|
+
|
|
141
|
+
// 5. 解析 OpenAI 格式响应 (choices[0].message.content)
|
|
142
|
+
const aiContent = response.data?.choices?.[0]?.message?.content;
|
|
143
|
+
|
|
144
|
+
if (!aiContent) {
|
|
145
|
+
throw new Error('Invalid response structure from AI API');
|
|
146
|
+
}
|
|
76
147
|
|
|
77
|
-
//
|
|
148
|
+
// 6. 更新历史记录
|
|
149
|
+
// 只有请求成功才记录
|
|
78
150
|
addToConversationHistory('user', question);
|
|
151
|
+
addToConversationHistory('assistant', aiContent);
|
|
79
152
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
153
|
+
// 返回结果
|
|
154
|
+
// 为了兼容旧代码可能使用的 .explanation 属性,这里做一层包装
|
|
155
|
+
return {
|
|
156
|
+
explanation: aiContent, // 兼容字段
|
|
157
|
+
content: aiContent, // 标准字段建议
|
|
158
|
+
raw: response.data // 原始响应
|
|
159
|
+
};
|
|
84
160
|
|
|
85
|
-
return answer;
|
|
86
161
|
} catch (error) {
|
|
87
|
-
|
|
162
|
+
const errorMsg = error.response?.data?.error?.message || error.response?.data?.message || error.message || '未知错误';
|
|
163
|
+
console.error('AI 请求失败:', errorMsg);
|
|
88
164
|
return null;
|
|
89
165
|
}
|
|
90
166
|
}
|
|
91
167
|
|
|
92
168
|
module.exports = {
|
|
93
169
|
urls: APPS,
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
170
|
+
// Dynamic function to open any app by key
|
|
171
|
+
openApp: (appKey) => {
|
|
172
|
+
const url = APPS[appKey];
|
|
173
|
+
if (url) {
|
|
174
|
+
openUrl(url);
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
console.error(`App '${appKey}' not found`);
|
|
178
|
+
return false;
|
|
179
|
+
},
|
|
180
|
+
// Specific functions for default apps (for backward compatibility)
|
|
181
|
+
openShici: () => openUrl(APPS.shici || DEFAULT_APPS.shici),
|
|
182
|
+
openDict: () => openUrl(APPS.dict || DEFAULT_APPS.dict),
|
|
183
|
+
openPong: () => openUrl(APPS.pong || DEFAULT_APPS.pong),
|
|
97
184
|
listApps: () => {
|
|
98
185
|
console.log('--- YGS Apps ---');
|
|
99
186
|
Object.entries(APPS).forEach(([key, url]) => console.log(`${key}: ${url}`));
|
|
@@ -102,4 +189,4 @@ module.exports = {
|
|
|
102
189
|
addToConversationHistory,
|
|
103
190
|
clearConversationHistory,
|
|
104
191
|
getConversationHistory
|
|
105
|
-
};
|
|
192
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "yuangs",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.29",
|
|
4
4
|
"description": "苑广山的个人应用集合 CLI(彩色版)",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
"tools",
|
|
16
16
|
"colorful"
|
|
17
17
|
],
|
|
18
|
-
"homepage": "https://github.com/
|
|
18
|
+
"homepage": "https://github.com/yauangshan/npm_yuangs#readme",
|
|
19
19
|
"repository": {
|
|
20
20
|
"type": "git",
|
|
21
21
|
"url": "git+https://github.com/yuanguangshan/npm_yuangs.git"
|
|
@@ -27,7 +27,8 @@
|
|
|
27
27
|
"license": "ISC",
|
|
28
28
|
"dependencies": {
|
|
29
29
|
"axios": "^1.13.2",
|
|
30
|
-
"chalk": "^4.1.2"
|
|
30
|
+
"chalk": "^4.1.2",
|
|
31
|
+
"js-yaml": "^4.1.0"
|
|
31
32
|
},
|
|
32
33
|
"devDependencies": {
|
|
33
34
|
"jest": "^29.7.0"
|
package/test/index.test.js
CHANGED
|
@@ -14,6 +14,16 @@ describe('Module: index.js', () => {
|
|
|
14
14
|
expect(yuangs.urls.shici).toContain('shici/index.html');
|
|
15
15
|
});
|
|
16
16
|
|
|
17
|
+
test('should have openApp function', () => {
|
|
18
|
+
expect(typeof yuangs.openApp).toBe('function');
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
test('should have backward compatibility functions', () => {
|
|
22
|
+
expect(typeof yuangs.openShici).toBe('function');
|
|
23
|
+
expect(typeof yuangs.openDict).toBe('function');
|
|
24
|
+
expect(typeof yuangs.openPong).toBe('function');
|
|
25
|
+
});
|
|
26
|
+
|
|
17
27
|
test('should manage conversation history correctly', () => {
|
|
18
28
|
yuangs.addToConversationHistory('user', 'hello');
|
|
19
29
|
let history = yuangs.getConversationHistory();
|
|
@@ -0,0 +1,8 @@
|
|
|
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
|
+
"github": "https://github.com",
|
|
6
|
+
"calendar": "https://calendar.google.com",
|
|
7
|
+
"mail": "https://mail.google.com"
|
|
8
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# Example configuration file for yuangs CLI
|
|
2
|
+
# Add your custom applications here
|
|
3
|
+
|
|
4
|
+
shici: "https://wealth.want.biz/shici/index.html"
|
|
5
|
+
dict: "https://wealth.want.biz/pages/dict.html"
|
|
6
|
+
pong: "https://wealth.want.biz/pages/pong.html"
|
|
7
|
+
github: "https://github.com"
|
|
8
|
+
calendar: "https://calendar.google.com"
|
|
9
|
+
mail: "https://mail.google.com"
|
|
10
|
+
|
|
11
|
+
# You can also use the apps property if you prefer to group them
|
|
12
|
+
# apps:
|
|
13
|
+
# shici: "https://wealth.want.biz/shici/index.html"
|
|
14
|
+
# dict: "https://wealth.want.biz/pages/dict.html"
|
|
15
|
+
# pong: "https://wealth.want.biz/pages/pong.html"
|
|
16
|
+
# github: "https://github.com"
|
|
17
|
+
# calendar: "https://calendar.google.com"
|
|
18
|
+
# mail: "https://mail.google.com"
|
|
@@ -0,0 +1,9 @@
|
|
|
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
|
+
}
|