@coze-arch/cli 0.0.1-alpha.a1ca15 → 0.0.1-alpha.a37c60

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.
Files changed (33) hide show
  1. package/lib/__templates__/expo/.coze +7 -2
  2. package/lib/__templates__/expo/.cozeproj/scripts/{deploy_build.sh → dev_build.sh} +4 -10
  3. package/lib/__templates__/expo/.cozeproj/scripts/{deploy_run.sh → dev_run.sh} +15 -51
  4. package/lib/__templates__/expo/.cozeproj/scripts/prod_build.sh +47 -0
  5. package/lib/__templates__/expo/.cozeproj/scripts/prod_run.sh +35 -0
  6. package/lib/__templates__/expo/babel.config.js +10 -0
  7. package/lib/__templates__/expo/client/components/ThemedText.tsx +33 -0
  8. package/lib/__templates__/expo/client/components/ThemedView.tsx +38 -0
  9. package/lib/__templates__/expo/client/constants/theme.ts +779 -47
  10. package/lib/__templates__/expo/client/contexts/AuthContext.tsx +14 -107
  11. package/lib/__templates__/expo/client/hooks/useTheme.ts +1 -1
  12. package/lib/__templates__/expo/client/screens/home/index.tsx +1 -4
  13. package/lib/__templates__/expo/client/screens/home/styles.ts +1 -273
  14. package/lib/__templates__/expo/client/utils/index.ts +1 -2
  15. package/lib/__templates__/expo/metro.config.js +76 -8
  16. package/lib/__templates__/expo/package.json +15 -6
  17. package/lib/__templates__/expo/pnpm-lock.yaml +71 -516
  18. package/lib/__templates__/expo/src/index.ts +2 -2
  19. package/lib/__templates__/expo/template.config.js +1 -1
  20. package/lib/__templates__/nextjs/next.config.ts +1 -0
  21. package/lib/__templates__/nextjs/package.json +1 -1
  22. package/lib/__templates__/nextjs/pnpm-lock.yaml +5 -5
  23. package/lib/__templates__/nextjs/scripts/dev.sh +7 -26
  24. package/lib/__templates__/nextjs/src/app/globals.css +99 -87
  25. package/lib/__templates__/nextjs/src/app/layout.tsx +18 -18
  26. package/lib/__templates__/templates.json +34 -0
  27. package/lib/__templates__/vite/package.json +1 -1
  28. package/lib/__templates__/vite/pnpm-lock.yaml +120 -120
  29. package/lib/__templates__/vite/scripts/dev.sh +7 -26
  30. package/lib/__templates__/vite/template.config.js +11 -2
  31. package/lib/__templates__/vite/vite.config.ts +3 -3
  32. package/lib/cli.js +214 -79
  33. package/package.json +8 -4
package/lib/cli.js CHANGED
@@ -9,6 +9,8 @@ var perf_hooks = require('perf_hooks');
9
9
  var fs$1 = require('fs/promises');
10
10
  var toml = require('@iarna/toml');
11
11
  var jsYaml = require('js-yaml');
12
+ var child_process = require('child_process');
13
+ var os = require('os');
12
14
  var addFormats = require('ajv-formats');
13
15
  var Ajv = require('ajv');
14
16
  var minimist = require('minimist');
@@ -125,7 +127,7 @@ const generateTemplatesHelpText = () => {
125
127
  return lines.join('\n');
126
128
  };
127
129
 
