jjb-cmd 2.5.6-beta.2 → 2.5.7

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/command.js CHANGED
@@ -3,18 +3,16 @@
3
3
  const commander = require('commander');
4
4
  const path = require('path');
5
5
  const fs = require('fs');
6
- const { checkCmdVersion } = require('../src/utils');
7
6
 
8
- commander.command('v').description('-- 查看版本').action(async () => {
7
+ commander.command('v').description('-- 查看版本').action(() => {
9
8
  const package_json_file = JSON.parse(fs.readFileSync(path.join(__dirname, '../package.json')).toString());
10
9
  const {
11
10
  version
12
11
  } = package_json_file;
13
12
  console.log(`当前版本: v${version}`);
14
- // 版本查看命令不需要检查版本
15
13
  });
16
14
 
17
- commander.command('help').description('-- 帮助').action(async () => {
15
+ commander.command('help').description('-- 帮助').action(() => {
18
16
  console.log('jjb-cmd <command>');
19
17
  console.log('');
20
18
  console.log('使用:');
@@ -26,42 +24,25 @@ commander.command('help').description('-- 帮助').action(async () => {
26
24
  console.log('jjb-cmd auth 登录授权(交互式输入用户名和密码)');
27
25
  console.log('jjb-cmd push java 推送微应用到服务器');
28
26
  console.log('jjb-cmd ai-pull [branch] 拉取 AI 配置文件和规则\n\targ1 <branch> 指定分支/标签,默认为 v_1.0.0');
29
- // 帮助命令不需要检查版本
30
27
  });
31
28
 
32
29
  // 命令
33
30
  commander.command('auth').description('-- 授权').action(async () => {
34
- const isLatest = await checkCmdVersion();
35
- if (!isLatest) {
36
- process.exit(1);
37
- }
38
31
  await require('../src/auth.js')([]);
39
32
  });
40
33
 
41
34
  // 优化
42
35
  commander.command('opti').description('-- 代码优化').action(async args => {
43
- const isLatest = await checkCmdVersion();
44
- if (!isLatest) {
45
- process.exit(1);
46
- }
47
36
  await require('../src/code-optimization')();
48
37
  });
49
38
 
50
39
  // 发包
51
- commander.command('publish [args]').description('-- 发布包').action(async args => {
52
- const isLatest = await checkCmdVersion();
53
- if (!isLatest) {
54
- process.exit(1);
55
- }
40
+ commander.command('publish [args]').description('-- 发布包').action(args => {
56
41
  require('../src/publish.js')(args);
57
42
  });
58
43
 
59
44
  // publish 命令
60
- commander.command('push [args]').description('-- 发布包').action(async args => {
61
- const isLatest = await checkCmdVersion();
62
- if (!isLatest) {
63
- process.exit(1);
64
- }
45
+ commander.command('push [args]').description('-- 发布包').action(args => {
65
46
  if (args) {
66
47
  if ([ 'java' ].includes(args)) {
67
48
  require('../src/push.js')([]);
@@ -75,40 +56,13 @@ commander.command('push [args]').description('-- 发布包').action(async args =
75
56
  }
76
57
  });
77
58
 
78
- // npm-publish 命令 - 发布npm包
79
- commander.command('npm-publish [version]')
80
- .description('-- 发布npm包(内部命令)')
81
- .option('--tag <tag>', 'npm发布标签(如:beta、alpha等,默认为latest)')
82
- .action(async (version, options) => {
83
- const isLatest = await checkCmdVersion();
84
- if (!isLatest) {
85
- process.exit(1);
86
- }
87
- if (!version) {
88
- console.log(`缺少必要参数,请使用:`);
89
- console.log(` yarn npm-publish <version> 发布npm包(latest标签)`);
90
- console.log(` yarn npm-publish <version> --tag <tag> 发布npm包(指定标签)`);
91
- process.exit(0);
92
- }
93
- const tag = options.tag || null;
94
- await require('../src/push-npm.js')(version, tag);
95
- });
96
-
97
59
  // rm-rf 命令
98
60
  commander.command('rm-rf').description('-- 删除全部').action(async () => {
99
- const isLatest = await checkCmdVersion();
100
- if (!isLatest) {
101
- process.exit(1);
102
- }
103
61
  await require('../src/rm-rf.js')();
104
62
  });
105
63
 
106
64
  // ai-pull 命令
107
- commander.command('ai-pull [branch]').description('-- 拉取 AI 配置文件和规则').action(async (branch) => {
108
- const isLatest = await checkCmdVersion();
109
- if (!isLatest) {
110
- process.exit(1);
111
- }
65
+ commander.command('ai-pull [branch]').description('-- 拉取 AI 配置文件和规则').action((branch) => {
112
66
  require('../src/ai-pull.js')(branch);
113
67
  });
114
68
 
package/package.json CHANGED
@@ -1,41 +1,40 @@
1
- {
2
- "name": "jjb-cmd",
3
- "use": "no",
4
- "env": "prod",
5
- "httpMethod": "http",
6
- "pushMessage": "yes",
7
- "version": "2.5.6-beta.2",
8
- "description": "jjb-cmd命令行工具",
9
- "main": "index.js",
10
- "scripts": {
11
- "test": "node bin/command.js help",
12
- "install:package": "npm i",
13
- "test:ai-pull": "node bin/command.js ai-pull",
14
- "npm-publish": "node bin/command.js npm-publish"
15
- },
16
- "bin": {
17
- "jjb-cmd": "bin/command.js"
18
- },
19
- "author": "jiaoxiwei",
20
- "license": "MIT",
21
- "dependencies": {
22
- "@babel/core": "^7.19.3",
23
- "@babel/generator": "^7.26.0",
24
- "@babel/parser": "^7.26.1",
25
- "@babel/preset-env": "^7.23.2",
26
- "@babel/preset-react": "^7.18.6",
27
- "@babel/preset-typescript": "^7.23.2",
28
- "@babel/traverse": "^7.25.9",
29
- "@cqsjjb/react-code-optimization": "latest",
30
- "axios": "^1.1.3",
31
- "better-sqlite3": "^12.6.2",
32
- "chalk": "2.4.0",
33
- "commander": "^1.3.2",
34
- "compressing": "^1.5.1",
35
- "inquirer": "^8.2.7",
36
- "jenkins": "^1.0.2",
37
- "prettier": "^3.3.3",
38
- "request": "2.88.2",
39
- "single-line-log": "1.1.2"
40
- }
41
- }
1
+ {
2
+ "name": "jjb-cmd",
3
+ "use": "no",
4
+ "env": "prod",
5
+ "httpMethod": "http",
6
+ "pushMessage": "yes",
7
+ "version": "2.5.7",
8
+ "description": "jjb-cmd命令行工具",
9
+ "main": "index.js",
10
+ "scripts": {
11
+ "test": "node bin/command.js help",
12
+ "install:package": "npm i",
13
+ "test:ai-pull": "node bin/command.js ai-pull"
14
+ },
15
+ "bin": {
16
+ "jjb-cmd": "bin/command.js"
17
+ },
18
+ "author": "jiaoxiwei",
19
+ "license": "MIT",
20
+ "dependencies": {
21
+ "@babel/core": "^7.19.3",
22
+ "@babel/generator": "^7.26.0",
23
+ "@babel/parser": "^7.26.1",
24
+ "@babel/preset-env": "^7.23.2",
25
+ "@babel/preset-react": "^7.18.6",
26
+ "@babel/preset-typescript": "^7.23.2",
27
+ "@babel/traverse": "^7.25.9",
28
+ "@cqsjjb/react-code-optimization": "latest",
29
+ "axios": "^1.1.3",
30
+ "better-sqlite3": "^12.6.2",
31
+ "chalk": "2.4.0",
32
+ "commander": "^1.3.2",
33
+ "compressing": "^1.5.1",
34
+ "inquirer": "^8.2.7",
35
+ "jenkins": "^1.0.2",
36
+ "prettier": "^3.3.3",
37
+ "request": "2.88.2",
38
+ "single-line-log": "1.1.2"
39
+ }
40
+ }
package/src/ai-pull.js CHANGED
@@ -18,7 +18,7 @@ const CURSOR_DB_PATH = path.join(
18
18
  // Git 仓库地址
19
19
  const AI_REPO_URL = 'http://192.168.1.242:10985/root/jjb-ai.git';
20
20
  // Git 分支/标签(默认值)
21
- const DEFAULT_BRANCH = 'v_1.0.0';
21
+ const DEFAULT_BRANCH = 'v_2.0.0';
22
22
 
23
23
  /**
24
24
  * 删除目录(回调方式,使用递归删除)
@@ -241,40 +241,72 @@ module.exports = (branch) => {
241
241
  });
242
242
  console.log(`✓ 仓库代码拉取成功(分支: ${targetBranch})`);
243
243
 
244
- // 步骤2: 复制 .ai 文件夹到当前项目
245
- console.log('步骤2: 正在复制 .ai 文件夹...');
246
- const aiSourcePath = path.join(cloneDir, '.ai');
247
- const aiTargetPath = path.join(root_path, '.ai');
244
+ // 步骤2: 将仓库 admin 下所有子文件夹同步到当前项目 .cursor/(与 admin 同名的子目录)
245
+ console.log('步骤2: 正在复制 admin 下的文件夹到 .cursor/...');
246
+ const adminPath = path.join(cloneDir, 'admin');
247
+ const cursorDir = path.join(root_path, '.cursor');
248
248
 
249
- if (!fs.existsSync(aiSourcePath)) {
250
- console.log('【Warning】:仓库中未找到 .ai 文件夹');
249
+ if (!fs.existsSync(adminPath)) {
250
+ console.log('【Warning】:仓库中未找到 admin 文件夹');
251
251
  step3();
252
252
  } else {
253
- // 如果目标目录已存在,先删除
254
- if (fs.existsSync(aiTargetPath)) {
255
- deleteDir(aiTargetPath, () => {
256
- // 复制 .ai 文件夹
257
- copyFolder(aiSourcePath, aiTargetPath, (err) => {
253
+ let subdirs;
254
+ try {
255
+ subdirs = fs.readdirSync(adminPath).filter((name) => {
256
+ const p = path.join(adminPath, name);
257
+ try {
258
+ return fs.statSync(p).isDirectory();
259
+ } catch (e) {
260
+ return false;
261
+ }
262
+ });
263
+ } catch (e) {
264
+ console.error('【Error】:读取 admin 目录失败', e.message);
265
+ cleanupAndExit(1);
266
+ return;
267
+ }
268
+
269
+ if (subdirs.length === 0) {
270
+ console.log('【Warning】:admin 下没有子文件夹可同步');
271
+ step3();
272
+ } else {
273
+ try {
274
+ if (!fs.existsSync(cursorDir)) {
275
+ fs.mkdirSync(cursorDir, { recursive: true });
276
+ }
277
+ } catch (e) {
278
+ console.error('【Error】:创建 .cursor 目录失败', e.message);
279
+ cleanupAndExit(1);
280
+ return;
281
+ }
282
+
283
+ function copyAdminDirAt(i) {
284
+ if (i >= subdirs.length) {
285
+ console.log(`✓ 已将 admin 下 ${subdirs.length} 个文件夹复制到 .cursor/(${subdirs.join(', ')})`);
286
+ step3();
287
+ return;
288
+ }
289
+ const name = subdirs[i];
290
+ const srcPath = path.join(adminPath, name);
291
+ const destPath = path.join(cursorDir, name);
292
+ const afterCopy = (err) => {
258
293
  if (err) {
259
- console.error('【Error】:复制 .ai 文件夹失败', err.message);
294
+ console.error(`【Error】:复制 admin/${name} 失败`, err.message);
260
295
  cleanupAndExit(1);
261
296
  return;
262
297
  }
263
- console.log('✓ .ai 文件夹复制成功');
264
- step3();
265
- });
266
- });
267
- } else {
268
- // 复制 .ai 文件夹
269
- copyFolder(aiSourcePath, aiTargetPath, (err) => {
270
- if (err) {
271
- console.error('【Error】:复制 .ai 文件夹失败', err.message);
272
- cleanupAndExit(1);
273
- return;
298
+ copyAdminDirAt(i + 1);
299
+ };
300
+ if (fs.existsSync(destPath)) {
301
+ deleteDir(destPath, () => {
302
+ copyFolder(srcPath, destPath, afterCopy);
303
+ });
304
+ } else {
305
+ copyFolder(srcPath, destPath, afterCopy);
274
306
  }
275
- console.log('✓ .ai 文件夹复制成功');
276
- step3();
277
- });
307
+ }
308
+
309
+ copyAdminDirAt(0);
278
310
  }
279
311
  }
280
312
  } catch (error) {
@@ -283,26 +315,26 @@ module.exports = (branch) => {
283
315
  }
284
316
  });
285
317
 
286
- // 步骤3: 复制 prompt.md Cursor user rules (SQLite 数据库)
318
+ // 步骤3: admin/rules/PROJECT.md 写入 Cursor user rules (SQLite 数据库)
287
319
  function step3() {
288
320
  console.log('步骤3: 正在更新 Cursor user rules (SQLite 数据库)...');
289
- const promptSourcePath = path.join(cloneDir, 'prompt.md');
321
+ const projectRulesPath = path.join(cloneDir, 'admin', 'rules', 'PROJECT.md');
290
322
 
291
323
  try {
292
- if (!fs.existsSync(promptSourcePath)) {
293
- console.log('【Warning】:仓库中未找到 prompt.md 文件');
324
+ if (!fs.existsSync(projectRulesPath)) {
325
+ console.log('【Warning】:仓库中未找到 admin/rules/PROJECT.md 文件');
294
326
  step4();
295
327
  return;
296
328
  }
297
329
 
298
- // 读取 prompt.md 内容
299
- const promptContent = fs.readFileSync(promptSourcePath, 'utf8');
330
+ // 读取 PROJECT.md 内容
331
+ const rulesContent = fs.readFileSync(projectRulesPath, 'utf8');
300
332
 
301
333
  // 检查数据库文件是否存在
302
334
  if (!fs.existsSync(CURSOR_DB_PATH)) {
303
335
  console.log('【Warning】:Cursor 数据库文件不存在,可能 Cursor 未安装或路径不正确');
304
336
  console.log(`【路径】:${CURSOR_DB_PATH}`);
305
- console.log('【建议】:请确保已安装 Cursor 编辑器,或手动复制 prompt.md 内容');
337
+ console.log('【建议】:请确保已安装 Cursor 编辑器,或手动复制 admin/rules/PROJECT.md 内容');
306
338
  step4();
307
339
  return;
308
340
  }
@@ -348,7 +380,7 @@ module.exports = (branch) => {
348
380
  try {
349
381
  const sqlite3 = require('sqlite3');
350
382
  // sqlite3 是异步的,需要使用回调或 Promise
351
- console.log(`【调试】:准备更新数据库,内容长度: ${promptContent.length} 字符`);
383
+ console.log(`【调试】:准备更新数据库,内容长度: ${rulesContent.length} 字符`);
352
384
 
353
385
  const db = new sqlite3.Database(CURSOR_DB_PATH, (err) => {
354
386
  if (err) {
@@ -379,8 +411,8 @@ module.exports = (branch) => {
379
411
  oldContent = String(row.value);
380
412
  }
381
413
 
382
- if (oldContent === promptContent) {
383
- console.log('✓ prompt.md 内容未变化,无需更新');
414
+ if (oldContent === rulesContent) {
415
+ console.log('✓ admin/rules/PROJECT.md 内容未变化,无需更新');
384
416
  console.log('【提示】:请重启 Cursor 编辑器以使规则生效');
385
417
  db.close();
386
418
  step4();
@@ -388,7 +420,7 @@ module.exports = (branch) => {
388
420
  }
389
421
 
390
422
  // 更新现有记录
391
- db.run("UPDATE ItemTable SET value = ? WHERE key = ?", [promptContent, 'aicontext.personalContext'], function(err) {
423
+ db.run("UPDATE ItemTable SET value = ? WHERE key = ?", [rulesContent, 'aicontext.personalContext'], function(err) {
392
424
  if (err) {
393
425
  console.error('【Error】:更新数据库失败', err.message);
394
426
  if (err.code === 'SQLITE_BUSY' || err.message.includes('locked')) {
@@ -422,15 +454,15 @@ module.exports = (branch) => {
422
454
  verifyContent = String(verifyRow.value);
423
455
  }
424
456
 
425
- if (verifyContent === promptContent) {
426
- console.log(`✓ prompt.md 已更新到 Cursor user rules (${verifyRow.len} 字节)`);
457
+ if (verifyContent === rulesContent) {
458
+ console.log(`✓ PROJECT.md 已更新到 Cursor user rules (${verifyRow.len} 字节)`);
427
459
  // 显示前几行内容确认
428
460
  const previewLines = verifyContent.split('\n').slice(0, 3).join('\n');
429
461
  console.log(`【预览】:内容前3行:\n${previewLines}${verifyContent.split('\n').length > 3 ? '...' : ''}`);
430
462
  } else {
431
463
  console.error(`【Error】:更新后内容不匹配!`);
432
- console.error(` 期望长度: ${promptContent.length}, 实际长度: ${verifyContent.length}`);
433
- console.error(` 期望前100字符: ${promptContent.substring(0, 100)}`);
464
+ console.error(` 期望长度: ${rulesContent.length}, 实际长度: ${verifyContent.length}`);
465
+ console.error(` 期望前100字符: ${rulesContent.substring(0, 100)}`);
434
466
  console.error(` 实际前100字符: ${verifyContent.substring(0, 100)}`);
435
467
  }
436
468
  } else {
@@ -445,7 +477,7 @@ module.exports = (branch) => {
445
477
  } else {
446
478
  console.log('【调试】:记录不存在,将插入新记录');
447
479
  // 插入新记录
448
- db.run("INSERT INTO ItemTable (key, value) VALUES (?, ?)", ['aicontext.personalContext', promptContent], function(err) {
480
+ db.run("INSERT INTO ItemTable (key, value) VALUES (?, ?)", ['aicontext.personalContext', rulesContent], function(err) {
449
481
  if (err) {
450
482
  console.error('【Error】:插入数据库失败', err.message);
451
483
  if (err.code === 'SQLITE_BUSY' || err.message.includes('locked')) {
@@ -478,14 +510,14 @@ module.exports = (branch) => {
478
510
  verifyContent = String(verifyRow.value);
479
511
  }
480
512
 
481
- if (verifyContent === promptContent) {
482
- console.log(`✓ prompt.md 已添加到 Cursor user rules (${verifyRow.len} 字节)`);
513
+ if (verifyContent === rulesContent) {
514
+ console.log(`✓ PROJECT.md 已添加到 Cursor user rules (${verifyRow.len} 字节)`);
483
515
  // 显示前几行内容确认
484
516
  const previewLines = verifyContent.split('\n').slice(0, 3).join('\n');
485
517
  console.log(`【预览】:内容前3行:\n${previewLines}${verifyContent.split('\n').length > 3 ? '...' : ''}`);
486
518
  } else {
487
519
  console.error(`【Error】:插入后内容不匹配!`);
488
- console.error(` 期望长度: ${promptContent.length}, 实际长度: ${verifyContent.length}`);
520
+ console.error(` 期望长度: ${rulesContent.length}, 实际长度: ${verifyContent.length}`);
489
521
  }
490
522
  } else {
491
523
  console.error('【Error】:插入后验证失败,值为空');
@@ -513,7 +545,7 @@ module.exports = (branch) => {
513
545
  const db = new Database(CURSOR_DB_PATH, { readonly: false });
514
546
 
515
547
  try {
516
- console.log(`【调试】:准备更新数据库,内容长度: ${promptContent.length} 字符`);
548
+ console.log(`【调试】:准备更新数据库,内容长度: ${rulesContent.length} 字符`);
517
549
 
518
550
  // 先查询是否存在该键
519
551
  const row = db.prepare("SELECT value, length(value) as len FROM ItemTable WHERE key = ?").get('aicontext.personalContext');
@@ -529,8 +561,8 @@ module.exports = (branch) => {
529
561
  oldContent = String(row.value);
530
562
  }
531
563
 
532
- if (oldContent === promptContent) {
533
- console.log('✓ prompt.md 内容未变化,无需更新');
564
+ if (oldContent === rulesContent) {
565
+ console.log('✓ admin/rules/PROJECT.md 内容未变化,无需更新');
534
566
  console.log('【提示】:请重启 Cursor 编辑器以使规则生效');
535
567
  db.close();
536
568
  step4();
@@ -539,7 +571,7 @@ module.exports = (branch) => {
539
571
 
540
572
  // 更新现有记录
541
573
  const updateStmt = db.prepare("UPDATE ItemTable SET value = ? WHERE key = ?");
542
- const result = updateStmt.run(promptContent, 'aicontext.personalContext');
574
+ const result = updateStmt.run(rulesContent, 'aicontext.personalContext');
543
575
  console.log(`【调试】:更新影响行数: ${result.changes}`);
544
576
 
545
577
  if (result.changes === 0) {
@@ -560,15 +592,15 @@ module.exports = (branch) => {
560
592
  verifyContent = String(verifyRow.value);
561
593
  }
562
594
 
563
- if (verifyContent === promptContent) {
564
- console.log(`✓ prompt.md 已更新到 Cursor user rules (${verifyRow.len} 字节)`);
595
+ if (verifyContent === rulesContent) {
596
+ console.log(`✓ PROJECT.md 已更新到 Cursor user rules (${verifyRow.len} 字节)`);
565
597
  // 显示前几行内容确认
566
598
  const previewLines = verifyContent.split('\n').slice(0, 3).join('\n');
567
599
  console.log(`【预览】:内容前3行:\n${previewLines}${verifyContent.split('\n').length > 3 ? '...' : ''}`);
568
600
  } else {
569
601
  console.error(`【Error】:更新后内容不匹配!`);
570
- console.error(` 期望长度: ${promptContent.length}, 实际长度: ${verifyContent.length}`);
571
- console.error(` 期望前100字符: ${promptContent.substring(0, 100)}`);
602
+ console.error(` 期望长度: ${rulesContent.length}, 实际长度: ${verifyContent.length}`);
603
+ console.error(` 期望前100字符: ${rulesContent.substring(0, 100)}`);
572
604
  console.error(` 实际前100字符: ${verifyContent.substring(0, 100)}`);
573
605
  }
574
606
  } else {
@@ -578,7 +610,7 @@ module.exports = (branch) => {
578
610
  console.log('【调试】:记录不存在,将插入新记录');
579
611
  // 插入新记录
580
612
  const insertStmt = db.prepare("INSERT INTO ItemTable (key, value) VALUES (?, ?)");
581
- const result = insertStmt.run('aicontext.personalContext', promptContent);
613
+ const result = insertStmt.run('aicontext.personalContext', rulesContent);
582
614
  console.log(`【调试】:插入结果,最后插入ID: ${result.lastInsertRowid}`);
583
615
 
584
616
  if (!result.lastInsertRowid) {
@@ -598,14 +630,14 @@ module.exports = (branch) => {
598
630
  verifyContent = String(verifyRow.value);
599
631
  }
600
632
 
601
- if (verifyContent === promptContent) {
602
- console.log(`✓ prompt.md 已添加到 Cursor user rules (${verifyRow.len} 字节)`);
633
+ if (verifyContent === rulesContent) {
634
+ console.log(`✓ PROJECT.md 已添加到 Cursor user rules (${verifyRow.len} 字节)`);
603
635
  // 显示前几行内容确认
604
636
  const previewLines = verifyContent.split('\n').slice(0, 3).join('\n');
605
637
  console.log(`【预览】:内容前3行:\n${previewLines}${verifyContent.split('\n').length > 3 ? '...' : ''}`);
606
638
  } else {
607
639
  console.error(`【Error】:插入后内容不匹配!`);
608
- console.error(` 期望长度: ${promptContent.length}, 实际长度: ${verifyContent.length}`);
640
+ console.error(` 期望长度: ${rulesContent.length}, 实际长度: ${verifyContent.length}`);
609
641
  }
610
642
  } else {
611
643
  console.error('【Error】:插入后验证失败,值为空');
@@ -630,8 +662,8 @@ module.exports = (branch) => {
630
662
 
631
663
  step4();
632
664
  } catch (error) {
633
- console.error('【Error】:处理 prompt.md 失败', error.message);
634
- console.log('【Warning】:可能 Cursor 未安装或路径不正确,请手动复制 prompt.md');
665
+ console.error('【Error】:处理 admin/rules/PROJECT.md 失败', error.message);
666
+ console.log('【Warning】:可能 Cursor 未安装或路径不正确,请手动复制 admin/rules/PROJECT.md 内容');
635
667
  step4();
636
668
  }
637
669
  }
@@ -644,23 +676,23 @@ module.exports = (branch) => {
644
676
  try {
645
677
  if (fs.existsSync(gitignorePath)) {
646
678
  let gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
647
- const aiIgnorePattern = /^\.ai\s*$/m;
679
+ const cursorIgnorePattern = /^\.cursor\/?\s*$/m;
648
680
 
649
- if (!aiIgnorePattern.test(gitignoreContent)) {
681
+ if (!cursorIgnorePattern.test(gitignoreContent)) {
650
682
  // 如果文件末尾没有换行,先添加换行
651
683
  if (!gitignoreContent.endsWith('\n')) {
652
684
  gitignoreContent += '\n';
653
685
  }
654
- gitignoreContent += '.ai\n';
686
+ gitignoreContent += '.cursor/\n';
655
687
  fs.writeFileSync(gitignorePath, gitignoreContent, 'utf8');
656
- console.log('✓ 已在 .gitignore 中添加 .ai 忽略规则');
688
+ console.log('✓ 已在 .gitignore 中添加 .cursor/ 忽略规则');
657
689
  } else {
658
- console.log('✓ .gitignore 中已存在 .ai 忽略规则');
690
+ console.log('✓ .gitignore 中已存在 .cursor 忽略规则');
659
691
  }
660
692
  } else {
661
693
  // 如果 .gitignore 不存在,创建它
662
- fs.writeFileSync(gitignorePath, '.ai\n', 'utf8');
663
- console.log('✓ 已创建 .gitignore 并添加 .ai 忽略规则');
694
+ fs.writeFileSync(gitignorePath, '.cursor/\n', 'utf8');
695
+ console.log('✓ 已创建 .gitignore 并添加 .cursor/ 忽略规则');
664
696
  }
665
697
  } catch (error) {
666
698
  console.error('【Error】:更新 .gitignore 失败', error.message);
package/src/publish.js CHANGED
@@ -12,41 +12,10 @@ const {
12
12
  executeCommand,
13
13
  fileExists,
14
14
  readFile,
15
- writeFile,
16
- getGitBranch,
17
- getDeviceInfo,
18
- calculateSimilarity
15
+ writeFile
19
16
  } = require('./utils');
20
17
  const { loadAuth } = require('./crypto-utils');
21
18
 
22
- /**
23
- * 格式化版本号
24
- * 如果输入的版本号以v开头(包括v_),则去掉v前缀(服务器端会统一添加v前缀)
25
- * 如果输入的版本号不以v开头,则保持原样(服务器端会添加v前缀)
26
- * 这样可以避免服务器端重复添加前缀导致vv_1.0.0的问题
27
- * @param {string} version - 输入的版本号
28
- * @returns {string} 格式化后的版本号(去掉v前缀的纯版本号)
29
- */
30
- function formatVersion(version) {
31
- if (!version) {
32
- return '0';
33
- }
34
-
35
- let formattedVersion = version.trim();
36
-
37
- // 如果版本号以v_开头,去掉v_前缀(服务器端会添加v前缀)
38
- if (formattedVersion.toLowerCase().startsWith('v_')) {
39
- formattedVersion = formattedVersion.substring(2);
40
- }
41
- // 如果版本号以v开头(但不是v_),去掉v前缀(服务器端会添加v前缀)
42
- else if (formattedVersion.toLowerCase().startsWith('v')) {
43
- formattedVersion = formattedVersion.substring(1);
44
- }
45
- // 如果版本号不以v开头,保持原样(服务器端会添加v前缀)
46
-
47
- return formattedVersion;
48
- }
49
-
50
19
  /**
51
20
  * 解析组件配置信息
52
21
  * @param {string} distContent - 构建后的文件内容
@@ -229,67 +198,12 @@ module.exports = version => {
229
198
  logError(error.message);
230
199
  process.exit(1);
231
200
  }
232
-
233
- // 检查README相似度
234
- logInfo('正在检查README文档...');
235
- try {
236
- const defaultReadmePath = path.join(__dirname, '../DEFAULT_README.md');
237
- if (fileExists(defaultReadmePath)) {
238
- const defaultReadme = readFile(defaultReadmePath);
239
- const projectReadme = readFile(filePaths.readmePath);
240
- const similarity = calculateSimilarity(defaultReadme, projectReadme);
241
-
242
- if (similarity >= 90) {
243
- logError(`README文档与默认文档相似度过高(${similarity.toFixed(2)}%),请修改README文档后再发布`);
244
- logWarning('提示:README文档应该包含组件的具体说明和使用方法,不能使用默认模板');
245
- process.exit(1);
246
- } else {
247
- logSuccess(`README文档检查通过(相似度:${similarity.toFixed(2)}%)`);
248
- }
249
- } else {
250
- logWarning('未找到DEFAULT_README.md文件,跳过README相似度检查');
251
- }
252
- } catch (error) {
253
- logWarning(`README相似度检查失败: ${error.message},将继续发布流程`);
254
- }
255
201
 
256
202
  // 读取包信息
257
203
  logInfo('正在读取包信息...');
258
204
  const packageJsonFile = JSON.parse(readFile(path.join(__dirname, '../package.json')));
259
205
  const { version: cmdVersion } = packageJsonFile;
260
206
 
261
- // 格式化版本号
262
- const formattedVersion = formatVersion(version);
263
-
264
- // 读取dist/index.js内容
265
- let distContent = readFile(filePaths.distPath);
266
-
267
- // 获取构建信息
268
- const gitBranch = getGitBranch(rootPath);
269
- const deviceInfo = getDeviceInfo();
270
- const buildTime = new Date().toLocaleString('zh-CN', {
271
- year: 'numeric',
272
- month: '2-digit',
273
- day: '2-digit',
274
- hour: '2-digit',
275
- minute: '2-digit',
276
- second: '2-digit',
277
- hour12: false
278
- });
279
-
280
- // 在文件最上方添加构建信息注释
281
- const buildInfoComment = `/*
282
- * Build Info:
283
- * Branch: ${gitBranch}
284
- * Device: ${deviceInfo.hostname} (MAC: ${deviceInfo.macAddress}, IP: ${deviceInfo.localIP})
285
- * Build Time: ${buildTime}
286
- */
287
- `;
288
-
289
- // 如果文件开头已经有注释,需要判断是否已有构建信息注释
290
- // 简单处理:直接在最前面添加
291
- distContent = buildInfoComment + distContent;
292
-
293
207
  // 准备发布数据
294
208
  logInfo('正在准备发布数据...');
295
209
  const pushData = {
@@ -297,8 +211,8 @@ module.exports = version => {
297
211
  cmd_version: cmdVersion,
298
212
  hostname: os.hostname(),
299
213
  platform: os.platform(),
300
- package_version: formattedVersion || 0,
301
- package_content: distContent,
214
+ package_version: version || 0,
215
+ package_content: readFile(filePaths.distPath),
302
216
  readme_content: readFile(filePaths.readmePath)
303
217
  };
304
218
 
package/src/utils.js CHANGED
@@ -2,8 +2,6 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const child_process = require('child_process');
4
4
  const chalk = require('chalk');
5
- const axios = require('axios');
6
- const { getApiHost } = require('./config');
7
5
 
8
6
  // 常量定义
9
7
  const CONSTANTS = {
@@ -209,230 +207,6 @@ function CopyFolder(srcDir, tarDir, cb) {
209
207
  }
210
208
 
211
209
 
212
- /**
213
- * 获取Git当前分支名
214
- * @param {string} rootPath - 项目根路径
215
- * @returns {string} 分支名,如果获取失败返回'unknown'
216
- */
217
- function getGitBranch(rootPath) {
218
- try {
219
- const result = executeCommand('git rev-parse --abbrev-ref HEAD', rootPath, { encoding: 'utf8' });
220
- return result.toString().trim() || 'unknown';
221
- } catch (error) {
222
- return 'unknown';
223
- }
224
- }
225
-
226
- /**
227
- * 获取MAC地址
228
- * @returns {string} MAC地址
229
- */
230
- function getMacAddress() {
231
- const os = require('os');
232
- const networkInterfaces = os.networkInterfaces();
233
-
234
- for (const interfaceName in networkInterfaces) {
235
- const interfaces = networkInterfaces[interfaceName];
236
- for (const iface of interfaces) {
237
- // 跳过内部(即127.0.0.1)和非IPv4地址
238
- if (!iface.internal && iface.family === 'IPv4') {
239
- return iface.mac || 'unknown';
240
- }
241
- }
242
- }
243
- return 'unknown';
244
- }
245
-
246
- /**
247
- * 获取内网IP地址
248
- * @returns {string} 内网IP地址
249
- */
250
- function getLocalIP() {
251
- const os = require('os');
252
- const networkInterfaces = os.networkInterfaces();
253
-
254
- for (const interfaceName in networkInterfaces) {
255
- const interfaces = networkInterfaces[interfaceName];
256
- for (const iface of interfaces) {
257
- // 跳过内部(即127.0.0.1)和非IPv4地址
258
- if (!iface.internal && iface.family === 'IPv4') {
259
- return iface.address || 'unknown';
260
- }
261
- }
262
- }
263
- return 'unknown';
264
- }
265
-
266
- /**
267
- * 获取设备信息
268
- * @returns {Object} 设备信息对象
269
- */
270
- function getDeviceInfo() {
271
- const os = require('os');
272
- return {
273
- hostname: os.hostname(),
274
- macAddress: getMacAddress(),
275
- localIP: getLocalIP()
276
- };
277
- }
278
-
279
- /**
280
- * 标准化文本(去除空白字符、换行符等)
281
- * @param {string} text - 原始文本
282
- * @returns {string} 标准化后的文本
283
- */
284
- function normalizeText(text) {
285
- if (!text) return '';
286
- return text
287
- .replace(/\s+/g, ' ') // 将所有空白字符(包括换行、制表符等)替换为单个空格
288
- .replace(/\n/g, ' ') // 替换换行符
289
- .replace(/\r/g, '') // 替换回车符
290
- .replace(/\t/g, ' ') // 替换制表符
291
- .trim(); // 去除首尾空格
292
- }
293
-
294
- /**
295
- * 计算两个文本的相似度(基于字符匹配)
296
- * @param {string} text1 - 文本1
297
- * @param {string} text2 - 文本2
298
- * @returns {number} 相似度百分比(0-100)
299
- */
300
- function calculateSimilarity(text1, text2) {
301
- if (!text1 || !text2) {
302
- return 0;
303
- }
304
-
305
- // 标准化文本
306
- const normalized1 = normalizeText(text1);
307
- const normalized2 = normalizeText(text2);
308
-
309
- if (normalized1 === normalized2) {
310
- return 100;
311
- }
312
-
313
- // 使用简单的字符匹配算法
314
- // 计算相同字符的数量
315
- const len1 = normalized1.length;
316
- const len2 = normalized2.length;
317
-
318
- if (len1 === 0 && len2 === 0) {
319
- return 100;
320
- }
321
-
322
- if (len1 === 0 || len2 === 0) {
323
- return 0;
324
- }
325
-
326
- // 使用最长公共子序列(LCS)的简化版本
327
- // 这里使用更简单的字符匹配算法
328
- let matches = 0;
329
- const minLen = Math.min(len1, len2);
330
- const maxLen = Math.max(len1, len2);
331
-
332
- // 计算相同位置的相同字符数
333
- for (let i = 0; i < minLen; i++) {
334
- if (normalized1[i] === normalized2[i]) {
335
- matches++;
336
- }
337
- }
338
-
339
- // 计算相似度百分比
340
- const similarity = (matches / maxLen) * 100;
341
-
342
- // 如果相似度很高,使用更精确的算法
343
- if (similarity > 80) {
344
- // 使用编辑距离算法(Levenshtein距离)的简化版本
345
- return calculateLevenshteinSimilarity(normalized1, normalized2);
346
- }
347
-
348
- return similarity;
349
- }
350
-
351
- /**
352
- * 使用Levenshtein距离计算相似度
353
- * @param {string} str1 - 字符串1
354
- * @param {string} str2 - 字符串2
355
- * @returns {number} 相似度百分比(0-100)
356
- */
357
- function calculateLevenshteinSimilarity(str1, str2) {
358
- const len1 = str1.length;
359
- const len2 = str2.length;
360
-
361
- if (len1 === 0) return len2 === 0 ? 100 : 0;
362
- if (len2 === 0) return 0;
363
-
364
- // 创建矩阵
365
- const matrix = [];
366
- for (let i = 0; i <= len1; i++) {
367
- matrix[i] = [i];
368
- }
369
- for (let j = 0; j <= len2; j++) {
370
- matrix[0][j] = j;
371
- }
372
-
373
- // 填充矩阵
374
- for (let i = 1; i <= len1; i++) {
375
- for (let j = 1; j <= len2; j++) {
376
- if (str1[i - 1] === str2[j - 1]) {
377
- matrix[i][j] = matrix[i - 1][j - 1];
378
- } else {
379
- matrix[i][j] = Math.min(
380
- matrix[i - 1][j - 1] + 1, // 替换
381
- matrix[i][j - 1] + 1, // 插入
382
- matrix[i - 1][j] + 1 // 删除
383
- );
384
- }
385
- }
386
- }
387
-
388
- // 计算相似度
389
- const distance = matrix[len1][len2];
390
- const maxLen = Math.max(len1, len2);
391
- const similarity = ((maxLen - distance) / maxLen) * 100;
392
-
393
- return Math.max(0, Math.min(100, similarity));
394
- }
395
-
396
- /**
397
- * 检查jjb-cmd版本是否为最新版本
398
- * @returns {Promise<boolean>} 是否为最新版本
399
- */
400
- async function checkCmdVersion() {
401
- try {
402
- const packageJsonPath = path.join(__dirname, '../package.json');
403
- const packageJson = JSON.parse(readFile(packageJsonPath));
404
- const currentVersion = packageJson.version;
405
-
406
- const apiHost = getApiHost();
407
- const response = await axios.get(`${apiHost}/api/cmd/version`, {
408
- timeout: 5000
409
- });
410
-
411
- if (response.data && response.data.status && response.data.data) {
412
- const latestVersion = response.data.data.version;
413
-
414
- // 版本号比较:将版本号转换为数字进行比较
415
- const currentVersionNum = parseInt(currentVersion.replace(/[.]/g, ''));
416
- const latestVersionNum = parseInt(latestVersion.replace(/[.]/g, ''));
417
-
418
- if (currentVersionNum < latestVersionNum) {
419
- logError(`当前版本 v${currentVersion} 不是最新版本,最新版本为 v${latestVersion}`);
420
- logInfo('请执行以下命令更新到最新版本:');
421
- logInfo('npm install jjb-cmd -g');
422
- return false;
423
- }
424
- return true;
425
- }
426
- // 如果API返回异常,允许继续执行(避免因网络问题阻塞)
427
- logWarning('无法获取服务器最新版本信息,将继续执行命令');
428
- return true;
429
- } catch (error) {
430
- // 网络错误或其他错误时,允许继续执行
431
- logWarning('版本检查失败,将继续执行命令');
432
- return true;
433
- }
434
- }
435
-
436
210
  module.exports = {
437
211
  CONSTANTS,
438
212
  logInfo,
@@ -450,9 +224,5 @@ module.exports = {
450
224
  validateEnvironment,
451
225
  DeleteDirAllFile,
452
226
  CopyFile,
453
- CopyFolder,
454
- checkCmdVersion,
455
- getGitBranch,
456
- getDeviceInfo,
457
- calculateSimilarity
227
+ CopyFolder
458
228
  };
package/DEFAULT_README.md DELETED
@@ -1,203 +0,0 @@
1
- # 云组件开发指南
2
-
3
- ## 🛠 技术栈
4
-
5
- | 类别 | 技术选型 |
6
- |------|----------|
7
- | **语言** | JavaScript (ES6+) |
8
- | **框架** | React 18 |
9
- | **样式** | Less |
10
- | **UI组件库** | Ant Design |
11
- | **编译工具** | Babel 7 |
12
- | **构建工具** | Webpack 5 |
13
-
14
- ## 📁 目录结构
15
-
16
- ```
17
- .
18
- ├── jjb.script/ # Webpack开发与构建配置(不建议修改)
19
- ├── public/ # 本地开发调试HTML
20
- ├── src/ # 源码目录
21
- ├── jjb.config.json # 项目基础配置
22
- ├── package.json # 项目依赖配置
23
- ├── thumbnail.png # 组件缩略图
24
- └── webstorm.config.js # WebStorm路径别名配置(其他编辑器可忽略)
25
- ```
26
-
27
- ## 🚀 开发说明
28
-
29
- ### 基本信息配置
30
- - **组件名称**:修改 `package.json` 中的 `name` 属性(必须唯一)
31
- - **检索标题**:修改 `package.json` 中的 `description` 属性
32
- - **描述说明**:修改 `package.json` 中的 `remark` 属性
33
- - **版本号**:修改 `package.json` 中的 `version` 属性
34
- - **缩略图**:在根目录添加 `thumbnail.png` 文件
35
-
36
- ### 入口文件
37
- - `__dev__.js` - 开发环境入口
38
- - `__prod__.js` - 生产环境构建入口
39
-
40
- ## ⚙️ jjb.config.json 配置详解
41
-
42
- ### 1. environment
43
- 配置云组件的接口服务环境,与 `package.json` 中 scripts 的 `NODE_ENV` 匹配。云组件运行时,接口服务域名会与应用保持一致。
44
-
45
- ### 2. dependencies
46
- 配置云组件的依赖清单。如果应用中没有提供匹配的依赖项,运行时将报错。
47
-
48
- 云组件默认从 `window.__coreLib` 获取依赖项,如果应用使用其他变量,需要显式指定:
49
-
50
- ```javascript
51
- // 应用入口文件中声明依赖
52
- // 默认依赖变量
53
- window.__coreLib = {
54
- 'react': require('react'),
55
- 'react-dom': require('react-dom')
56
- };
57
-
58
- // 其他依赖变量
59
- window.__coreTestLib = {
60
- 'react': require('react'),
61
- 'react-dom': require('react-dom')
62
- };
63
- ```
64
-
65
- ```jsx
66
- // 使用组件时指定依赖变量
67
- function App() {
68
- return (
69
- <>
70
- {/* 使用默认依赖变量 */}
71
- <CloudComponent />
72
-
73
- {/* 使用其他依赖变量 */}
74
- <CloudComponent lib="__coreTestLib" />
75
- </>
76
- );
77
- }
78
- ```
79
-
80
- ### 3. server
81
-
82
- 配置云组件本地开发服务。
83
-
84
- ### 4. framework
85
-
86
- 配置UI库主题,可根据业务需求定制颜色和其他样式。
87
-
88
- ## 💻 本地开发
89
-
90
- ### 安装依赖
91
-
92
- ```bash
93
- npm install
94
- ```
95
-
96
- ### 启动开发服务
97
-
98
- ```bash
99
- npm run serve:<env>
100
- ```
101
-
102
- ## 本地联调
103
-
104
- ### 1. 构建云组件
105
- ```bash
106
- npm run build:<env>
107
- ```
108
- ### 2. 启动代理服务
109
- ```bash
110
- node jjb.script/proxy.js
111
- ```
112
- ### 3. 在应用中使用
113
- 访问 http://127.0.0.1:8080/index.js 可查看编译后的代码,将此链接放入应用的云组件中:
114
- ```jsx
115
- function App() {
116
- return (
117
- <CloudComponent from="http://127.0.0.1:8080/index.js" />
118
- );
119
- }
120
- ```
121
- 注意:云组件更新后需要重新执行第一步编译。
122
-
123
- ## 📤 发布云组件
124
-
125
- ### 自动发布(推荐)
126
- 1. 安装命令行工具
127
- ```bash
128
- npm install jjb-cmd -g
129
- ```
130
- 2. 账号认证
131
- ```bash
132
- jjb-cmd auth <用户名> <密码>
133
- ```
134
- 账号密码请联系管理员获取
135
- 3. 发布组件
136
- ```bash
137
- jjb-cmd publish <版本号 | latest>
138
- ```
139
- 注意:版本号不要加 v 前缀!
140
-
141
- ### 👀 查看已发布组件
142
- * 云组件平台:https://jcloud.cqjjb.cn/container/home?pageNum=1&pageSize=10
143
- * 云组件检索表:https://cdn.cqjjb.cn
144
-
145
-
146
- ### 手动发布
147
-
148
- 1. 构建组件
149
- ```bash
150
- npm run build:<env>
151
- ```
152
- 2. 部署文件 将 dist/index.js 上传到文件服务器,并确保允许跨域访问。
153
-
154
- ## 📦 在应用中使用
155
-
156
- ### 安装依赖
157
-
158
- ```bash
159
- # 使用 npm
160
- npm install @cqsjjb/jjb-cloud-component
161
-
162
- # 使用 yarn
163
- yarn add @cqsjjb/jjb-cloud-component
164
- ```
165
-
166
- ### 基本用法
167
-
168
- ```jsx
169
- import React from 'react';
170
- import { CloudComponent } from '@cqsjjb/jjb-cloud-component';
171
-
172
- function App() {
173
- return (
174
- <CloudComponent
175
- // 依赖变量(默认使用 window.__coreLib)
176
- lib="__coreLib"
177
-
178
- // 云组件地址(从云组件平台获取)
179
- from="https://your-component-url/index.js"
180
-
181
- // 缓存策略
182
- cache={true}
183
-
184
- // 组件唯一标识
185
- componentKey="your-component-unique-key"
186
-
187
- // 组件属性
188
- componentProps={{}}
189
-
190
- // 事件回调
191
- onLoadStart={() => console.log('开始加载')}
192
- onLoadEnd={() => console.log('加载完成')}
193
- onMounted={() => console.log('挂载完成')}
194
- onUpdated={() => console.log('数据更新')}
195
- onDestroy={() => console.log('组件销毁')}
196
- />
197
- );
198
- }
199
- ```
200
-
201
- ---
202
-
203
- 💡 提示:更多详细配置请参考组件类型定义文件(d.ts)
package/src/push-npm.js DELETED
@@ -1,199 +0,0 @@
1
- const path = require('path');
2
- const fs = require('fs');
3
- const child_process = require('child_process');
4
- const axios = require('axios');
5
- const {
6
- logInfo,
7
- logSuccess,
8
- logError,
9
- logWarning,
10
- readFile,
11
- writeFile,
12
- executeCommand,
13
- fileExists
14
- } = require('./utils');
15
- const { getApiHost } = require('./config');
16
- const { loadAuth } = require('./crypto-utils');
17
-
18
- /**
19
- * 执行npm发布流程
20
- * @param {string} version - 版本号
21
- * @param {string} tag - npm tag(可选,默认为latest)
22
- */
23
- module.exports = async (version, tag) => {
24
- if (!version) {
25
- logError('请提供版本号,例如: jjb-cmd push 2.5.7 或 jjb-cmd push 2.5.7 --tag beta');
26
- process.exit(1);
27
- }
28
-
29
- // 如果没有指定tag,默认为latest
30
- const npmTag = tag || 'latest';
31
-
32
- const rootPath = path.resolve(__dirname, '..');
33
- const packageJsonPath = path.join(rootPath, 'package.json');
34
-
35
- // 读取当前package.json
36
- let packageJson;
37
- try {
38
- const packageJsonContent = readFile(packageJsonPath);
39
- packageJson = JSON.parse(packageJsonContent);
40
- } catch (error) {
41
- logError(`读取package.json失败: ${error.message}`);
42
- process.exit(1);
43
- }
44
-
45
- const currentVersion = packageJson.version;
46
- logInfo(`当前版本: ${currentVersion}`);
47
- logInfo(`目标版本: ${version}`);
48
-
49
- // 更新package.json中的版本号
50
- try {
51
- packageJson.version = version;
52
- writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
53
- logSuccess(`已更新package.json版本号为: ${version}`);
54
- } catch (error) {
55
- logError(`更新package.json失败: ${error.message}`);
56
- process.exit(1);
57
- }
58
-
59
- // 保存原始npm registry配置
60
- let originalRegistry = 'https://registry.npmmirror.com/';
61
- try {
62
- const registryResult = child_process.execSync('npm config get registry', { encoding: 'utf8', cwd: rootPath });
63
- originalRegistry = registryResult.trim() || 'https://registry.npmmirror.com/';
64
- } catch (error) {
65
- logWarning('无法获取当前npm registry配置,将使用默认值');
66
- }
67
-
68
- try {
69
- // 1. 切换npm源到官方源
70
- logInfo('正在切换npm源到官方源...');
71
- executeCommand('npm config set registry https://registry.npmjs.org/', rootPath);
72
- logSuccess('npm源已切换到官方源');
73
-
74
- // 检查是否已登录npm(针对官方源)
75
- logInfo('正在检查npm登录状态...');
76
- try {
77
- const whoamiResult = child_process.execSync('npm whoami --registry=https://registry.npmjs.org/', { encoding: 'utf8', cwd: rootPath, stdio: 'pipe' });
78
- const npmUser = whoamiResult.trim();
79
- if (npmUser) {
80
- logSuccess(`已登录npm账号: ${npmUser}`);
81
- } else {
82
- logError('未登录npm官方源,请先执行: npm login --registry=https://registry.npmjs.org/');
83
- throw new Error('需要先登录npm官方源才能发布');
84
- }
85
- } catch (error) {
86
- if (error.message.includes('需要先登录')) {
87
- throw error;
88
- }
89
- logError('未登录npm官方源,请先执行: npm login --registry=https://registry.npmjs.org/');
90
- logError('或者执行: npm adduser --registry=https://registry.npmjs.org/');
91
- throw new Error('需要先登录npm官方源才能发布');
92
- }
93
-
94
- // 2. 发布到npm
95
- // 注意:npm publish 不需要在命令中指定版本号,版本号已经从package.json中读取
96
- // 但需要明确指定registry,确保使用官方源
97
- logInfo(`正在发布版本 ${version} 到npm(tag: ${npmTag})...`);
98
- logInfo(`执行命令: npm publish --tag ${npmTag} --registry=https://registry.npmjs.org/(版本号 ${version} 已从package.json读取)`);
99
- try {
100
- const publishCommand = npmTag === 'latest'
101
- ? 'npm publish --registry=https://registry.npmjs.org/'
102
- : `npm publish --tag ${npmTag} --registry=https://registry.npmjs.org/`;
103
- executeCommand(publishCommand, rootPath);
104
- logSuccess(`版本 ${version} 发布成功(tag: ${npmTag})!`);
105
- } catch (error) {
106
- // 如果发布失败,恢复package.json版本号
107
- packageJson.version = currentVersion;
108
- writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
109
- logError(`npm发布失败: ${error.message}`);
110
- throw error;
111
- }
112
-
113
- // 3. 切回镜像源
114
- logInfo('正在切回npm镜像源...');
115
- executeCommand('npm config set registry https://registry.npmmirror.com/', rootPath);
116
- logSuccess('npm源已切回镜像源');
117
-
118
- // 4. 更新数据库中的版本号
119
- logInfo('正在更新服务器端版本号...');
120
- try {
121
- const authPath = path.join(rootPath, '.auth');
122
- let username, password;
123
-
124
- // 尝试从认证文件读取
125
- if (fileExists(authPath)) {
126
- try {
127
- const authData = loadAuth(authPath);
128
- username = authData.username;
129
- password = authData.password;
130
- } catch (error) {
131
- logWarning(`读取认证信息失败: ${error.message}`);
132
- logWarning('跳过服务器端版本号更新,请手动执行 jjb-cmd update-server-version');
133
- logSuccess('npm发布完成!');
134
- process.exit(0);
135
- }
136
- } else {
137
- logWarning('未找到认证信息,跳过服务器端版本号更新');
138
- logWarning('请手动执行 jjb-cmd update-server-version 来更新服务器端版本号');
139
- logSuccess('npm发布完成!');
140
- process.exit(0);
141
- }
142
-
143
- // 调用服务器接口更新版本号
144
- const apiHost = getApiHost();
145
- const response = await axios.post(`${apiHost}/api/cmd/update-version`, {
146
- username,
147
- password
148
- }, {
149
- timeout: 15000
150
- });
151
-
152
- if (response.data && response.data.status) {
153
- const { version: updatedVersion, previousVersion } = response.data.data;
154
- logSuccess('服务器端版本号更新成功!');
155
- if (previousVersion) {
156
- logInfo(` 旧版本: ${previousVersion}`);
157
- }
158
- logInfo(` 新版本: ${updatedVersion}`);
159
- } else {
160
- logWarning(`服务器端版本号更新失败: ${response.data.message || '未知错误'}`);
161
- logWarning('请手动执行 jjb-cmd update-server-version 来更新服务器端版本号');
162
- }
163
- } catch (error) {
164
- if (error && error.response) {
165
- const status = error.response.status;
166
- const serverMsg = (error.response.data && (error.response.data.message || error.response.data.msg)) || '';
167
- logWarning(`服务器端版本号更新失败 (${status}): ${serverMsg || error.message}`);
168
- } else if (error && error.request) {
169
- logWarning('网络连接失败: 无法连接到服务器');
170
- } else {
171
- logWarning(`服务器端版本号更新失败: ${error.message || '未知错误'}`);
172
- }
173
- logWarning('请手动执行 jjb-cmd update-server-version 来更新服务器端版本号');
174
- }
175
-
176
- logSuccess('所有操作完成!');
177
- process.exit(0);
178
- } catch (error) {
179
- // 发生错误时,尝试恢复npm源
180
- try {
181
- logInfo('正在恢复npm源...');
182
- executeCommand(`npm config set registry ${originalRegistry}`, rootPath);
183
- logInfo('npm源已恢复');
184
- } catch (restoreError) {
185
- logError(`恢复npm源失败: ${restoreError.message}`);
186
- }
187
-
188
- // 恢复package.json版本号
189
- try {
190
- packageJson.version = currentVersion;
191
- writeFile(packageJsonPath, JSON.stringify(packageJson, null, 2) + '\n');
192
- logInfo('已恢复package.json版本号');
193
- } catch (restoreError) {
194
- logError(`恢复package.json失败: ${restoreError.message}`);
195
- }
196
-
197
- process.exit(1);
198
- }
199
- };