@zhin.js/cli 1.0.6 → 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/CHANGELOG.md +14 -0
- package/lib/cli.js +8 -0
- package/lib/cli.js.map +1 -1
- package/lib/commands/install.d.ts +4 -0
- package/lib/commands/install.d.ts.map +1 -0
- package/lib/commands/install.js +209 -0
- package/lib/commands/install.js.map +1 -0
- package/lib/commands/new.d.ts.map +1 -1
- package/lib/commands/new.js +70 -54
- package/lib/commands/new.js.map +1 -1
- package/lib/commands/pub.d.ts +3 -0
- package/lib/commands/pub.d.ts.map +1 -0
- package/lib/commands/pub.js +151 -0
- package/lib/commands/pub.js.map +1 -0
- package/lib/commands/search.d.ts +4 -0
- package/lib/commands/search.d.ts.map +1 -0
- package/lib/commands/search.js +205 -0
- package/lib/commands/search.js.map +1 -0
- package/lib/commands/start.d.ts.map +1 -1
- package/lib/commands/start.js +10 -4
- package/lib/commands/start.js.map +1 -1
- package/lib/utils/process.d.ts +2 -2
- package/lib/utils/process.d.ts.map +1 -1
- package/lib/utils/process.js +4 -10
- package/lib/utils/process.js.map +1 -1
- package/package.json +2 -2
- package/src/cli.ts +8 -0
- package/src/commands/install.ts +242 -0
- package/src/commands/new.ts +77 -60
- package/src/commands/pub.ts +176 -0
- package/src/commands/search.ts +243 -0
- package/src/commands/start.ts +10 -5
- package/src/utils/process.ts +5 -11
- package/tsconfig.json +17 -11
|
@@ -0,0 +1,243 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { logger } from '../utils/logger.js';
|
|
3
|
+
import { execSync } from 'node:child_process';
|
|
4
|
+
|
|
5
|
+
interface SearchOptions {
|
|
6
|
+
category?: string;
|
|
7
|
+
limit?: number;
|
|
8
|
+
official?: boolean;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const searchCommand = new Command('search')
|
|
12
|
+
.description('搜索 Zhin.js 插件')
|
|
13
|
+
.argument('[keyword]', '搜索关键词')
|
|
14
|
+
.option('-c, --category <category>', '按分类搜索 (utility|service|game|adapter|admin|ai)')
|
|
15
|
+
.option('-l, --limit <number>', '限制结果数量', '20')
|
|
16
|
+
.option('--official', '仅显示官方插件', false)
|
|
17
|
+
.action(async (keyword: string, options: SearchOptions) => {
|
|
18
|
+
try {
|
|
19
|
+
logger.info('正在搜索插件...');
|
|
20
|
+
logger.log('');
|
|
21
|
+
|
|
22
|
+
// 构建搜索查询
|
|
23
|
+
let searchQuery = 'zhin';
|
|
24
|
+
|
|
25
|
+
if (options.official) {
|
|
26
|
+
searchQuery = '@zhin.js';
|
|
27
|
+
} else if (keyword) {
|
|
28
|
+
searchQuery = `zhin.js ${keyword} `;
|
|
29
|
+
} else {
|
|
30
|
+
searchQuery = 'zhin.js plugin';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 使用 npm search
|
|
34
|
+
const cmd = `npm search ${searchQuery} --json`;
|
|
35
|
+
|
|
36
|
+
try {
|
|
37
|
+
const output = execSync(cmd, {
|
|
38
|
+
encoding: 'utf-8',
|
|
39
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB
|
|
40
|
+
stdio: ['pipe', 'pipe', 'ignore'] // 忽略 stderr
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const results = JSON.parse(output);
|
|
44
|
+
|
|
45
|
+
// 过滤结果
|
|
46
|
+
let filteredResults = results.filter((pkg: any) => {
|
|
47
|
+
// 必须包含 zhin 关键词
|
|
48
|
+
const keywords = pkg.keywords || [];
|
|
49
|
+
const hasZhin = keywords.includes('zhin') ||
|
|
50
|
+
keywords.includes('plugin') ||
|
|
51
|
+
pkg.name.startsWith('@zhin.js/') ||
|
|
52
|
+
pkg.name.startsWith('zhin.js-');
|
|
53
|
+
|
|
54
|
+
if (!hasZhin) return false;
|
|
55
|
+
|
|
56
|
+
// 按分类过滤
|
|
57
|
+
if (options.category) {
|
|
58
|
+
const category = pkg.keywords?.find((k: string) =>
|
|
59
|
+
k.includes(options.category!)
|
|
60
|
+
);
|
|
61
|
+
if (!category) return false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// 按关键词过滤
|
|
65
|
+
if (keyword) {
|
|
66
|
+
const searchIn = [
|
|
67
|
+
pkg.name,
|
|
68
|
+
pkg.description || '',
|
|
69
|
+
...(pkg.keywords || [])
|
|
70
|
+
].join(' ').toLowerCase();
|
|
71
|
+
|
|
72
|
+
return searchIn.includes(keyword.toLowerCase());
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return true;
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// 限制结果数量
|
|
79
|
+
const limit = parseInt(String(options.limit) || '20');
|
|
80
|
+
if (filteredResults.length > limit) {
|
|
81
|
+
filteredResults = filteredResults.slice(0, limit);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// 显示结果
|
|
85
|
+
if (filteredResults.length === 0) {
|
|
86
|
+
logger.warn('未找到匹配的插件');
|
|
87
|
+
logger.log('');
|
|
88
|
+
logger.log('💡 提示:');
|
|
89
|
+
logger.log(' - 尝试使用不同的关键词');
|
|
90
|
+
logger.log(' - 访问插件市场: https://zhin.pages.dev/plugins');
|
|
91
|
+
logger.log(' - 在 GitHub 搜索: https://github.com/topics/zhin.js');
|
|
92
|
+
return;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
logger.success(`找到 ${filteredResults.length} 个插件:`);
|
|
96
|
+
logger.log('');
|
|
97
|
+
|
|
98
|
+
// 按下载量排序
|
|
99
|
+
filteredResults.sort((a: any, b: any) => {
|
|
100
|
+
const aDownloads = parseInt(a.downloads || '0');
|
|
101
|
+
const bDownloads = parseInt(b.downloads || '0');
|
|
102
|
+
return bDownloads - aDownloads;
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// 显示插件列表
|
|
106
|
+
filteredResults.forEach((pkg: any, index: number) => {
|
|
107
|
+
const name = pkg.name;
|
|
108
|
+
const version = pkg.version;
|
|
109
|
+
const description = pkg.description || '无描述';
|
|
110
|
+
const author = pkg.publisher?.username || '未知';
|
|
111
|
+
const date = pkg.date ? new Date(pkg.date).toLocaleDateString('zh-CN') : '未知';
|
|
112
|
+
|
|
113
|
+
// 判断插件类型
|
|
114
|
+
let badge = '';
|
|
115
|
+
if (name.startsWith('@zhin.js/')) {
|
|
116
|
+
badge = '✨ [官方]';
|
|
117
|
+
} else if (name.startsWith('zhin.js-')) {
|
|
118
|
+
badge = '📦 [社区]';
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
logger.log(`${index + 1}. ${badge} ${name}@${version}`);
|
|
122
|
+
logger.log(` ${description}`);
|
|
123
|
+
logger.log(` 作者: ${author} | 更新: ${date}`);
|
|
124
|
+
|
|
125
|
+
// 显示安装命令
|
|
126
|
+
logger.log(` 安装: zhin install ${name}`);
|
|
127
|
+
logger.log('');
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
logger.log('💡 提示:');
|
|
131
|
+
logger.log(' - 使用 zhin info <package> 查看插件详情');
|
|
132
|
+
logger.log(' - 使用 zhin install <package> 安装插件');
|
|
133
|
+
logger.log(' - 访问 https://zhin.pages.dev/plugins 查看完整列表');
|
|
134
|
+
|
|
135
|
+
} catch (error) {
|
|
136
|
+
logger.error('搜索失败,请检查网络连接');
|
|
137
|
+
logger.log('');
|
|
138
|
+
logger.log('💡 替代方案:');
|
|
139
|
+
logger.log(' - 访问 npm: https://www.npmjs.com/search?q=zhin.js');
|
|
140
|
+
logger.log(' - 访问 GitHub: https://github.com/topics/zhin.js');
|
|
141
|
+
throw error;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
} catch (error: any) {
|
|
145
|
+
logger.error(`搜索插件失败: ${error.message}`);
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
export const infoCommand = new Command('info')
|
|
151
|
+
.description('查看插件详细信息')
|
|
152
|
+
.argument('<package>', '插件包名')
|
|
153
|
+
.action(async (packageName: string) => {
|
|
154
|
+
try {
|
|
155
|
+
logger.info(`正在获取 ${packageName} 的信息...`);
|
|
156
|
+
logger.log('');
|
|
157
|
+
|
|
158
|
+
const cmd = `npm view ${packageName} --json`;
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const output = execSync(cmd, {
|
|
162
|
+
encoding: 'utf-8',
|
|
163
|
+
stdio: ['pipe', 'pipe', 'ignore']
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
const info = JSON.parse(output);
|
|
167
|
+
|
|
168
|
+
// 显示插件信息
|
|
169
|
+
logger.success('插件信息:');
|
|
170
|
+
logger.log('');
|
|
171
|
+
|
|
172
|
+
logger.log(`📦 名称: ${info.name}`);
|
|
173
|
+
logger.log(`📝 版本: ${info.version}`);
|
|
174
|
+
logger.log(`📄 描述: ${info.description || '无'}`);
|
|
175
|
+
logger.log(`👤 作者: ${info.author?.name || info.maintainers?.[0]?.name || '未知'}`);
|
|
176
|
+
logger.log(`📅 发布时间: ${new Date(info.time?.modified || info.time?.created).toLocaleDateString('zh-CN')}`);
|
|
177
|
+
|
|
178
|
+
if (info.keywords?.length > 0) {
|
|
179
|
+
logger.log(`🏷️ 标签: ${info.keywords.join(', ')}`);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
if (info.homepage) {
|
|
183
|
+
logger.log(`🏠 主页: ${info.homepage}`);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (info.repository?.url) {
|
|
187
|
+
logger.log(`📂 仓库: ${info.repository.url.replace(/^git\+/, '').replace(/\.git$/, '')}`);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (info.bugs?.url) {
|
|
191
|
+
logger.log(`🐛 问题: ${info.bugs.url}`);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
if (info.license) {
|
|
195
|
+
logger.log(`⚖️ 许可: ${info.license}`);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// 显示依赖
|
|
199
|
+
if (info.peerDependencies) {
|
|
200
|
+
logger.log('');
|
|
201
|
+
logger.log('🔗 对等依赖:');
|
|
202
|
+
Object.entries(info.peerDependencies).forEach(([dep, ver]) => {
|
|
203
|
+
logger.log(` ${dep}: ${ver}`);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// 显示安装命令
|
|
208
|
+
logger.log('');
|
|
209
|
+
logger.log('📥 安装命令:');
|
|
210
|
+
logger.log(` zhin install ${info.name}`);
|
|
211
|
+
logger.log(` # 或`);
|
|
212
|
+
logger.log(` pnpm add ${info.name}`);
|
|
213
|
+
|
|
214
|
+
// Zhin 特定信息
|
|
215
|
+
if (info.zhin) {
|
|
216
|
+
logger.log('');
|
|
217
|
+
logger.log('🎯 Zhin 信息:');
|
|
218
|
+
if (info.zhin.displayName) {
|
|
219
|
+
logger.log(` 显示名称: ${info.zhin.displayName}`);
|
|
220
|
+
}
|
|
221
|
+
if (info.zhin.category) {
|
|
222
|
+
logger.log(` 分类: ${info.zhin.category}`);
|
|
223
|
+
}
|
|
224
|
+
if (info.zhin.features?.length > 0) {
|
|
225
|
+
logger.log(` 功能: ${info.zhin.features.join(', ')}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
} catch (error) {
|
|
230
|
+
logger.error(`未找到插件: ${packageName}`);
|
|
231
|
+
logger.log('');
|
|
232
|
+
logger.log('💡 提示:');
|
|
233
|
+
logger.log(' - 检查插件名称是否正确');
|
|
234
|
+
logger.log(' - 使用 zhin search 搜索插件');
|
|
235
|
+
throw error;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
} catch (error: any) {
|
|
239
|
+
logger.error(`获取插件信息失败: ${error.message}`);
|
|
240
|
+
process.exit(1);
|
|
241
|
+
}
|
|
242
|
+
});
|
|
243
|
+
|
package/src/commands/start.ts
CHANGED
|
@@ -10,7 +10,7 @@ export const startCommand = new Command('start')
|
|
|
10
10
|
.description('生产模式启动机器人')
|
|
11
11
|
.option('-d, --daemon', '后台运行', false)
|
|
12
12
|
.option('--log-file [file]', '日志文件路径')
|
|
13
|
-
.option('--bun', '使用 bun 运行(默认使用
|
|
13
|
+
.option('--bun', '使用 bun 运行(默认使用 tsx)', false)
|
|
14
14
|
.action(async (options: { daemon: boolean; logFile?: string; bun: boolean }) => {
|
|
15
15
|
try {
|
|
16
16
|
const cwd = process.cwd();
|
|
@@ -27,7 +27,7 @@ export const startCommand = new Command('start')
|
|
|
27
27
|
loadEnvFiles(cwd, 'production');
|
|
28
28
|
|
|
29
29
|
// 检查构建产物
|
|
30
|
-
const distPath = path.join(cwd, '
|
|
30
|
+
const distPath = path.join(cwd, 'lib');
|
|
31
31
|
const sourcePath = path.join(cwd, 'src');
|
|
32
32
|
const sourceFile = path.join(sourcePath, 'index.ts');
|
|
33
33
|
const distFile = path.join(distPath, 'index.js');
|
|
@@ -47,7 +47,6 @@ export const startCommand = new Command('start')
|
|
|
47
47
|
...process.env,
|
|
48
48
|
NODE_ENV: 'production'
|
|
49
49
|
};
|
|
50
|
-
|
|
51
50
|
// 配置stdio
|
|
52
51
|
let stdio: any = 'inherit';
|
|
53
52
|
if (options.daemon) {
|
|
@@ -57,11 +56,17 @@ export const startCommand = new Command('start')
|
|
|
57
56
|
}
|
|
58
57
|
|
|
59
58
|
// 选择运行时
|
|
60
|
-
const runtime = options.bun ? 'bun' : '
|
|
59
|
+
const runtime = options.bun ? 'bun' : 'tsx';
|
|
61
60
|
const args = options.bun ? [entryFile] : ['--expose-gc', entryFile];
|
|
62
61
|
|
|
63
62
|
logger.info(`📦 启动命令: ${runtime} ${args.join(' ')}`);
|
|
64
|
-
return startProcess(runtime, args,
|
|
63
|
+
return startProcess(runtime, args, {
|
|
64
|
+
cwd,
|
|
65
|
+
env,
|
|
66
|
+
stdio,
|
|
67
|
+
shell:true,
|
|
68
|
+
detached: options.daemon,
|
|
69
|
+
});
|
|
65
70
|
};
|
|
66
71
|
|
|
67
72
|
let child = await startBot();
|
package/src/utils/process.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { spawn, ChildProcess, exec } from 'child_process';
|
|
1
|
+
import { spawn, ChildProcess, exec, SpawnOptions } from 'child_process';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { logger } from './logger.js';
|
|
@@ -60,21 +60,15 @@ async function killProcess(pid: number, signal: string = 'SIGTERM'): Promise<boo
|
|
|
60
60
|
});
|
|
61
61
|
}
|
|
62
62
|
|
|
63
|
-
export async function startProcess(command: string, args: string[],
|
|
63
|
+
export async function startProcess(command: string, args: string[], options:SpawnOptions): Promise<ChildProcess> {
|
|
64
64
|
return new Promise((resolve, reject) => {
|
|
65
|
-
const child = spawn(command, args,
|
|
66
|
-
cwd,
|
|
67
|
-
detached: daemon,
|
|
68
|
-
stdio: 'inherit',
|
|
69
|
-
shell:true,
|
|
70
|
-
env: { ...process.env }
|
|
71
|
-
});
|
|
65
|
+
const child = spawn(command, args, options);
|
|
72
66
|
|
|
73
|
-
if(
|
|
67
|
+
if(options.detached) child.unref();
|
|
74
68
|
|
|
75
69
|
child.on('spawn', () => {
|
|
76
70
|
// 保存进程ID
|
|
77
|
-
const pidFile = path.join(cwd, PID_FILE);
|
|
71
|
+
const pidFile = path.join((options.cwd!).toString(), PID_FILE);
|
|
78
72
|
fs.writeFileSync(pidFile, child.pid!.toString());
|
|
79
73
|
logger.success(`机器人已启动,PID: ${child.pid}`);
|
|
80
74
|
resolve(child);
|
package/tsconfig.json
CHANGED
|
@@ -1,23 +1,29 @@
|
|
|
1
1
|
{
|
|
2
2
|
"compilerOptions": {
|
|
3
|
-
"
|
|
3
|
+
"outDir": "./lib",
|
|
4
|
+
"baseUrl": ".",
|
|
5
|
+
"declaration": true,
|
|
4
6
|
"module": "ESNext",
|
|
5
7
|
"moduleResolution": "bundler",
|
|
6
|
-
"
|
|
7
|
-
"
|
|
8
|
+
"target": "ES2022",
|
|
9
|
+
"declarationMap": true,
|
|
10
|
+
"sourceMap": true,
|
|
11
|
+
"skipLibCheck": true,
|
|
12
|
+
"noEmit": false,
|
|
8
13
|
"strict": true,
|
|
9
14
|
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
15
|
"forceConsistentCasingInFileNames": true,
|
|
12
16
|
"resolveJsonModule": true,
|
|
13
17
|
"isolatedModules": true,
|
|
14
18
|
"allowSyntheticDefaultImports": true,
|
|
15
19
|
"experimentalDecorators": true,
|
|
16
|
-
"emitDecoratorMetadata": true
|
|
17
|
-
"declaration": true,
|
|
18
|
-
"declarationMap": true,
|
|
19
|
-
"sourceMap": true
|
|
20
|
+
"emitDecoratorMetadata": true
|
|
20
21
|
},
|
|
21
|
-
"include": [
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
"include": [
|
|
23
|
+
"./src/**/*"
|
|
24
|
+
],
|
|
25
|
+
"exclude": [
|
|
26
|
+
"node_modules",
|
|
27
|
+
"./lib"
|
|
28
|
+
]
|
|
29
|
+
}
|