128
- function _nullishCoalesce$2(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain$4(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var LogLevel; (function (LogLevel) {
130
+ function _nullishCoalesce$2(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain$3(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var LogLevel; (function (LogLevel) {
129
131
  const ERROR = 0; LogLevel[LogLevel["ERROR"] = ERROR] = "ERROR";
130
132
  const WARN = 1; LogLevel[LogLevel["WARN"] = WARN] = "WARN";
131
133
  const SUCCESS = 2; LogLevel[LogLevel["SUCCESS"] = SUCCESS] = "SUCCESS";
@@ -172,7 +174,7 @@ class Logger {
172
174
  return level;
173
175
  }
174
176
 
175
- const envLevel = _optionalChain$4([process, 'access', _ => _.env, 'access', _2 => _2.LOG_LEVEL, 'optionalAccess', _3 => _3.toLowerCase, 'call', _4 => _4()]);
177
+ const envLevel = _optionalChain$3([process, 'access', _ => _.env, 'access', _2 => _2.LOG_LEVEL, 'optionalAccess', _3 => _3.toLowerCase, 'call', _4 => _4()]);
176
178
  if (envLevel && envLevel in LOG_LEVEL_MAP) {
177
179
  return LOG_LEVEL_MAP[envLevel];
178
180
  }
@@ -184,7 +186,7 @@ class Logger {
184
186
  // 简单检测:Node.js 环境且支持 TTY
185
187
  return (
186
188
  typeof process !== 'undefined' &&
187
- _optionalChain$4([process, 'access', _5 => _5.stdout, 'optionalAccess', _6 => _6.isTTY]) === true &&
189
+ _optionalChain$3([process, 'access', _5 => _5.stdout, 'optionalAccess', _6 => _6.isTTY]) === true &&
188
190
  process.env.NO_COLOR === undefined
189
191
  );
190
192
  }
@@ -593,7 +595,7 @@ const registerCommand$2 = program => {
593
595
  });
594
596
  };
595
597
 
596
- function _optionalChain$3(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/* eslint-disable @typescript-eslint/no-explicit-any */
598
+ function _optionalChain$2(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/* eslint-disable @typescript-eslint/no-explicit-any */
597
599
  // Safe JSON parsing utilities with type safety and error handling
598
600
  // Provides fallback values, validation, and error monitoring capabilities
599
601
 
@@ -684,12 +686,12 @@ function safeJsonParse(
684
686
  const parsed = JSON.parse(String(input));
685
687
 
686
688
  // Optional validation
687
- if (_optionalChain$3([options, 'optionalAccess', _ => _.validate])) {
689
+ if (_optionalChain$2([options, 'optionalAccess', _ => _.validate])) {
688
690
  if (options.validate(parsed)) {
689
691
  return parsed;
690
692
  } else {
691
693
  const validationError = new Error('JSON validation failed');
692
- _optionalChain$3([options, 'access', _2 => _2.onError, 'optionalCall', _3 => _3(validationError, input)]);
694
+ _optionalChain$2([options, 'access', _2 => _2.onError, 'optionalCall', _3 => _3(validationError, input)]);
693
695
 
694
696
  if (options.throwOnValidationError) {
695
697
  throw validationError;
@@ -701,15 +703,15 @@ function safeJsonParse(
701
703
  return parsed;
702
704
  } catch (error) {
703
705
  // Re-throw validation errors when throwOnValidationError is true
704
- if (error instanceof Error && error.message === 'JSON validation failed' && _optionalChain$3([options, 'optionalAccess', _4 => _4.throwOnValidationError])) {
706
+ if (error instanceof Error && error.message === 'JSON validation failed' && _optionalChain$2([options, 'optionalAccess', _4 => _4.throwOnValidationError])) {
705
707
  throw error;
706
708
  }
707
- _optionalChain$3([options, 'optionalAccess', _5 => _5.onError, 'optionalCall', _6 => _6(error , input)]);
709
+ _optionalChain$2([options, 'optionalAccess', _5 => _5.onError, 'optionalCall', _6 => _6(error , input)]);
708
710
  return defaultValue;
709
711
  }
710
712
  }
711
713
 
712
- function _optionalChain$2(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
714
+ function _optionalChain$1(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
713
715
 
714
716
 
715
717
  /**
@@ -799,13 +801,13 @@ const getCommandConfig = (
799
801
  // 根据命令名称映射到配置路径
800
802
  switch (commandName) {
801
803
  case 'dev':
802
- commandConfig = _optionalChain$2([config, 'access', _ => _.dev, 'optionalAccess', _2 => _2.run]);
804
+ commandConfig = _optionalChain$1([config, 'access', _ => _.dev, 'optionalAccess', _2 => _2.run]);
803
805
  break;
804
806
  case 'build':
805
- commandConfig = _optionalChain$2([config, 'access', _3 => _3.deploy, 'optionalAccess', _4 => _4.build]);
807
+ commandConfig = _optionalChain$1([config, 'access', _3 => _3.deploy, 'optionalAccess', _4 => _4.build]);
806
808
  break;
807
809
  case 'start':
808
- commandConfig = _optionalChain$2([config, 'access', _5 => _5.deploy, 'optionalAccess', _6 => _6.run]);
810
+ commandConfig = _optionalChain$1([config, 'access', _5 => _5.deploy, 'optionalAccess', _6 => _6.run]);
809
811
  break;
810
812
  default:
811
813
  throw new Error(`Unknown command: ${commandName}`);
@@ -821,7 +823,7 @@ const getCommandConfig = (
821
823
  return commandConfig;
822
824
  };
823
825
 
824
- function _nullishCoalesce$1(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain$1(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
826
+ function _nullishCoalesce$1(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
825
827
 
826
828
  /**
827
829
  * 创建日志管理器
@@ -879,12 +881,12 @@ const executeRun = async (
879
881
  }
880
882
 
881
883
  // 将输出同时写入控制台和日志文件
882
- _optionalChain$1([childProcess, 'access', _ => _.stdout, 'optionalAccess', _2 => _2.on, 'call', _3 => _3('data', (data) => {
884
+ _optionalChain([childProcess, 'access', _ => _.stdout, 'optionalAccess', _2 => _2.on, 'call', _3 => _3('data', (data) => {
883
885
  process.stdout.write(data);
884
886
  logStream.write(data);
885
887
  })]);
886
888
 
887
- _optionalChain$1([childProcess, 'access', _4 => _4.stderr, 'optionalAccess', _5 => _5.on, 'call', _6 => _6('data', (data) => {
889
+ _optionalChain([childProcess, 'access', _4 => _4.stderr, 'optionalAccess', _5 => _5.on, 'call', _6 => _6('data', (data) => {
888
890
  process.stderr.write(data);
889
891
  logStream.write(data);
890
892
  })]);
@@ -952,6 +954,45 @@ const registerCommand$1 = program => {
952
954
  });
953
955
  };
954
956
 
957
+ /**
958
+ * 在后台启动一个独立的子进程
959
+ * 类似于 `setsid command args >/dev/null 2>&1 &`
960
+ *
961
+ * @param command - 要执行的命令 (例如: 'npm', 'node', 'bash')
962
+ * @param args - 命令参数数组 (例如: ['run', 'dev'])
963
+ * @param options - 配置选项
964
+ * @returns 子进程的 PID
965
+ */
966
+ function spawnDetached(
967
+ command,
968
+ args,
969
+ options,
970
+ ) {
971
+ const { cwd, verbose = true } = options;
972
+ const isWindows = os.platform() === 'win32';
973
+
974
+ if (verbose) {
975
+ console.log(`Spawning detached process: ${command} ${args.join(' ')}`);
976
+ console.log(`Working directory: ${cwd}`);
977
+ }
978
+
979
+ // 使用 spawn 创建后台子进程
980
+ const child = child_process.spawn(command, args, {
981
+ cwd,
982
+ detached: !isWindows, // Windows 不完全支持 detached,但仍可以使用
983
+ stdio: 'ignore', // 忽略所有输入输出,让进程完全独立运行
984
+ });
985
+
986
+ // 分离父子进程引用,允许父进程退出而不等待子进程
987
+ child.unref();
988
+
989
+ if (verbose && child.pid) {
990
+ console.log(`Process started with PID: ${child.pid}`);
991
+ }
992
+
993
+ return child.pid;
994
+ }
995
+
955
996
  /**
956
997
  * 创建 AJV 验证器实例
957
998
  */
@@ -1207,17 +1248,139 @@ const convertDotfileName = (filePath) => {
1207
1248
  };
1208
1249
 
1209
1250
  /**
1210
- * 复制并处理模板文件到目标目录
1251
+ * 执行文件渲染钩子
1211
1252
  *
1212
- * @param templatePath - 模板目录路径
1213
- * @param outputPath - 输出目录路径
1253
+ * @param templateConfig - 模板配置
1254
+ * @param fileInfo - 文件渲染信息
1214
1255
  * @param context - 模板上下文
1256
+ * @returns 处理后的文件信息,或 null 表示跳过该文件
1215
1257
  */
1216
- const processTemplateFiles = async (
1217
- templatePath,
1218
- outputPath,
1258
+ const executeFileRenderHook = async (
1259
+ templateConfig,
1260
+ fileInfo,
1219
1261
  context,
1220
1262
  ) => {
1263
+ if (!templateConfig.onFileRender) {
1264
+ return fileInfo;
1265
+ }
1266
+
1267
+ const result = await templateConfig.onFileRender(
1268
+ fileInfo,
1269
+ context,
1270
+ );
1271
+
1272
+ // false: 跳过文件
1273
+ if (result === false) {
1274
+ return null;
1275
+ }
1276
+
1277
+ // undefined/void: 使用默认内容
1278
+ if (result === undefined || result === null) {
1279
+ return fileInfo;
1280
+ }
1281
+
1282
+ // string: 作为 content,其他不变
1283
+ if (typeof result === 'string') {
1284
+ return {
1285
+ ...fileInfo,
1286
+ content: result,
1287
+ };
1288
+ }
1289
+
1290
+ // FileRenderInfo: 使用新对象的信息
1291
+ return result;
1292
+ };
1293
+
1294
+ /**
1295
+ * 处理单个文件
1296
+ */
1297
+ const processSingleFile = async (options
1298
+
1299
+
1300
+
1301
+
1302
+
1303
+ ) => {
1304
+ const { file, templatePath, outputPath, context, templateConfig } = options;
1305
+
1306
+ const srcPath = path.join(templatePath, file);
1307
+ const destFile = convertDotfileName(file);
1308
+
1309
+ logger.verbose(
1310
+ ` - Processing: ${file}${destFile !== file ? ` -> ${destFile}` : ''}`,
1311
+ );
1312
+
1313
+ // 判断是否为二进制文件
1314
+ const isBinary = !shouldRenderFile(srcPath);
1315
+ let content;
1316
+ let wasRendered = false;
1317
+
1318
+ if (isBinary) {
1319
+ // 二进制文件,读取为 buffer 然后转为 base64
1320
+ const buffer = await fs$1.readFile(srcPath);
1321
+ content = buffer.toString('base64');
1322
+ } else {
1323
+ // 文本文件,渲染后的内容
1324
+ content = await renderTemplate(srcPath, context);
1325
+ wasRendered = true;
1326
+ }
1327
+
1328
+ // 构造文件信息对象
1329
+ const fileInfo = {
1330
+ path: file,
1331
+ destPath: destFile,
1332
+ content,
1333
+ isBinary,
1334
+ wasRendered,
1335
+ };
1336
+
1337
+ // 执行文件渲染钩子
1338
+ const processedFileInfo = await executeFileRenderHook(
1339
+ templateConfig,
1340
+ fileInfo,
1341
+ context,
1342
+ );
1343
+
1344
+ // 如果返回 null,跳过该文件
1345
+ if (processedFileInfo === null) {
1346
+ logger.verbose(' ⊘ Skipped by onFileRender hook');
1347
+ return;
1348
+ }
1349
+
1350
+ // 使用处理后的目标路径
1351
+ const finalDestPath = path.join(outputPath, processedFileInfo.destPath);
1352
+
1353
+ // 确保目标目录存在
1354
+ await ensureDir(path.dirname(finalDestPath));
1355
+
1356
+ // 写入文件
1357
+ if (processedFileInfo.isBinary) {
1358
+ // 二进制文件:如果内容没变,直接复制;否则从 base64 解码写入
1359
+ if (processedFileInfo.content === content) {
1360
+ await fs$1.copyFile(srcPath, finalDestPath);
1361
+ logger.verbose(' ✓ Copied (binary)');
1362
+ } else {
1363
+ const buffer = Buffer.from(processedFileInfo.content, 'base64');
1364
+ await fs$1.writeFile(finalDestPath, buffer);
1365
+ logger.verbose(' ✓ Written (binary, modified by hook)');
1366
+ }
1367
+ } else {
1368
+ // 文本文件
1369
+ await fs$1.writeFile(finalDestPath, processedFileInfo.content, 'utf-8');
1370
+ logger.verbose(' ✓ Rendered and written');
1371
+ }
1372
+ };
1373
+
1374
+ /**
1375
+ * 复制并处理模板文件到目标目录
1376
+ */
1377
+ const processTemplateFiles = async (options
1378
+
1379
+
1380
+
1381
+
1382
+ ) => {
1383
+ const { templatePath, outputPath, context, templateConfig } = options;
1221
1384
  logger.verbose('Processing template files:');
1222
1385
  logger.verbose(` - Template path: ${templatePath}`);
1223
1386
  logger.verbose(` - Output path: ${outputPath}`);
@@ -1248,29 +1411,15 @@ const processTemplateFiles = async (
1248
1411
  }
1249
1412
 
1250
1413
  await Promise.all(
1251
- files.map(async file => {
1252
- const srcPath = path.join(templatePath, file);
1253
- const destFile = convertDotfileName(file);
1254
- const destPath = path.join(outputPath, destFile);
1255
-
1256
- logger.verbose(
1257
- ` - Processing: ${file}${destFile !== file ? ` -> ${destFile}` : ''}`,
1258
- );
1259
-
1260
- // 确保目标目录存在
1261
- await ensureDir(path.dirname(destPath));
1262
-
1263
- if (shouldRenderFile(srcPath)) {
1264
- // 渲染文本文件
1265
- const rendered = await renderTemplate(srcPath, context);
1266
- await fs$1.writeFile(destPath, rendered, 'utf-8');
1267
- logger.verbose(' ✓ Rendered and written');
1268
- } else {
1269
- // 直接复制二进制文件
1270
- await fs$1.copyFile(srcPath, destPath);
1271
- logger.verbose(' ✓ Copied');
1272
- }
1273
- }),
1414
+ files.map(file =>
1415
+ processSingleFile({
1416
+ file,
1417
+ templatePath,
1418
+ outputPath,
1419
+ context,
1420
+ templateConfig,
1421
+ }),
1422
+ ),
1274
1423
  );
1275
1424
 
1276
1425
  logger.verbose('✓ All files processed successfully');
@@ -1457,7 +1606,12 @@ const execute = async (
1457
1606
  const absoluteOutputPath = await prepareOutputDirectory(outputPath);
1458
1607
 
1459
1608
  // 6. 处理模板文件
1460
- await processTemplateFiles(templatePath, absoluteOutputPath, context);
1609
+ await processTemplateFiles({
1610
+ templatePath,
1611
+ outputPath: absoluteOutputPath,
1612
+ context,
1613
+ templateConfig,
1614
+ });
1461
1615
 
1462
1616
  // 7. 执行 onAfterRender 钩子
1463
1617
  await executeAfterRenderHook(templateConfig, context, absoluteOutputPath);
@@ -1465,7 +1619,7 @@ const execute = async (
1465
1619
  return absoluteOutputPath;
1466
1620
  };
1467
1621
 
1468
- function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }
1622
+ function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } }
1469
1623
  /**
1470
1624
  * 运行 pnpm install
1471
1625
  */
@@ -1568,45 +1722,26 @@ const runGitInit = (projectPath) => {
1568
1722
  };
1569
1723
 
1570
1724
  /**
1571
- * 运行开发服务器
1725
+ * 运行开发服务器(后台模式)
1726
+ * 启动后台子进程运行开发服务器,父进程可以直接退出
1572
1727
  */
1573
1728
  const runNpmDev = (projectPath) => {
1574
- logger.info('\nStarting development server...');
1729
+ logger.info('\nStarting development server in background...');
1575
1730
  logger.info(`Executing: npm run dev in ${projectPath}`);
1576
- logger.info('Press Ctrl+C to stop the server\n');
1577
1731
 
1578
- // 使用 async: true 异步执行,不阻塞进程
1579
- const child = shelljs.exec('npm run dev', {
1732
+ // 使用通用的后台执行函数启动开发服务器
1733
+ const pid = spawnDetached('npm', ['run', 'dev'], {
1580
1734
  cwd: projectPath,
1581
- async: true,
1582
- silent: true, // 手动处理输出以便显示详细信息
1735
+ verbose: false, // 不输出额外的进程信息,由 logger 统一处理
1583
1736
  });
1584
1737
 
1585
- if (child) {
1586
- // 输出 stdout
1587
- _optionalChain([child, 'access', _ => _.stdout, 'optionalAccess', _2 => _2.on, 'call', _3 => _3('data', (data) => {
1588
- process.stdout.write(data);
1589
- })]);
1590
-
1591
- // 输出 stderr
1592
- _optionalChain([child, 'access', _4 => _4.stderr, 'optionalAccess', _5 => _5.on, 'call', _6 => _6('data', (data) => {
1593
- process.stderr.write(data);
1594
- })]);
1595
-
1596
- // 监听错误
1597
- child.on('error', (error) => {
1598
- logger.error(`Failed to start dev server: ${error.message}`);
1599
- logger.error(`Error stack: ${error.stack}`);
1600
- });
1601
-
1602
- // 监听退出
1603
- child.on('exit', (code, signal) => {
1604
- if (code !== 0 && code !== null) {
1605
- logger.error(
1606
- `Dev server exited with code ${code}${signal ? ` and signal ${signal}` : ''}`,
1607
- );
1608
- }
1609
- });
1738
+ logger.success('Development server started in background!');
1739
+ if (pid) {
1740
+ logger.info(`Process ID: ${pid}`);
1741
+ logger.info(
1742
+ '\nThe dev server is running independently. You can close this terminal.',
1743
+ );
1744
+ logger.info(`To stop the server later, use: kill ${pid}`);
1610
1745
  }
1611
1746
  };
1612
1747
 
@@ -1719,7 +1854,7 @@ const registerCommand = program => {
1719
1854
  });
1720
1855
  };
1721
1856
 
1722
- var version = "0.0.1-alpha.a1ca15";
1857
+ var version = "0.0.1-alpha.a37c60";
1723
1858
  var packageJson = {
1724
1859
  version: version};
1725
1860
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@coze-arch/cli",
3
- "version": "0.0.1-alpha.a1ca15",
3
+ "version": "0.0.1-alpha.a37c60",
4
4
  "private": false,
5
5
  "description": "coze coding devtools cli",
6
6
  "license": "MIT",
@@ -19,13 +19,13 @@
19
19
  "scripts": {
20
20
  "prebuild": "tsx scripts/prebuild.ts",
21
21
  "build": "tsx scripts/build.ts",
22
- "generate-templates": "tsx scripts/generate-templates-config.ts",
22
+ "create": "tsx scripts/create-template.ts",
23
23
  "lint": "eslint ./ --cache",
24
24
  "postpublish": "bash scripts/sync-npmmirror.sh",
25
25
  "test": "vitest --run --passWithNoTests",
26
26
  "test:all": "bash scripts/test-coverage.sh",
27
27
  "test:cov": "vitest --run --passWithNoTests --coverage",
28
- "test:e2e": "bash scripts/e2e.sh",
28
+ "test:e2e": "NODE_ENV=test bash scripts/e2e.sh",
29
29
  "test:perf": "vitest bench --run --config vitest.perf.config.ts",
30
30
  "test:perf:compare": "bash scripts/compare-perf.sh",
31
31
  "test:perf:save": "bash scripts/run-perf-with-output.sh"
@@ -49,21 +49,25 @@
49
49
  "@coze-arch/ts-config": "workspace:*",
50
50
  "@coze-arch/vitest-config": "workspace:*",
51
51
  "@coze-coding/lambda": "workspace:*",
52
+ "@inquirer/prompts": "^3.2.0",
52
53
  "@types/ejs": "^3.1.5",
53
54
  "@types/iarna__toml": "^2.0.5",
54
55
  "@types/js-yaml": "^4.0.9",
56
+ "@types/minimatch": "^5.1.2",
55
57
  "@types/minimist": "^1.2.5",
56
58
  "@types/node": "^24",
57
59
  "@types/shelljs": "^0.10.0",
58
60
  "@vitest/coverage-v8": "~4.0.16",
59
61
  "json-schema-to-typescript": "^15.0.3",
62
+ "minimatch": "^10.0.1",
60
63
  "rollup": "^4.41.1",
61
64
  "sucrase": "^3.35.0",
62
65
  "tsx": "^4.20.6",
63
66
  "vitest": "~4.0.16"
64
67
  },
65
68
  "publishConfig": {
66
- "access": "public"
69
+ "access": "public",
70
+ "registry": "https://registry.npmjs.org"
67
71
  },
68
72
  "cozePublishConfig": {
69
73
  "bin": {