xinyu-pro 0.21.2 → 0.21.3
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/bin/cli.js +114 -89
- package/package.json +1 -1
- package/version.json +1 -1
package/bin/cli.js
CHANGED
|
@@ -1,20 +1,24 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
const { existsSync, readFileSync, writeFileSync } = require('fs');
|
|
4
|
-
const { join
|
|
3
|
+
const { existsSync, readFileSync, writeFileSync, mkdirSync, readdirSync } = require('fs');
|
|
4
|
+
const { join } = require('path');
|
|
5
5
|
const { createInterface } = require('readline');
|
|
6
6
|
const { execSync, spawn } = require('child_process');
|
|
7
|
+
const { homedir } = require('os');
|
|
7
8
|
|
|
8
9
|
const isWin = process.platform === 'win32';
|
|
9
|
-
const
|
|
10
|
-
const
|
|
10
|
+
const APP_DIR = __dirname.includes('node_modules') ? resolve(__dirname, '..') : process.cwd();
|
|
11
|
+
const XINYU_HOME = join(homedir(), '.xinyu-pro');
|
|
12
|
+
const RUN_DIR = XINYU_HOME;
|
|
11
13
|
|
|
12
14
|
const args = process.argv.slice(2);
|
|
13
15
|
const HELP_FLAG = args.includes('--help') || args.includes('-h');
|
|
14
16
|
const DEV_FLAG = args.includes('--dev');
|
|
17
|
+
const RESET_FLAG = args.includes('--reset');
|
|
15
18
|
const PORT_INDEX = args.indexOf('--port');
|
|
16
19
|
const CUSTOM_PORT = PORT_INDEX !== -1 ? args[PORT_INDEX + 1] : '3000';
|
|
17
|
-
|
|
20
|
+
|
|
21
|
+
const SKIP_DIRS = new Set(['node_modules', '.next', 'data', '.git']);
|
|
18
22
|
|
|
19
23
|
function echo(msg, type = 'info') {
|
|
20
24
|
const colors = {
|
|
@@ -48,10 +52,10 @@ function showHelp() {
|
|
|
48
52
|
echo(' ╚══════════════════════════════════════╝', 'bold');
|
|
49
53
|
echo('');
|
|
50
54
|
echo(' 用法:', 'bold');
|
|
51
|
-
echo(' $ xinyu
|
|
55
|
+
echo(' $ xinyu 启动服务器');
|
|
52
56
|
echo(' $ xinyu --dev 开发模式(热更新)');
|
|
53
57
|
echo(' $ xinyu --port 4000 指定端口');
|
|
54
|
-
echo(' $ xinyu --
|
|
58
|
+
echo(' $ xinyu --reset 重新安装并启动');
|
|
55
59
|
echo(' $ xinyu --help 显示帮助');
|
|
56
60
|
echo('');
|
|
57
61
|
echo(' 环境要求:', 'bold');
|
|
@@ -69,14 +73,82 @@ function checkNodeVersion() {
|
|
|
69
73
|
}
|
|
70
74
|
}
|
|
71
75
|
|
|
72
|
-
function
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
+
function copyRecursive(src, dest) {
|
|
77
|
+
mkdirSync(dest, { recursive: true });
|
|
78
|
+
const entries = readdirSync(src, { withFileTypes: true });
|
|
79
|
+
for (const entry of entries) {
|
|
80
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
81
|
+
if (entry.name.startsWith('.env') && entry.name !== '.env.example') continue;
|
|
82
|
+
const srcPath = join(src, entry.name);
|
|
83
|
+
const destPath = join(dest, entry.name);
|
|
84
|
+
if (entry.isDirectory()) {
|
|
85
|
+
copyRecursive(srcPath, destPath);
|
|
86
|
+
} else {
|
|
87
|
+
if (!existsSync(destPath)) {
|
|
88
|
+
try {
|
|
89
|
+
mkdirSync(join(dest, '..'), { recursive: true });
|
|
90
|
+
writeFileSync(destPath, readFileSync(srcPath));
|
|
91
|
+
} catch { }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function ensureAppSetup() {
|
|
98
|
+
const isFresh = !existsSync(join(RUN_DIR, 'package.json'));
|
|
99
|
+
|
|
100
|
+
if (isFresh || RESET_FLAG) {
|
|
101
|
+
if (RESET_FLAG && existsSync(RUN_DIR)) {
|
|
102
|
+
echo(' ■ 正在重置应用...', 'info');
|
|
103
|
+
try {
|
|
104
|
+
const rmDir = join(RUN_DIR, '..', '.xinyu-pro-backup');
|
|
105
|
+
if (existsSync(rmDir)) {
|
|
106
|
+
execSync(isWin ? `rmdir /s /q "${rmDir}"` : `rm -rf "${rmDir}"`, { shell: isWin });
|
|
107
|
+
}
|
|
108
|
+
execSync(isWin ? `move "${RUN_DIR}" "${rmDir}"` : `mv "${RUN_DIR}" "${rmDir}"`, { shell: isWin });
|
|
109
|
+
} catch { }
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
echo(' ■ 正在初始化应用目录...', 'info');
|
|
113
|
+
echo(' ' + RUN_DIR, 'dim');
|
|
114
|
+
mkdirSync(RUN_DIR, { recursive: true });
|
|
115
|
+
copyRecursive(APP_DIR, RUN_DIR);
|
|
116
|
+
|
|
117
|
+
const envTarget = join(RUN_DIR, '.env.local');
|
|
118
|
+
if (!existsSync(envTarget)) {
|
|
119
|
+
const envExample = join(RUN_DIR, '.env.example');
|
|
120
|
+
if (existsSync(envExample)) {
|
|
121
|
+
writeFileSync(envTarget, readFileSync(envExample, 'utf-8'), 'utf-8');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return false;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function installDependencies() {
|
|
131
|
+
if (!existsSync(join(RUN_DIR, 'node_modules'))) {
|
|
132
|
+
echo(' ■ 正在安装依赖...', 'info');
|
|
133
|
+
execSync('npm install', { cwd: RUN_DIR, stdio: 'inherit', shell: isWin });
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function runBuild() {
|
|
140
|
+
echo(' ■ 正在构建应用(首次需要 1-2 分钟)...', 'info');
|
|
141
|
+
try {
|
|
142
|
+
execSync('npx next build', { cwd: RUN_DIR, stdio: 'inherit', shell: isWin });
|
|
143
|
+
echo(' ✓ 构建完成', 'success');
|
|
144
|
+
} catch {
|
|
145
|
+
echo(' ✗ 构建失败,请检查错误信息', 'error');
|
|
146
|
+
process.exit(1);
|
|
147
|
+
}
|
|
76
148
|
}
|
|
77
149
|
|
|
78
|
-
function
|
|
79
|
-
const binDir = join(
|
|
150
|
+
function getNextBin() {
|
|
151
|
+
const binDir = join(RUN_DIR, 'node_modules', '.bin');
|
|
80
152
|
if (isWin) {
|
|
81
153
|
const cmdPath = join(binDir, 'next.cmd');
|
|
82
154
|
if (existsSync(cmdPath)) return cmdPath;
|
|
@@ -86,106 +158,66 @@ function getNextCommand() {
|
|
|
86
158
|
return 'next';
|
|
87
159
|
}
|
|
88
160
|
|
|
89
|
-
function
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
const pm = detectPackageManager();
|
|
93
|
-
const cmd = pm === 'yarn' ? 'yarn install' : pm === 'pnpm' ? 'pnpm install' : 'npm install';
|
|
94
|
-
echo(` $ ${cmd}`, 'dim');
|
|
95
|
-
execSync(cmd, { cwd: APP_DIR, stdio: 'inherit', shell: isWin });
|
|
96
|
-
return true;
|
|
97
|
-
}
|
|
98
|
-
return false;
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function setupEnvFile() {
|
|
102
|
-
const envPath = join(APP_DIR, '.env.local');
|
|
103
|
-
const examplePath = join(APP_DIR, '.env.example');
|
|
161
|
+
function setupEnvInteractive() {
|
|
162
|
+
const envPath = join(RUN_DIR, '.env.local');
|
|
163
|
+
const examplePath = join(RUN_DIR, '.env.example');
|
|
104
164
|
|
|
105
165
|
if (existsSync(envPath)) {
|
|
106
166
|
const content = readFileSync(envPath, 'utf-8');
|
|
107
167
|
if (content.includes('your-api-key') || content.includes('你的密码')) {
|
|
108
|
-
echo(' ⚠ .env.local
|
|
168
|
+
echo(' ⚠ .env.local 中存在占位值,建议修改', 'warn');
|
|
109
169
|
}
|
|
110
170
|
return;
|
|
111
171
|
}
|
|
112
172
|
|
|
113
|
-
echo(' ⚠ 未检测到 .env.local 配置文件', 'warn');
|
|
114
|
-
echo('');
|
|
115
|
-
|
|
116
173
|
if (!existsSync(examplePath)) {
|
|
117
|
-
echo(' ✗ 未找到 .env.example
|
|
174
|
+
echo(' ✗ 未找到 .env.example', 'error');
|
|
118
175
|
process.exit(1);
|
|
119
176
|
}
|
|
120
177
|
|
|
121
178
|
const example = readFileSync(examplePath, 'utf-8');
|
|
122
179
|
writeFileSync(envPath, example, 'utf-8');
|
|
123
|
-
echo(' ✓
|
|
124
|
-
echo('');
|
|
180
|
+
echo(' ✓ 已创建 .env.local', 'success');
|
|
125
181
|
|
|
126
|
-
const
|
|
127
|
-
echo(' ── 请填写以下必要配置(直接回车可跳过) ──', 'bold');
|
|
182
|
+
const run = async () => {
|
|
128
183
|
echo('');
|
|
129
|
-
|
|
184
|
+
echo(' ── 填写配置(直接回车可跳过) ──', 'bold');
|
|
130
185
|
const apiKey = await ask(' AI API 密钥 (AI_API_KEY): ');
|
|
131
186
|
if (apiKey) {
|
|
132
|
-
let
|
|
133
|
-
|
|
134
|
-
writeFileSync(envPath, content, 'utf-8');
|
|
187
|
+
let c = readFileSync(envPath, 'utf-8');
|
|
188
|
+
writeFileSync(envPath, c.replace(/AI_API_KEY=.*/, `AI_API_KEY=${apiKey}`), 'utf-8');
|
|
135
189
|
}
|
|
136
|
-
|
|
137
|
-
const dbPassword = await ask(' 数据库密码 (DB_PASSWORD,默认空): ');
|
|
190
|
+
const dbPassword = await ask(' 数据库密码 (DB_PASSWORD): ');
|
|
138
191
|
if (dbPassword) {
|
|
139
|
-
let
|
|
140
|
-
|
|
141
|
-
writeFileSync(envPath, content, 'utf-8');
|
|
192
|
+
let c = readFileSync(envPath, 'utf-8');
|
|
193
|
+
writeFileSync(envPath, c.replace(/DB_PASSWORD=.*/, `DB_PASSWORD=${dbPassword}`), 'utf-8');
|
|
142
194
|
}
|
|
143
|
-
|
|
144
195
|
const dbName = await ask(' 数据库名称 (DB_NAME,默认 xinyu): ');
|
|
145
196
|
if (dbName) {
|
|
146
|
-
let
|
|
147
|
-
|
|
148
|
-
writeFileSync(envPath, content, 'utf-8');
|
|
197
|
+
let c = readFileSync(envPath, 'utf-8');
|
|
198
|
+
writeFileSync(envPath, c.replace(/DB_NAME=.*/, `DB_NAME=${dbName}`), 'utf-8');
|
|
149
199
|
}
|
|
150
|
-
|
|
151
|
-
echo('');
|
|
152
|
-
echo(' ✓ .env.local 配置完成', 'success');
|
|
153
|
-
echo(' ✎ 如需修改,请编辑 ' + envPath, 'dim');
|
|
200
|
+
echo(' ✓ 配置完成', 'success');
|
|
201
|
+
echo(' ✎ 编辑: ' + envPath, 'dim');
|
|
154
202
|
};
|
|
155
|
-
|
|
156
|
-
return setupInteractive();
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function checkBuildExists() {
|
|
160
|
-
return existsSync(join(APP_DIR, '.next', 'BUILD_ID'));
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
function runBuild() {
|
|
164
|
-
echo(' ■ 正在构建应用,首次构建可能需要 1-2 分钟...', 'info');
|
|
165
|
-
const nextCmd = getNextCommand();
|
|
166
|
-
try {
|
|
167
|
-
execSync(`"${nextCmd}" build`, { cwd: APP_DIR, stdio: 'inherit', shell: isWin });
|
|
168
|
-
echo(' ✓ 构建完成', 'success');
|
|
169
|
-
} catch (e) {
|
|
170
|
-
echo(' ✗ 构建失败,请检查代码错误', 'error');
|
|
171
|
-
process.exit(1);
|
|
172
|
-
}
|
|
203
|
+
return run();
|
|
173
204
|
}
|
|
174
205
|
|
|
175
206
|
function startServer(port, devMode) {
|
|
176
|
-
const
|
|
207
|
+
const nextBin = getNextBin();
|
|
177
208
|
const action = devMode ? 'dev' : 'start';
|
|
178
209
|
|
|
179
210
|
echo('');
|
|
180
211
|
echo(` ╔══════════════════════════════════════╗`, 'success');
|
|
181
212
|
echo(` ║ 星语 Pro 服务器已启动 ║`, 'success');
|
|
182
213
|
echo(` ║ 访问地址: http://localhost:${port} ║`, 'success');
|
|
214
|
+
echo(` ║ 工作目录: ${RUN_DIR}`, 'dim');
|
|
183
215
|
echo(` ║ 按 Ctrl+C 停止 ║`, 'success');
|
|
184
216
|
echo(` ╚══════════════════════════════════════╝`, 'success');
|
|
185
217
|
echo('');
|
|
186
218
|
|
|
187
|
-
const child = spawn(
|
|
188
|
-
cwd:
|
|
219
|
+
const child = spawn(nextBin, [action, '-p', port], {
|
|
220
|
+
cwd: RUN_DIR,
|
|
189
221
|
stdio: 'inherit',
|
|
190
222
|
shell: isWin,
|
|
191
223
|
env: { ...process.env, PORT: port },
|
|
@@ -205,7 +237,7 @@ function startServer(port, devMode) {
|
|
|
205
237
|
|
|
206
238
|
process.on('SIGINT', () => {
|
|
207
239
|
echo('');
|
|
208
|
-
echo(' ■
|
|
240
|
+
echo(' ■ 正在关闭...', 'info');
|
|
209
241
|
child.kill('SIGINT');
|
|
210
242
|
process.exit(0);
|
|
211
243
|
});
|
|
@@ -220,7 +252,6 @@ async function main() {
|
|
|
220
252
|
echo('');
|
|
221
253
|
echo(' ╔══════════════════════════════════════╗', 'bold');
|
|
222
254
|
echo(' ║ 星语 Pro (xinyu-pro) ║', 'bold');
|
|
223
|
-
echo(' ║ 启动中... ║', 'bold');
|
|
224
255
|
echo(' ╚══════════════════════════════════════╝', 'bold');
|
|
225
256
|
echo('');
|
|
226
257
|
|
|
@@ -230,19 +261,13 @@ async function main() {
|
|
|
230
261
|
}
|
|
231
262
|
|
|
232
263
|
checkNodeVersion();
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
await
|
|
236
|
-
|
|
237
|
-
if (BUILD_FLAG) {
|
|
238
|
-
runBuild();
|
|
239
|
-
echo(' ✓ 构建完成,可使用 xinyu 启动服务器', 'success');
|
|
240
|
-
return;
|
|
241
|
-
}
|
|
264
|
+
ensureAppSetup();
|
|
265
|
+
installDependencies();
|
|
266
|
+
await setupEnvInteractive();
|
|
242
267
|
|
|
243
|
-
if (!DEV_FLAG && !
|
|
244
|
-
echo(' ⚠
|
|
245
|
-
echo('
|
|
268
|
+
if (!DEV_FLAG && !existsSync(join(RUN_DIR, '.next', 'BUILD_ID'))) {
|
|
269
|
+
echo(' ⚠ 未检测到构建产物,自动构建...', 'warn');
|
|
270
|
+
echo(' (首次构建需要下载依赖并编译,请耐心等待)', 'dim');
|
|
246
271
|
echo('');
|
|
247
272
|
runBuild();
|
|
248
273
|
}
|
package/package.json
CHANGED
package/version.json
CHANGED