xinyu-pro 0.21.1 → 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 +118 -99
- 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, mkdirSync,
|
|
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
7
|
const { homedir } = require('os');
|
|
8
8
|
|
|
9
|
-
const
|
|
10
|
-
const APP_DIR = __dirname.includes('node_modules') ? resolve(__dirname, '..') : cwd;
|
|
9
|
+
const isWin = process.platform === 'win32';
|
|
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,132 +73,154 @@ 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
|
+
}
|
|
76
95
|
}
|
|
77
96
|
|
|
78
|
-
function
|
|
79
|
-
const
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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;
|
|
86
125
|
}
|
|
87
|
-
|
|
126
|
+
|
|
127
|
+
return false;
|
|
88
128
|
}
|
|
89
129
|
|
|
90
|
-
function
|
|
91
|
-
if (!existsSync(join(
|
|
92
|
-
echo('
|
|
93
|
-
|
|
94
|
-
const cmd = pm === 'yarn' ? 'yarn install' : pm === 'pnpm' ? 'pnpm install' : 'npm install';
|
|
95
|
-
echo(` $ ${cmd}`, 'dim');
|
|
96
|
-
execSync(cmd, { cwd: APP_DIR, stdio: 'inherit' });
|
|
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 });
|
|
97
134
|
return true;
|
|
98
135
|
}
|
|
99
136
|
return false;
|
|
100
137
|
}
|
|
101
138
|
|
|
102
|
-
function
|
|
103
|
-
|
|
104
|
-
|
|
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
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function getNextBin() {
|
|
151
|
+
const binDir = join(RUN_DIR, 'node_modules', '.bin');
|
|
152
|
+
if (isWin) {
|
|
153
|
+
const cmdPath = join(binDir, 'next.cmd');
|
|
154
|
+
if (existsSync(cmdPath)) return cmdPath;
|
|
155
|
+
}
|
|
156
|
+
const unixPath = join(binDir, 'next');
|
|
157
|
+
if (existsSync(unixPath)) return unixPath;
|
|
158
|
+
return 'next';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function setupEnvInteractive() {
|
|
162
|
+
const envPath = join(RUN_DIR, '.env.local');
|
|
163
|
+
const examplePath = join(RUN_DIR, '.env.example');
|
|
105
164
|
|
|
106
165
|
if (existsSync(envPath)) {
|
|
107
166
|
const content = readFileSync(envPath, 'utf-8');
|
|
108
167
|
if (content.includes('your-api-key') || content.includes('你的密码')) {
|
|
109
|
-
echo(' ⚠ .env.local
|
|
168
|
+
echo(' ⚠ .env.local 中存在占位值,建议修改', 'warn');
|
|
110
169
|
}
|
|
111
170
|
return;
|
|
112
171
|
}
|
|
113
172
|
|
|
114
|
-
echo(' ⚠ 未检测到 .env.local 配置文件', 'warn');
|
|
115
|
-
echo('');
|
|
116
|
-
|
|
117
173
|
if (!existsSync(examplePath)) {
|
|
118
|
-
echo(' ✗ 未找到 .env.example
|
|
174
|
+
echo(' ✗ 未找到 .env.example', 'error');
|
|
119
175
|
process.exit(1);
|
|
120
176
|
}
|
|
121
177
|
|
|
122
178
|
const example = readFileSync(examplePath, 'utf-8');
|
|
123
179
|
writeFileSync(envPath, example, 'utf-8');
|
|
124
|
-
echo(' ✓
|
|
125
|
-
echo('');
|
|
180
|
+
echo(' ✓ 已创建 .env.local', 'success');
|
|
126
181
|
|
|
127
|
-
const
|
|
128
|
-
echo(' ── 请填写以下必要配置(直接回车可跳过) ──', 'bold');
|
|
182
|
+
const run = async () => {
|
|
129
183
|
echo('');
|
|
130
|
-
|
|
184
|
+
echo(' ── 填写配置(直接回车可跳过) ──', 'bold');
|
|
131
185
|
const apiKey = await ask(' AI API 密钥 (AI_API_KEY): ');
|
|
132
186
|
if (apiKey) {
|
|
133
|
-
let
|
|
134
|
-
|
|
135
|
-
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');
|
|
136
189
|
}
|
|
137
|
-
|
|
138
|
-
const dbPassword = await ask(' 数据库密码 (DB_PASSWORD,默认空): ');
|
|
190
|
+
const dbPassword = await ask(' 数据库密码 (DB_PASSWORD): ');
|
|
139
191
|
if (dbPassword) {
|
|
140
|
-
let
|
|
141
|
-
|
|
142
|
-
writeFileSync(envPath, content, 'utf-8');
|
|
192
|
+
let c = readFileSync(envPath, 'utf-8');
|
|
193
|
+
writeFileSync(envPath, c.replace(/DB_PASSWORD=.*/, `DB_PASSWORD=${dbPassword}`), 'utf-8');
|
|
143
194
|
}
|
|
144
|
-
|
|
145
195
|
const dbName = await ask(' 数据库名称 (DB_NAME,默认 xinyu): ');
|
|
146
196
|
if (dbName) {
|
|
147
|
-
let
|
|
148
|
-
|
|
149
|
-
writeFileSync(envPath, content, 'utf-8');
|
|
197
|
+
let c = readFileSync(envPath, 'utf-8');
|
|
198
|
+
writeFileSync(envPath, c.replace(/DB_NAME=.*/, `DB_NAME=${dbName}`), 'utf-8');
|
|
150
199
|
}
|
|
151
|
-
|
|
152
|
-
echo('');
|
|
153
|
-
echo(' ✓ .env.local 配置完成', 'success');
|
|
154
|
-
echo(' ✎ 如需修改,请编辑 ' + envPath, 'dim');
|
|
200
|
+
echo(' ✓ 配置完成', 'success');
|
|
201
|
+
echo(' ✎ 编辑: ' + envPath, 'dim');
|
|
155
202
|
};
|
|
156
|
-
|
|
157
|
-
return setupInteractive();
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
function checkBuildExists() {
|
|
161
|
-
return existsSync(join(APP_DIR, '.next', 'BUILD_ID'));
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
function runBuild() {
|
|
165
|
-
echo(' ■ 正在构建应用,首次构建可能需要 1-2 分钟...', 'info');
|
|
166
|
-
const nextCli = getNextCliPath();
|
|
167
|
-
try {
|
|
168
|
-
execSync(`"${process.execPath}" "${nextCli}" build`, {
|
|
169
|
-
cwd: APP_DIR,
|
|
170
|
-
stdio: 'inherit',
|
|
171
|
-
});
|
|
172
|
-
echo(' ✓ 构建完成', 'success');
|
|
173
|
-
} catch (e) {
|
|
174
|
-
echo(' ✗ 构建失败,请检查代码错误', 'error');
|
|
175
|
-
process.exit(1);
|
|
176
|
-
}
|
|
203
|
+
return run();
|
|
177
204
|
}
|
|
178
205
|
|
|
179
206
|
function startServer(port, devMode) {
|
|
180
|
-
const
|
|
207
|
+
const nextBin = getNextBin();
|
|
208
|
+
const action = devMode ? 'dev' : 'start';
|
|
181
209
|
|
|
182
210
|
echo('');
|
|
183
211
|
echo(` ╔══════════════════════════════════════╗`, 'success');
|
|
184
212
|
echo(` ║ 星语 Pro 服务器已启动 ║`, 'success');
|
|
185
213
|
echo(` ║ 访问地址: http://localhost:${port} ║`, 'success');
|
|
214
|
+
echo(` ║ 工作目录: ${RUN_DIR}`, 'dim');
|
|
186
215
|
echo(` ║ 按 Ctrl+C 停止 ║`, 'success');
|
|
187
216
|
echo(` ╚══════════════════════════════════════╝`, 'success');
|
|
188
217
|
echo('');
|
|
189
218
|
|
|
190
|
-
const
|
|
191
|
-
|
|
192
|
-
cwd: APP_DIR,
|
|
219
|
+
const child = spawn(nextBin, [action, '-p', port], {
|
|
220
|
+
cwd: RUN_DIR,
|
|
193
221
|
stdio: 'inherit',
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
PORT: port,
|
|
197
|
-
},
|
|
222
|
+
shell: isWin,
|
|
223
|
+
env: { ...process.env, PORT: port },
|
|
198
224
|
});
|
|
199
225
|
|
|
200
226
|
child.on('error', (err) => {
|
|
@@ -211,7 +237,7 @@ function startServer(port, devMode) {
|
|
|
211
237
|
|
|
212
238
|
process.on('SIGINT', () => {
|
|
213
239
|
echo('');
|
|
214
|
-
echo(' ■
|
|
240
|
+
echo(' ■ 正在关闭...', 'info');
|
|
215
241
|
child.kill('SIGINT');
|
|
216
242
|
process.exit(0);
|
|
217
243
|
});
|
|
@@ -226,7 +252,6 @@ async function main() {
|
|
|
226
252
|
echo('');
|
|
227
253
|
echo(' ╔══════════════════════════════════════╗', 'bold');
|
|
228
254
|
echo(' ║ 星语 Pro (xinyu-pro) ║', 'bold');
|
|
229
|
-
echo(' ║ 启动中... ║', 'bold');
|
|
230
255
|
echo(' ╚══════════════════════════════════════╝', 'bold');
|
|
231
256
|
echo('');
|
|
232
257
|
|
|
@@ -236,19 +261,13 @@ async function main() {
|
|
|
236
261
|
}
|
|
237
262
|
|
|
238
263
|
checkNodeVersion();
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
await
|
|
242
|
-
|
|
243
|
-
if (BUILD_FLAG) {
|
|
244
|
-
runBuild();
|
|
245
|
-
echo(' ✓ 构建完成,可使用 xinyu 启动服务器', 'success');
|
|
246
|
-
return;
|
|
247
|
-
}
|
|
264
|
+
ensureAppSetup();
|
|
265
|
+
installDependencies();
|
|
266
|
+
await setupEnvInteractive();
|
|
248
267
|
|
|
249
|
-
if (!DEV_FLAG && !
|
|
250
|
-
echo(' ⚠
|
|
251
|
-
echo('
|
|
268
|
+
if (!DEV_FLAG && !existsSync(join(RUN_DIR, '.next', 'BUILD_ID'))) {
|
|
269
|
+
echo(' ⚠ 未检测到构建产物,自动构建...', 'warn');
|
|
270
|
+
echo(' (首次构建需要下载依赖并编译,请耐心等待)', 'dim');
|
|
252
271
|
echo('');
|
|
253
272
|
runBuild();
|
|
254
273
|
}
|
package/package.json
CHANGED
package/version.json
CHANGED