@coze-arch/cli 0.0.5-alpha.6339fe → 0.0.6

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 (25) hide show
  1. package/lib/__templates__/nuxt-vue/nuxt.config.ts +0 -13
  2. package/lib/__templates__/taro/components.md +1686 -0
  3. package/lib/__templates__/taro/eslint.config.mjs +8 -2
  4. package/lib/__templates__/taro/package.json +1 -1
  5. package/lib/__templates__/taro/pnpm-lock.yaml +5 -5
  6. package/lib/__templates__/taro/src/app.tsx +6 -1
  7. package/lib/__templates__/taro/src/components/ui/accordion.tsx +1 -1
  8. package/lib/__templates__/taro/src/components/ui/breadcrumb.tsx +1 -1
  9. package/lib/__templates__/taro/src/components/ui/calendar.tsx +4 -4
  10. package/lib/__templates__/taro/src/components/ui/carousel.tsx +2 -2
  11. package/lib/__templates__/taro/src/components/ui/command.tsx +1 -1
  12. package/lib/__templates__/taro/src/components/ui/context-menu.tsx +3 -3
  13. package/lib/__templates__/taro/src/components/ui/dialog.tsx +1 -1
  14. package/lib/__templates__/taro/src/components/ui/dropdown-menu.tsx +3 -3
  15. package/lib/__templates__/taro/src/components/ui/input-otp.tsx +1 -1
  16. package/lib/__templates__/taro/src/components/ui/menubar.tsx +3 -3
  17. package/lib/__templates__/taro/src/components/ui/navigation-menu.tsx +2 -0
  18. package/lib/__templates__/taro/src/components/ui/pagination.tsx +3 -3
  19. package/lib/__templates__/taro/src/components/ui/resizable.tsx +1 -1
  20. package/lib/__templates__/taro/src/components/ui/select.tsx +4 -4
  21. package/lib/__templates__/taro/src/components/ui/sheet.tsx +1 -1
  22. package/lib/__templates__/taro/src/components/ui/toast.tsx +1 -1
  23. package/lib/__templates__/taro/types/lucide.d.ts +6 -0
  24. package/lib/cli.js +122 -968
  25. package/package.json +1 -2
package/lib/cli.js CHANGED
@@ -15,7 +15,6 @@ var fs$1 = require('fs/promises');
15
15
  var os = require('os');
16
16
  var jsYaml = require('js-yaml');
17
17
  var toml = require('@iarna/toml');
18
- var fastGlob = require('fast-glob');
19
18
  var child_process = require('child_process');
20
19
  var addFormats = require('ajv-formats');
21
20
  var Ajv = require('ajv');
@@ -1491,7 +1490,7 @@ var browserClient = createBaseClient();
1491
1490
 
1492
1491
 
1493
1492
 
1494
- const log$6 = debug('slardar:transport');
1493
+ const log$2 = debug('slardar:transport');
1495
1494
  // eslint-disable-next-line @typescript-eslint/no-empty-function
1496
1495
  const noop = () => {};
1497
1496
 
@@ -1511,22 +1510,22 @@ const noop = () => {};
1511
1510
  function makeRequest(options) {
1512
1511
  const { url, method, data, success = noop, fail = noop, getResponseText = noop } = options;
1513
1512
 
1514
- log$6('Making %s request to %s', method, url);
1513
+ log$2('Making %s request to %s', method, url);
1515
1514
  if (data) {
1516
- log$6('Request data: %s', data.slice(0, 200));
1515
+ log$2('Request data: %s', data.slice(0, 200));
1517
1516
  }
1518
1517
 
1519
1518
  // 检查 URL 是否有效
1520
1519
  if (!url || typeof url !== 'string') {
1521
1520
  const error = new Error(`Invalid URL: ${url}`);
1522
- log$6('Invalid URL provided: %o', url);
1521
+ log$2('Invalid URL provided: %o', url);
1523
1522
  fail(error);
1524
1523
  return;
1525
1524
  }
1526
1525
 
1527
1526
  try {
1528
1527
  const urlObj = new URL(url);
1529
- log$6('Parsed URL - protocol: %s, hostname: %s, port: %s, path: %s',
1528
+ log$2('Parsed URL - protocol: %s, hostname: %s, port: %s, path: %s',
1530
1529
  urlObj.protocol, urlObj.hostname, urlObj.port, urlObj.pathname);
1531
1530
 
1532
1531
  const isHttps = urlObj.protocol === 'https:';
@@ -1544,34 +1543,34 @@ function makeRequest(options) {
1544
1543
  },
1545
1544
  },
1546
1545
  res => {
1547
- log$6('Response callback triggered: status=%s', res.statusCode);
1546
+ log$2('Response callback triggered: status=%s', res.statusCode);
1548
1547
  let responseText = '';
1549
1548
 
1550
1549
  res.on('data', chunk => {
1551
- log$6('Response data chunk received: %s bytes', chunk.length);
1550
+ log$2('Response data chunk received: %s bytes', chunk.length);
1552
1551
  responseText += chunk.toString();
1553
1552
  });
1554
1553
 
1555
1554
  res.on('end', () => {
1556
- log$6('Response end event fired');
1557
- log$6('Response received: status=%s, body=%s', res.statusCode, responseText.slice(0, 200));
1555
+ log$2('Response end event fired');
1556
+ log$2('Response received: status=%s, body=%s', res.statusCode, responseText.slice(0, 200));
1558
1557
  getResponseText(responseText);
1559
1558
 
1560
1559
  try {
1561
1560
  if (res.statusCode && res.statusCode >= 400) {
1562
- log$6('Request failed with status %s: %s', res.statusCode, responseText);
1561
+ log$2('Request failed with status %s: %s', res.statusCode, responseText);
1563
1562
  fail(new Error(responseText || res.statusMessage || 'Request failed'));
1564
1563
  } else if (responseText) {
1565
1564
  // eslint-disable-next-line no-restricted-syntax -- Parsing trusted Slardar API responses
1566
1565
  const result = JSON.parse(responseText);
1567
- log$6('Request succeeded');
1566
+ log$2('Request succeeded');
1568
1567
  success(result);
1569
1568
  } else {
1570
- log$6('Request succeeded with empty response');
1569
+ log$2('Request succeeded with empty response');
1571
1570
  success({});
1572
1571
  }
1573
1572
  } catch (e) {
1574
- log$6('Failed to parse response: %s', (e ).message);
1573
+ log$2('Failed to parse response: %s', (e ).message);
1575
1574
  fail(e );
1576
1575
  }
1577
1576
  });
@@ -1579,12 +1578,12 @@ function makeRequest(options) {
1579
1578
  );
1580
1579
 
1581
1580
  req.on('error', err => {
1582
- log$6('Request error: %s', err.message);
1581
+ log$2('Request error: %s', err.message);
1583
1582
  fail(err);
1584
1583
  });
1585
1584
 
1586
1585
  req.on('timeout', () => {
1587
- log$6('Request timeout');
1586
+ log$2('Request timeout');
1588
1587
  req.destroy();
1589
1588
  fail(new Error('Request timeout'));
1590
1589
  });
@@ -1595,7 +1594,7 @@ function makeRequest(options) {
1595
1594
 
1596
1595
  req.end();
1597
1596
  } catch (e) {
1598
- log$6('Exception during request: %s', (e ).message);
1597
+ log$2('Exception during request: %s', (e ).message);
1599
1598
  fail(e );
1600
1599
  }
1601
1600
  }
@@ -1606,14 +1605,14 @@ function makeRequest(options) {
1606
1605
  function createNodeTransport() {
1607
1606
  return {
1608
1607
  get(options) {
1609
- log$6('Transport GET called: %s', options.url);
1608
+ log$2('Transport GET called: %s', options.url);
1610
1609
  makeRequest({
1611
1610
  method: 'GET',
1612
1611
  ...options,
1613
1612
  });
1614
1613
  },
1615
1614
  post({ url, data }) {
1616
- log$6('Transport POST called: %s', url);
1615
+ log$2('Transport POST called: %s', url);
1617
1616
  makeRequest({
1618
1617
  method: 'POST',
1619
1618
  url,
@@ -1634,7 +1633,7 @@ function _nullishCoalesce$3(lhs, rhsFn) { if (lhs != null) { return lhs; } else
1634
1633
 
1635
1634
  // 创建 debug 实例
1636
1635
  // 使用方式: DEBUG=slardar:* your-cli-command
1637
- const log$5 = debug('slardar:reporter');
1636
+ const log$1 = debug('slardar:reporter');
1638
1637
 
1639
1638
  /**
1640
1639
  * Slardar CLI Reporter
@@ -1659,12 +1658,12 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1659
1658
  */
1660
1659
  setup(config) {
1661
1660
  if (this.initialized) {
1662
- log$5('Already initialized, skipping setup');
1661
+ log$1('Already initialized, skipping setup');
1663
1662
  return;
1664
1663
  }
1665
1664
 
1666
1665
  try {
1667
- log$5('Initializing Slardar with config:', {
1666
+ log$1('Initializing Slardar with config:', {
1668
1667
  bid: config.bid,
1669
1668
  release: config.release,
1670
1669
  env: config.env,
@@ -1697,7 +1696,7 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1697
1696
 
1698
1697
  // 添加 send 钩子来追踪事件上报
1699
1698
  this.client.on('send', (ev) => {
1700
- log$5(
1699
+ log$1(
1701
1700
  'send hook called for event: %s',
1702
1701
  (_optionalChain$6([ev, 'optionalAccess', _ => _.ev_type]) ) || 'unknown',
1703
1702
  );
@@ -1705,9 +1704,9 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1705
1704
 
1706
1705
  this.client.start();
1707
1706
  this.initialized = true;
1708
- log$5('Slardar initialized successfully');
1707
+ log$1('Slardar initialized successfully');
1709
1708
  } catch (error) {
1710
- log$5('Failed to initialize:', error);
1709
+ log$1('Failed to initialize:', error);
1711
1710
  }
1712
1711
  }
1713
1712
 
@@ -1716,7 +1715,7 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1716
1715
  */
1717
1716
  ensureInitialized() {
1718
1717
  if (!this.initialized) {
1719
- log$5('Not initialized, call setup() first');
1718
+ log$1('Not initialized, call setup() first');
1720
1719
  return false;
1721
1720
  }
1722
1721
  return true;
@@ -1738,7 +1737,7 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1738
1737
  }
1739
1738
 
1740
1739
  try {
1741
- log$5('Sending event:', { name, metrics, categories });
1740
+ log$1('Sending event:', { name, metrics, categories });
1742
1741
 
1743
1742
  // 过滤掉 undefined 值以满足 Slardar 类型要求
1744
1743
  const cleanMetrics = metrics
@@ -1763,7 +1762,7 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1763
1762
  categories: cleanCategories ,
1764
1763
  })]);
1765
1764
  } catch (error) {
1766
- log$5('Failed to send event:', error);
1765
+ log$1('Failed to send event:', error);
1767
1766
  }
1768
1767
  }
1769
1768
 
@@ -1783,7 +1782,7 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1783
1782
  }
1784
1783
 
1785
1784
  try {
1786
- log$5('Sending log:', { level, message, extra });
1785
+ log$1('Sending log:', { level, message, extra });
1787
1786
  // 使用 sendEvent 发送日志事件
1788
1787
  this.sendEvent('cli_log', undefined, {
1789
1788
  level,
@@ -1791,7 +1790,7 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1791
1790
  ...extra,
1792
1791
  });
1793
1792
  } catch (error) {
1794
- log$5('Failed to send log:', error);
1793
+ log$1('Failed to send log:', error);
1795
1794
  }
1796
1795
  }
1797
1796
 
@@ -1825,7 +1824,7 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1825
1824
  },
1826
1825
  };
1827
1826
 
1828
- log$5('Reporting JS error:', {
1827
+ log$1('Reporting JS error:', {
1829
1828
  name: error.name,
1830
1829
  message: error.message.slice(0, 100),
1831
1830
  stack: _optionalChain$6([error, 'access', _5 => _5.stack, 'optionalAccess', _6 => _6.slice, 'call', _7 => _7(0, 200)]),
@@ -1835,9 +1834,9 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1835
1834
 
1836
1835
  // 使用 Slardar 的 js_error 事件类型,这样会显示在 JS 错误总览页面
1837
1836
  this.client.report(errorEvent);
1838
- log$5('JS error reported successfully');
1837
+ log$1('JS error reported successfully');
1839
1838
  } catch (err) {
1840
- log$5('Failed to report error:', err);
1839
+ log$1('Failed to report error:', err);
1841
1840
  }
1842
1841
  }
1843
1842
 
@@ -1858,7 +1857,7 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1858
1857
  if (!this.ensureInitialized()) {
1859
1858
  return;
1860
1859
  }
1861
- log$5('Merging context:', context);
1860
+ log$1('Merging context:', context);
1862
1861
  _optionalChain$6([this, 'access', _12 => _12.client, 'access', _13 => _13.context, 'optionalAccess', _14 => _14.merge, 'call', _15 => _15(context)]);
1863
1862
  }
1864
1863
 
@@ -1895,7 +1894,7 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1895
1894
  }
1896
1895
 
1897
1896
  try {
1898
- log$5('Updating config:', config);
1897
+ log$1('Updating config:', config);
1899
1898
  this.client.config({
1900
1899
  userId: config.userId,
1901
1900
  release: config.release,
@@ -1903,7 +1902,7 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1903
1902
  env: config.env,
1904
1903
  });
1905
1904
  } catch (error) {
1906
- log$5('Failed to update config:', error);
1905
+ log$1('Failed to update config:', error);
1907
1906
  }
1908
1907
  }
1909
1908
 
@@ -1915,11 +1914,11 @@ class SlardarCLIReporter {constructor() { SlardarCLIReporter.prototype.__init.ca
1915
1914
  if (!this.ensureInitialized()) {
1916
1915
  return;
1917
1916
  }
1918
- log$5('Flushing Slardar data...');
1917
+ log$1('Flushing Slardar data...');
1919
1918
  _optionalChain$6([this, 'access', _24 => _24.client, 'access', _25 => _25.getSender, 'optionalCall', _26 => _26(), 'optionalAccess', _27 => _27.flush, 'call', _28 => _28()]);
1920
- log$5('Waiting %dms for events to be sent...', waitMs);
1919
+ log$1('Waiting %dms for events to be sent...', waitMs);
1921
1920
  await new Promise(resolve => setTimeout(resolve, waitMs));
1922
- log$5('Slardar data flushed');
1921
+ log$1('Slardar data flushed');
1923
1922
  }
1924
1923
 
1925
1924
  /**
@@ -2106,7 +2105,7 @@ const EventBuilder = {
2106
2105
  };
2107
2106
 
2108
2107
  var name = "@coze-arch/cli";
2109
- var version = "0.0.5-alpha.6339fe";
2108
+ var version = "0.0.6";
2110
2109
  var description = "coze coding devtools cli";
2111
2110
  var license = "MIT";
2112
2111
  var author = "fanwenjie.fe@bytedance.com";
@@ -2146,7 +2145,6 @@ var dependencies = {
2146
2145
  commander: "~12.1.0",
2147
2146
  debug: "^4.3.7",
2148
2147
  ejs: "^3.1.10",
2149
- "fast-glob": "^3.3.3",
2150
2148
  "js-yaml": "^4.1.0",
2151
2149
  minimist: "^1.2.5",
2152
2150
  shelljs: "^0.10.0"
@@ -2211,7 +2209,7 @@ function _optionalChain$4(ops) { let lastAccessLHS = undefined; let value = ops[
2211
2209
  * Slardar 监控初始化和上报
2212
2210
  */
2213
2211
 
2214
- const log$4 = debug('slardar:cli');
2212
+ const log = debug('slardar:cli');
2215
2213
 
2216
2214
  /**
2217
2215
  * 安全执行函数包装器
@@ -2224,18 +2222,18 @@ function safeRun(
2224
2222
  ) {
2225
2223
  return ((...args) => {
2226
2224
  try {
2227
- log$4('Calling Slardar function: %s', name);
2225
+ log('Calling Slardar function: %s', name);
2228
2226
  const result = fn(...args);
2229
2227
 
2230
2228
  // 如果是 Promise,处理异步错误
2231
2229
  if (result instanceof Promise) {
2232
2230
  return result
2233
2231
  .then(res => {
2234
- log$4('Slardar function %s completed', name);
2232
+ log('Slardar function %s completed', name);
2235
2233
  return res;
2236
2234
  })
2237
2235
  .catch(error => {
2238
- log$4(
2236
+ log(
2239
2237
  'Slardar function %s failed: %s',
2240
2238
  name,
2241
2239
  (error ).message,
@@ -2244,11 +2242,11 @@ function safeRun(
2244
2242
  });
2245
2243
  }
2246
2244
 
2247
- log$4('Slardar function %s completed', name);
2245
+ log('Slardar function %s completed', name);
2248
2246
  return result;
2249
2247
  } catch (error) {
2250
2248
  // Slardar 上报失败不应影响 CLI 正常运行,但要记录错误
2251
- log$4('Slardar function %s failed: %s', name, (error ).message);
2249
+ log('Slardar function %s failed: %s', name, (error ).message);
2252
2250
  }
2253
2251
  }) ;
2254
2252
  }
@@ -2812,76 +2810,6 @@ const registerCommand$4 = program => {
2812
2810
  });
2813
2811
  };
2814
2812
 
2815
- // ABOUTME: Temporary paths utility for Coze CLI
2816
- // ABOUTME: Provides centralized management of temporary directories and files
2817
-
2818
-
2819
- /**
2820
- * Base Coze directory in user's home
2821
- * Used for storing CLI-related temporary files, logs, and cache
2822
- * Default: ~/.coze
2823
- */
2824
- const COZE_HOME_DIR = '.coze';
2825
-
2826
- /**
2827
- * Logs directory name
2828
- * Default: .coze-logs
2829
- */
2830
- const LOGS_DIR = '.coze-logs';
2831
-
2832
- /**
2833
- * Get the Coze home directory path
2834
- * Can be customized via COZE_HOME environment variable
2835
- *
2836
- * @returns Absolute path to Coze home directory (default: ~/.coze)
2837
- */
2838
- const getCozeHome = () =>
2839
- process.env.COZE_HOME || path.join(os.homedir(), COZE_HOME_DIR);
2840
-
2841
- /**
2842
- * Get the logs directory path
2843
- * Can be customized via COZE_LOG_DIR environment variable
2844
- *
2845
- * @returns Absolute path to logs directory (default: ~/.coze-logs)
2846
- */
2847
- const getCozeLogsDir = () =>
2848
- process.env.COZE_LOG_DIR || path.join(os.homedir(), LOGS_DIR);
2849
-
2850
- /**
2851
- * Get path for a specific file/directory within Coze home
2852
- *
2853
- * @param relativePath - Relative path within Coze home directory
2854
- * @returns Absolute path to the file/directory
2855
- *
2856
- * @example
2857
- * ```ts
2858
- * // Get routes file path
2859
- * const routesPath = getCozeFilePath('routes.json');
2860
- * // Returns: ~/.coze/routes.json
2861
- *
2862
- * // Get cache directory
2863
- * const cacheDir = getCozeFilePath('cache');
2864
- * // Returns: ~/.coze/cache
2865
- * ```
2866
- */
2867
- const getCozeFilePath = (relativePath) =>
2868
- path.join(getCozeHome(), relativePath);
2869
-
2870
- /**
2871
- * Get path for a log file
2872
- *
2873
- * @param filename - Log file name
2874
- * @returns Absolute path to the log file
2875
- *
2876
- * @example
2877
- * ```ts
2878
- * const devLog = getCozeLogPath('dev.log');
2879
- * // Returns: ~/.coze-logs/dev.log
2880
- * ```
2881
- */
2882
- const getCozeLogPath = (filename) =>
2883
- path.join(getCozeLogsDir(), filename);
2884
-
2885
2813
  // ABOUTME: This file implements the update command for coze CLI
2886
2814
  // ABOUTME: It wraps pnpm update/install to update package dependencies with logging support
2887
2815
 
@@ -2892,22 +2820,29 @@ const getCozeLogPath = (filename) =>
2892
2820
  */
2893
2821
  const LOG_FILE_NAME$1 = 'update.log';
2894
2822
 
2823
+ /**
2824
+ * 获取日志目录
2825
+ * 优先使用环境变量 COZE_LOG_DIR,否则使用 ~/.coze-logs
2826
+ */
2827
+ const getLogDir$1 = () =>
2828
+ process.env.COZE_LOG_DIR || path.join(os.homedir(), '.coze-logs');
2829
+
2895
2830
  /**
2896
2831
  * 解析日志文件路径
2897
2832
  * - 如果是绝对路径,直接使用
2898
- * - 如果是相对路径,基于 logs 目录 + 相对路径
2899
- * - 如果为空,使用默认日志文件路径
2833
+ * - 如果是相对路径,基于 getLogDir() + 相对路径
2834
+ * - 如果为空,使用 getLogDir() + LOG_FILE_NAME
2900
2835
  */
2901
2836
  const resolveLogFilePath$1 = (logFile) => {
2902
2837
  if (!logFile) {
2903
- return getCozeLogPath(LOG_FILE_NAME$1);
2838
+ return path.join(getLogDir$1(), LOG_FILE_NAME$1);
2904
2839
  }
2905
2840
 
2906
2841
  if (path.isAbsolute(logFile)) {
2907
2842
  return logFile;
2908
2843
  }
2909
2844
 
2910
- return getCozeLogPath(logFile);
2845
+ return path.join(getLogDir$1(), logFile);
2911
2846
  };
2912
2847
 
2913
2848
  /**
@@ -3081,7 +3016,6 @@ const handleUpdateError = (
3081
3016
  /**
3082
3017
  * 执行 update 命令的内部实现
3083
3018
  */
3084
- // eslint-disable-next-line max-lines-per-function
3085
3019
  const executeUpdate = (
3086
3020
  packageName,
3087
3021
  options
@@ -3695,805 +3629,35 @@ const registerCommand$2 = program => {
3695
3629
  });
3696
3630
  };
3697
3631
 
3698
- // ABOUTME: Nuxt route scanner
3699
- // ABOUTME: Scans Nuxt file-based routing structure to extract page and server routes
3700
-
3701
-
3702
-
3703
-
3704
- const log$3 = debug('coze:route-scanner:nuxt');
3705
-
3706
- /**
3707
- * Convert Nuxt file path to route path
3708
- * Examples:
3709
- * - app/pages/index.vue -> /
3710
- * - pages/index.vue -> /
3711
- * - app/pages/blog/index.vue -> /blog
3712
- * - pages/blog/[id].vue -> /blog/[id]
3713
- * - server/api/users.ts -> /api/users
3714
- * - server/routes/webhook.ts -> /webhook
3715
- */
3716
- const nuxtFilePathToRoute = (
3717
- filePath,
3718
- type,
3719
- ) => {
3720
- let route;
3721
-
3722
- if (type === 'page') {
3723
- // Remove pages prefix (supports both app/pages/ and pages/) and .vue extension
3724
- route = filePath
3725
- .replace(/^app\/pages\//, '')
3726
- .replace(/^pages\//, '')
3727
- .replace(/\.vue$/, '')
3728
- .replace(/\/index$/, ''); // Remove trailing /index
3729
-
3730
- // Handle index.vue at root (becomes empty string)
3731
- if (route === 'index') {
3732
- return '/';
3733
- }
3734
- } else {
3735
- // API routes
3736
- if (filePath.startsWith('server/api/')) {
3737
- route = `/api/${filePath
3738
- .replace(/^server\/api\//, '')
3739
- .replace(/\.(ts|js)$/, '')}`;
3740
- } else {
3741
- // server/routes
3742
- route = `/${filePath
3743
- .replace(/^server\/routes\//, '')
3744
- .replace(/\.(ts|js)$/, '')}`;
3745
- }
3746
- }
3747
-
3748
- // Handle root route
3749
- if (route === '' || route === '/') {
3750
- return '/';
3751
- }
3752
-
3753
- // Ensure leading slash
3754
- if (!route.startsWith('/')) {
3755
- route = `/${route}`;
3756
- }
3757
-
3758
- return route;
3759
- };
3760
-
3761
- /**
3762
- * Normalize Nuxt route path to standard format
3763
- * Converts Nuxt dynamic segments to standard format:
3764
- * - [id] -> :id
3765
- * - [...slug] -> :slug*
3766
- */
3767
- const normalizeRoutePath$1 = (routePath) =>
3768
- routePath
3769
- .replace(/\[\.\.\.(\w+)\]/g, ':$1*') // [...slug] -> :slug*
3770
- .replace(/\[(\w+)\]/g, ':$1'); // [id] -> :id
3771
- /**
3772
- * Extract dynamic parameter names from route path
3773
- */
3774
- const extractParams$1 = (routePath) => {
3775
- const params = [];
3776
- const patterns = [
3777
- /\[\.\.\.(\w+)\]/g, // [...slug]
3778
- /\[(\w+)\]/g, // [id]
3779
- ];
3780
-
3781
- patterns.forEach(pattern => {
3782
- const matches = routePath.matchAll(pattern);
3783
- for (const match of matches) {
3784
- params.push(match[1]);
3785
- }
3786
- });
3787
-
3788
- return params;
3789
- };
3790
-
3791
- /**
3792
- * Scan Nuxt routes from pages and server directories
3793
- *
3794
- * Scans for:
3795
- * - app/pages/**\/*.vue OR pages/**\/*.vue - Page routes (both structures supported)
3796
- * - server/api/**\/*.{ts,js} - API routes
3797
- * - server/routes/**\/*.{ts,js} - Server routes
3798
- *
3799
- * Excludes:
3800
- * - app/layouts/, layouts/ - Layout components (NOT routes)
3801
- * - app.vue, error.vue - Special files (NOT regular routes)
3802
- *
3803
- * @param cwd - Current working directory
3804
- * @returns List of detected routes
3805
- */
3806
- const scanNuxtRoutes = async (cwd) => {
3807
- log$3('Scanning Nuxt routes in:', cwd);
3808
- const routes = [];
3809
-
3810
- // Scan page routes - support both app/pages/ and pages/ directories
3811
- const pageFiles = await fastGlob.glob(['{app/,}pages/**/*.vue'], {
3812
- cwd,
3813
- ignore: [
3814
- '**/node_modules/**',
3815
- '**/.nuxt/**',
3816
- '**/dist/**',
3817
- '**/.output/**',
3818
- // Exclude layouts directory (not routes)
3819
- '**/layouts/**',
3820
- 'app/layouts/**',
3821
- // Exclude special files
3822
- 'app.vue',
3823
- 'error.vue',
3824
- ],
3825
- onlyFiles: true,
3826
- followSymbolicLinks: false,
3827
- });
3828
-
3829
- log$3('Found %d page files', pageFiles.length);
3830
-
3831
- routes.push(
3832
- ...pageFiles.map(file => {
3833
- const routePath = nuxtFilePathToRoute(file, 'page');
3834
- const isDynamic = routePath.includes('[');
3835
- const params = extractParams$1(routePath);
3836
-
3837
- return {
3838
- path: normalizeRoutePath$1(routePath),
3839
- rawPath: routePath,
3840
- dynamic: isDynamic,
3841
- params,
3842
- type: 'page' ,
3843
- file,
3844
- framework: 'nuxt' ,
3845
- };
3846
- }),
3847
- );
3848
-
3849
- // Scan API routes
3850
- const apiFiles = await fastGlob.glob('server/api/**/*.{ts,js}', {
3851
- cwd,
3852
- ignore: ['**/node_modules/**', '**/dist/**', '**/.output/**'],
3853
- onlyFiles: true,
3854
- followSymbolicLinks: false,
3855
- });
3856
-
3857
- log$3('Found %d API files', apiFiles.length);
3858
-
3859
- routes.push(
3860
- ...apiFiles.map(file => {
3861
- const routePath = nuxtFilePathToRoute(file, 'api');
3862
- const isDynamic = routePath.includes('[');
3863
- const params = extractParams$1(routePath);
3864
-
3865
- return {
3866
- path: normalizeRoutePath$1(routePath),
3867
- rawPath: routePath,
3868
- dynamic: isDynamic,
3869
- params,
3870
- type: 'api' ,
3871
- file,
3872
- framework: 'nuxt' ,
3873
- };
3874
- }),
3875
- );
3876
-
3877
- // Scan server routes
3878
- const serverRouteFiles = await fastGlob.glob('server/routes/**/*.{ts,js}', {
3879
- cwd,
3880
- ignore: ['**/node_modules/**', '**/dist/**', '**/.output/**'],
3881
- onlyFiles: true,
3882
- followSymbolicLinks: false,
3883
- });
3884
-
3885
- log$3('Found %d server route files', serverRouteFiles.length);
3886
-
3887
- routes.push(
3888
- ...serverRouteFiles.map(file => {
3889
- const routePath = nuxtFilePathToRoute(file, 'api');
3890
- const isDynamic = routePath.includes('[');
3891
- const params = extractParams$1(routePath);
3892
-
3893
- return {
3894
- path: normalizeRoutePath$1(routePath),
3895
- rawPath: routePath,
3896
- dynamic: isDynamic,
3897
- params,
3898
- type: 'api' ,
3899
- file,
3900
- framework: 'nuxt' ,
3901
- };
3902
- }),
3903
- );
3904
-
3905
- log$3('Total routes found: %d', routes.length);
3906
- return routes;
3907
- };
3908
-
3909
- // ABOUTME: Next.js route scanner
3910
- // ABOUTME: Scans Next.js App Router file structure to extract routes
3911
-
3912
-
3913
-
3914
-
3915
- const log$2 = debug('coze:route-scanner:nextjs');
3916
-
3917
- /**
3918
- * Convert Next.js file path to route path
3919
- * Examples:
3920
- * - src/app/page.tsx -> /
3921
- * - src/app/blog/page.tsx -> /blog
3922
- * - src/app/blog/[id]/page.tsx -> /blog/[id]
3923
- * - src/app/api/users/route.ts -> /api/users
3924
- */
3925
- const filePathToRoute = (filePath) => {
3926
- // Remove src/app prefix and file extension
3927
- let route = filePath
3928
- .replace(/^src\/app/, '')
3929
- .replace(/\/(page|route|layout)\.(tsx?|jsx?)$/, '');
3930
-
3931
- // Handle root route
3932
- if (route === '' || route === '/') {
3933
- return '/';
3934
- }
3935
-
3936
- // Ensure leading slash
3937
- if (!route.startsWith('/')) {
3938
- route = `/${route}`;
3939
- }
3940
-
3941
- return route;
3942
- };
3943
-
3944
- /**
3945
- * Normalize route path to standard format
3946
- * Converts Next.js dynamic segments to standard format:
3947
- * - [id] -> :id
3948
- * - [...slug] -> :slug*
3949
- * - [[...slug]] -> :slug*?
3950
- */
3951
- const normalizeRoutePath = (routePath) =>
3952
- routePath
3953
- .replace(/\[\[\.\.\.(\w+)\]\]/g, ':$1*?') // [[...slug]] -> :slug*?
3954
- .replace(/\[\.\.\.(\w+)\]/g, ':$1*') // [...slug] -> :slug*
3955
- .replace(/\[(\w+)\]/g, ':$1'); // [id] -> :id
3956
- /**
3957
- * Extract dynamic parameter names from route path
3958
- * Note: Uses Set to avoid duplicates when patterns overlap (e.g., [[...slug]])
3959
- */
3960
- const extractParams = (routePath) => {
3961
- const paramsSet = new Set();
3962
- const patterns = [
3963
- /\[\[\.\.\.(\w+)\]\]/g, // [[...slug]]
3964
- /\[\.\.\.(\w+)\]/g, // [...slug]
3965
- /\[(\w+)\]/g, // [id]
3966
- ];
3967
-
3968
- patterns.forEach(pattern => {
3969
- const matches = routePath.matchAll(pattern);
3970
- for (const match of matches) {
3971
- paramsSet.add(match[1]);
3972
- }
3973
- });
3974
-
3975
- return Array.from(paramsSet);
3976
- };
3977
-
3978
- /**
3979
- * Determine route type based on file name
3980
- */
3981
- const getRouteType = (filePath) => {
3982
- if (filePath.includes('/route.')) {
3983
- return 'api';
3984
- }
3985
- return 'page';
3986
- };
3987
-
3988
- /**
3989
- * Scan Next.js routes from src/app directory
3990
- *
3991
- * Scans for:
3992
- * - page.tsx/jsx/ts/js - Page routes
3993
- * - route.ts/js - API routes
3994
- *
3995
- * Note: layout.tsx, loading.tsx, error.tsx are NOT routes, they are UI components
3996
- *
3997
- * @param cwd - Current working directory
3998
- * @returns List of detected routes
3999
- */
4000
- const scanNextjsRoutes = async (cwd) => {
4001
- log$2('Scanning Next.js routes in:', cwd);
4002
-
4003
- // Scan for page and route files only (NOT layout, loading, error, etc.)
4004
- const files = await fastGlob.glob('src/app/**/{page,route}.{tsx,ts,jsx,js}', {
4005
- cwd,
4006
- ignore: [
4007
- '**/node_modules/**',
4008
- '**/.next/**',
4009
- '**/dist/**',
4010
- '**/build/**',
4011
- '**/.turbo/**',
4012
- ],
4013
- onlyFiles: true,
4014
- followSymbolicLinks: false,
4015
- });
4016
-
4017
- log$2('Found %d files', files.length);
4018
-
4019
- return files.map(file => {
4020
- log$2('Processing file:', file);
4021
- const routePath = filePathToRoute(file);
4022
- const isDynamic = routePath.includes('[');
4023
- const params = extractParams(routePath);
4024
-
4025
- return {
4026
- path: normalizeRoutePath(routePath),
4027
- rawPath: routePath,
4028
- dynamic: isDynamic,
4029
- params,
4030
- type: getRouteType(file),
4031
- file,
4032
- framework: 'nextjs',
4033
- };
4034
- });
4035
- };
4036
-
4037
- // ABOUTME: Route manifest file writer
4038
- // ABOUTME: Writes route data to JSON file with proper formatting and directory creation
4039
-
4040
-
4041
-
4042
-
4043
- /**
4044
- * Calculate route statistics
4045
- */
4046
- const calculateStats = (routes) => {
4047
- const dynamic = routes.filter(r => r.dynamic).length;
4048
- const staticCount = routes.length - dynamic;
4049
-
4050
- return {
4051
- total: routes.length,
4052
- dynamic,
4053
- static: staticCount,
4054
- };
4055
- };
4056
-
4057
- /**
4058
- * Resolve output file path
4059
- * - Relative path: resolve based on cwd
4060
- * - Absolute path: use as-is
4061
- */
4062
- const resolveOutputPath = (cwd, outputPath) =>
4063
- path.isAbsolute(outputPath) ? outputPath : path.join(cwd, outputPath);
4064
-
4065
- /**
4066
- * Ensure parent directory exists
4067
- * Handles edge cases like existing files with same name
4068
- */
4069
- const ensureDir$1 = (filePath) => {
4070
- const dir = path.dirname(filePath);
4071
-
4072
- // If directory already exists, verify it's actually a directory
4073
- if (fs.existsSync(dir)) {
4074
- const stats = fs.statSync(dir);
4075
- if (!stats.isDirectory()) {
4076
- throw new Error(`Path exists but is not a directory: ${dir}`);
4077
- }
4078
- return; // Directory exists and is valid
4079
- }
4080
-
4081
- // Create directory with recursive option (equivalent to mkdir -p)
4082
- fs.mkdirSync(dir, { recursive: true });
4083
- };
4084
-
4085
- // JSON.stringify indent size
4086
- const JSON_INDENT = 2;
4087
-
4088
- /**
4089
- * Write route manifest to JSON file
4090
- *
4091
- * @param cwd - Current working directory (not used when outputPath is absolute)
4092
- * @param routes - List of routes to write
4093
- * @param templateType - Framework type
4094
- * @param outputPath - Output file path (absolute or relative to cwd)
4095
- */
4096
- const writeRoutesFile = (
4097
- cwd,
4098
- routes,
4099
- templateType,
4100
- outputPath,
4101
- ) => {
4102
- try {
4103
- const resolvedPath = resolveOutputPath(cwd, outputPath);
4104
-
4105
- // Ensure parent directory exists
4106
- ensureDir$1(resolvedPath);
4107
-
4108
- // Build manifest
4109
- const manifest = {
4110
- framework: templateType,
4111
- generatedAt: new Date().toISOString(),
4112
- routes,
4113
- stats: calculateStats(routes),
4114
- };
4115
-
4116
- // Write to file with pretty formatting
4117
- fs.writeFileSync(
4118
- resolvedPath,
4119
- JSON.stringify(manifest, null, JSON_INDENT),
4120
- 'utf-8',
4121
- );
4122
- } catch (error) {
4123
- // Wrap error with context for better debugging
4124
- const errorMessage = error instanceof Error ? error.message : String(error);
4125
- throw new Error(`Failed to write routes file: ${errorMessage}`);
4126
- }
4127
- };
4128
-
4129
- /**
4130
- * Get the absolute path of the output file
4131
- * Useful for logging and testing
4132
- */
4133
- const getOutputFilePath = (cwd, outputPath) =>
4134
- resolveOutputPath(cwd, outputPath);
4135
-
4136
- // ABOUTME: Template type detection logic
4137
- // ABOUTME: Detects framework type by checking config files and package.json dependencies
4138
-
4139
-
4140
-
4141
-
4142
- const log$1 = debug('coze:route-scanner:detect');
4143
-
4144
- /**
4145
- * Check if any of the specified files exist
4146
- */
4147
- const hasFile = (cwd, ...files) =>
4148
- files.some(file => fs.existsSync(path.join(cwd, file)));
4149
-
4150
- /**
4151
- * Detect template type by checking config files and package.json dependencies
4152
- *
4153
- * Detection rules (priority order):
4154
- * 1. Next.js (highest) - has `next` dependency + next.config.{js,mjs,ts}
4155
- * 2. Nuxt - has `nuxt` dependency + nuxt.config.{ts,js,mjs}
4156
- * 3. Vite (lowest) - has `vite` + `express` dependencies + vite.config.{ts,js,mjs}
4157
- *
4158
- * @param cwd - Current working directory
4159
- * @returns Template type or null if detection fails
4160
- */
4161
- const detectTemplate = (cwd) => {
4162
- log$1('Detecting template type in:', cwd);
4163
-
4164
- const pkgPath = path.join(cwd, 'package.json');
4165
-
4166
- if (!fs.existsSync(pkgPath)) {
4167
- log$1('package.json not found');
4168
- return null;
4169
- }
4170
-
4171
- try {
4172
- const pkgContent = fs.readFileSync(pkgPath, 'utf-8');
4173
- // eslint-disable-next-line no-restricted-syntax
4174
- const pkg = JSON.parse(pkgContent)
4175
-
4176
-
4177
- ;
4178
- const deps = { ...pkg.dependencies, ...pkg.devDependencies };
4179
- log$1('Dependencies found:', Object.keys(deps).join(', '));
4180
-
4181
- // 1. Next.js - highest priority
4182
- const hasNextDep = !!deps.next;
4183
- const hasNextConfig = hasFile(
4184
- cwd,
4185
- 'next.config.js',
4186
- 'next.config.mjs',
4187
- 'next.config.ts',
4188
- );
4189
- log$1('Next.js check - dependency:', hasNextDep, 'config:', hasNextConfig);
4190
-
4191
- if (hasNextDep && hasNextConfig) {
4192
- log$1('✓ Detected: nextjs');
4193
- return 'nextjs';
4194
- }
4195
-
4196
- // 2. Nuxt - second priority
4197
- const hasNuxtDep = !!deps.nuxt;
4198
- const hasNuxtConfig = hasFile(
4199
- cwd,
4200
- 'nuxt.config.ts',
4201
- 'nuxt.config.js',
4202
- 'nuxt.config.mjs',
4203
- );
4204
- log$1('Nuxt check - dependency:', hasNuxtDep, 'config:', hasNuxtConfig);
4205
-
4206
- if (hasNuxtDep && hasNuxtConfig) {
4207
- log$1('✓ Detected: nuxt');
4208
- return 'nuxt';
4209
- }
4210
-
4211
- // 3. Vite - lowest priority (requires both vite and express)
4212
- const hasViteDep = !!deps.vite;
4213
- const hasExpressDep = !!deps.express;
4214
- const hasViteConfig = hasFile(
4215
- cwd,
4216
- 'vite.config.ts',
4217
- 'vite.config.js',
4218
- 'vite.config.mjs',
4219
- );
4220
- log$1(
4221
- 'Vite check - vite dep:',
4222
- hasViteDep,
4223
- 'express dep:',
4224
- hasExpressDep,
4225
- 'config:',
4226
- hasViteConfig,
4227
- );
4228
-
4229
- if (hasViteDep && hasExpressDep && hasViteConfig) {
4230
- log$1('✓ Detected: vite');
4231
- return 'vite';
4232
- }
4233
-
4234
- log$1('✗ No template detected');
4235
- return null;
4236
- } catch (error) {
4237
- // Invalid package.json or other errors
4238
- log$1('Error during detection:', error);
4239
- return null;
4240
- }
4241
- };
4242
-
4243
- // ABOUTME: Route scanner main entry point
4244
- // ABOUTME: Orchestrates template detection, route scanning, and polling-based updates
4245
-
4246
-
4247
- const log = debug('coze:route-scanner:main');
4248
-
4249
- // Polling interval in milliseconds (500ms to balance responsiveness and performance)
4250
- const POLLING_INTERVAL_MS = 500;
4251
-
4252
- // Store polling interval for cleanup
4253
- let pollingInterval = null;
4254
- let previousRoutesHash = null;
4255
-
4256
- /**
4257
- * Scan routes based on template type
4258
- */
4259
- const scanRoutes = async (
4260
- cwd,
4261
- templateType,
4262
- ) => {
4263
- switch (templateType) {
4264
- case 'nextjs':
4265
- return scanNextjsRoutes(cwd);
4266
- case 'nuxt':
4267
- return scanNuxtRoutes(cwd);
4268
- case 'vite':
4269
- return [];
4270
- default:
4271
- return [];
4272
- }
4273
- };
4274
-
4275
- /**
4276
- * Generate a simple hash for routes to detect changes
4277
- */
4278
- const hashRoutes = (routes) =>
4279
- routes
4280
- .map(r => `${r.file}:${r.type}`)
4281
- .sort()
4282
- .join('|');
4283
-
4284
- /**
4285
- * Detect and validate template type
4286
- * @returns Template type or null if invalid/unsupported
4287
- */
4288
- const detectAndValidateTemplate = (cwd) => {
4289
- log('Detecting template type');
4290
- const templateType = detectTemplate(cwd);
4291
-
4292
- if (!templateType) {
4293
- log('No template type detected');
4294
- logger.warn('Unable to detect template type, skipping route scanning');
4295
- return null;
4296
- }
4297
-
4298
- log('Template detected:', templateType);
4299
-
4300
- if (templateType === 'vite') {
4301
- log('Skipping Vite template - not supported');
4302
- return null;
4303
- }
4304
-
4305
- return templateType;
4306
- };
4307
-
4308
- /**
4309
- * Perform initial route scan and write to file
4310
- */
4311
- const performInitialScan = async (
4312
- cwd,
4313
- templateType,
4314
- outputPath,
4315
- ) => {
4316
- log('Starting route scanning');
4317
- log('Performing initial scan');
4318
- const routes = await scanRoutes(cwd, templateType);
4319
- log('Initial scan found %d routes', routes.length);
4320
-
4321
- log('Writing routes to file:', outputPath);
4322
- writeRoutesFile(cwd, routes, templateType, outputPath);
4323
-
4324
- const outputFilePath = getOutputFilePath(cwd, outputPath);
4325
- log('Routes written to: %s (%d routes)', outputFilePath, routes.length);
4326
-
4327
- return routes;
4328
- };
4329
-
4330
- /**
4331
- * Setup polling interval to detect route changes
4332
- */
4333
- const setupPolling = (
4334
- cwd,
4335
- templateType,
4336
- outputPath,
4337
- initialRoutes,
4338
- ) => {
4339
- previousRoutesHash = hashRoutes(initialRoutes);
4340
- log('Starting periodic route scanning (every 500ms)');
4341
- log('About to call setInterval...');
4342
-
4343
- pollingInterval = setInterval(async () => {
4344
- try {
4345
- log('Polling cycle started...');
4346
- const updatedRoutes = await scanRoutes(cwd, templateType);
4347
- const newHash = hashRoutes(updatedRoutes);
4348
- log('Scanned %d routes, hash: %s vs %s', updatedRoutes.length, newHash, previousRoutesHash);
4349
-
4350
- if (newHash !== previousRoutesHash) {
4351
- log('Routes changed, updating file');
4352
- writeRoutesFile(cwd, updatedRoutes, templateType, outputPath);
4353
- previousRoutesHash = newHash;
4354
- log('Routes updated (%d routes)', updatedRoutes.length);
4355
- logger.info(`🔄 Routes updated (${updatedRoutes.length} routes)`);
4356
- }
4357
- } catch (pollingError) {
4358
- log('Polling error:', pollingError);
4359
- }
4360
- }, POLLING_INTERVAL_MS);
4361
-
4362
- log('setInterval called, returned interval ID:', pollingInterval);
4363
- log('pollingInterval type:', typeof pollingInterval);
4364
- log('Route polling started (checking every %dms)', POLLING_INTERVAL_MS);
4365
-
4366
- // Test if interval is actually working by logging after 1 second
4367
- setTimeout(() => {
4368
- log('setTimeout test fired after 1 second - event loop is working');
4369
- }, 1000);
4370
- };
4371
-
4372
- /**
4373
- * Register cleanup handlers for process termination signals
4374
- * Note: Do NOT listen to 'exit' event because the CLI parent process
4375
- * exits normally after spawning the dev server child process
4376
- */
4377
- const registerCleanupHandlers = () => {
4378
- const cleanup = () => {
4379
- if (pollingInterval) {
4380
- log('Cleaning up polling interval on process termination');
4381
- clearInterval(pollingInterval);
4382
- pollingInterval = null;
4383
- previousRoutesHash = null;
4384
- }
4385
- };
4386
-
4387
- // Only handle termination signals, not normal exit
4388
- process.on('SIGINT', cleanup);
4389
- process.on('SIGTERM', cleanup);
4390
- };
4391
-
4392
- /**
4393
- * Clean up existing polling interval
4394
- */
4395
- const cleanupExistingInterval = () => {
4396
- if (pollingInterval) {
4397
- log('Clearing existing polling interval before starting new one');
4398
- clearInterval(pollingInterval);
4399
- pollingInterval = null;
4400
- previousRoutesHash = null;
4401
- }
4402
- };
4403
-
4404
- /**
4405
- * Start route scanning and polling
4406
- *
4407
- * Workflow:
4408
- * 1. Detect template type (Next.js/Nuxt/Vite)
4409
- * 2. If Vite, skip scanning (too simple, not worth resources)
4410
- * 3. Perform initial route scan
4411
- * 4. Write routes to JSON file
4412
- * 5. Start periodic polling (every 500ms) to detect route changes
4413
- *
4414
- * Note: Uses polling instead of file watching to avoid EMFILE errors
4415
- *
4416
- * @param cwd - Current working directory
4417
- * @param options - Scanning options (output path, etc.)
4418
- */
4419
- const startRouteScanning = async (
4420
- cwd,
4421
- options = {},
4422
- ) => {
4423
- cleanupExistingInterval();
4424
-
4425
- try {
4426
- log('startRouteScanning called with cwd:', cwd, 'options:', options);
4427
- // Default output to ~/.coze/routes.json (user home), can be overridden via options or env var
4428
- // Using user home avoids conflict with project .coze config file
4429
- const defaultOutputPath =
4430
- process.env.COZE_ROUTES_OUTPUT || getCozeFilePath('routes.json');
4431
- const { outputPath = defaultOutputPath } = options;
4432
-
4433
- // Step 1: Detect and validate template
4434
- const templateType = detectAndValidateTemplate(cwd);
4435
- if (!templateType) {
4436
- return;
4437
- }
4438
-
4439
- // Step 2: Perform initial scan
4440
- const routes = await performInitialScan(cwd, templateType, outputPath);
4441
-
4442
- // Step 3: Check if polling is enabled
4443
- const enablePolling = process.env.COZE_ROUTES_WATCH !== 'false';
4444
- if (!enablePolling) {
4445
- log('Route polling disabled via COZE_ROUTES_WATCH=false');
4446
- return;
4447
- }
4448
-
4449
- // Step 4: Setup polling and cleanup handlers
4450
- log('About to call setupPolling...');
4451
- setupPolling(cwd, templateType, outputPath, routes);
4452
- log('setupPolling completed');
4453
-
4454
- log('About to call registerCleanupHandlers...');
4455
- registerCleanupHandlers();
4456
- log('registerCleanupHandlers completed');
4457
-
4458
- log('startRouteScanning function about to return');
4459
- } catch (error) {
4460
- log('ERROR CAUGHT in startRouteScanning:', error);
4461
- cleanupExistingInterval();
4462
-
4463
- // Silently handle all errors - route scanning should never break dev server
4464
- log('Route scanning error:', error);
4465
- logger.warn(
4466
- '⚠️ Route scanning skipped due to error (dev server continues normally)',
4467
- );
4468
-
4469
- if (process.env.DEBUG) {
4470
- logger.warn(error instanceof Error ? error.message : String(error));
4471
- }
4472
- }
4473
- };
4474
-
4475
3632
  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; }
4476
3633
  /**
4477
3634
  * 日志文件名常量
4478
3635
  */
4479
3636
  const LOG_FILE_NAME = 'dev.log';
4480
3637
 
3638
+ /**
3639
+ * 获取日志目录
3640
+ * 优先使用环境变量 COZE_LOG_DIR,否则使用 ~/.coze-logs
3641
+ */
3642
+ const getLogDir = () =>
3643
+ process.env.COZE_LOG_DIR || path.join(os.homedir(), '.coze-logs');
3644
+
4481
3645
  /**
4482
3646
  * 解析日志文件路径
4483
3647
  * - 如果是绝对路径,直接使用
4484
- * - 如果是相对路径,基于 logs 目录 + 相对路径
4485
- * - 如果为空,使用默认日志文件路径
3648
+ * - 如果是相对路径,基于 getLogDir() + 相对路径
3649
+ * - 如果为空,使用 getLogDir() + LOG_FILE_NAME
4486
3650
  */
4487
3651
  const resolveLogFilePath = (logFile) => {
4488
3652
  if (!logFile) {
4489
- return getCozeLogPath(LOG_FILE_NAME);
3653
+ return path.join(getLogDir(), LOG_FILE_NAME);
4490
3654
  }
4491
3655
 
4492
3656
  if (path.isAbsolute(logFile)) {
4493
3657
  return logFile;
4494
3658
  }
4495
3659
 
4496
- return getCozeLogPath(logFile);
3660
+ return path.join(getLogDir(), logFile);
4497
3661
  };
4498
3662
 
4499
3663
  /**
@@ -4539,21 +3703,15 @@ const executeRun = async (
4539
3703
  logger.info('');
4540
3704
  }
4541
3705
 
4542
- // 2. 启动路由扫描(仅在 dev 模式)
4543
- // Note: startRouteScanning handles all errors internally and never throws
4544
- if (commandName === 'dev') {
4545
- await startRouteScanning(process.cwd());
4546
- }
4547
-
4548
- // 3. 加载 .coze 配置
3706
+ // 2. 加载 .coze 配置
4549
3707
  const config = await loadCozeConfig();
4550
3708
  const commandArgs = getCommandConfig(config, commandName);
4551
3709
 
4552
- // 4. 准备日志
3710
+ // 3. 准备日志
4553
3711
  const logFilePath = resolveLogFilePath(options.logFile);
4554
3712
  const logStream = createLogStream(logFilePath);
4555
3713
 
4556
- // 5. 执行命令
3714
+ // 4. 执行命令
4557
3715
  const commandString = commandArgs.join(' ');
4558
3716
 
4559
3717
  logger.info(`Executing: ${commandString}`);
@@ -4580,76 +3738,72 @@ const executeRun = async (
4580
3738
  logStream.write(data);
4581
3739
  })]);
4582
3740
 
4583
- // Wait for child process to complete
4584
- await new Promise((resolve, reject) => {
4585
- childProcess.on('close', (code, signal) => {
4586
- logStream.end();
4587
-
4588
- if (code !== 0) {
4589
- const errorMessage = `Command exited with code ${_nullishCoalesce$1(code, () => ( 'unknown'))}${signal ? ` and signal ${signal}` : ''}`;
4590
- logger.error(errorMessage);
4591
- logger.error(`Check log file for details: ${logFilePath}`);
4592
-
4593
- // 上报命令失败
4594
- reportError(new Error(errorMessage), {
4595
- command: commandName,
4596
- exitCode: String(_nullishCoalesce$1(code, () => ( 'unknown'))),
4597
- signal: _nullishCoalesce$1(signal, () => ( 'none')),
4598
- logFile: logFilePath,
4599
- });
4600
- reportCommandComplete(commandName, false, Date.now() - cmdStartTime, {
4601
- args: JSON.stringify(options),
4602
- errorCode: _nullishCoalesce$1(code, () => ( 1)),
4603
- errorMessage,
4604
- });
4605
- flushSlardar()
4606
- .then(() => {
4607
- process.exit(code || 1);
4608
- })
4609
- .catch(() => {
4610
- // Catch any errors in the promise chain to prevent unhandled rejections
4611
- process.exit(code || 1);
4612
- });
4613
- } else {
4614
- logger.success('Command completed successfully');
4615
- logger.info(`Log file: ${logFilePath}`);
4616
-
4617
- // 上报命令成功
4618
- reportCommandComplete(commandName, true, Date.now() - cmdStartTime, {
4619
- args: JSON.stringify(options),
4620
- });
4621
- // flush 由 main 函数统一处理
4622
- resolve();
4623
- }
4624
- });
3741
+ childProcess.on('close', (code, signal) => {
3742
+ logStream.end();
4625
3743
 
4626
- childProcess.on('error', (error) => {
4627
- logger.error('Failed to execute command:');
4628
- logger.error(`Error: ${error.message}`);
4629
- if (error.stack) {
4630
- logger.error(`Stack trace:\n${error.stack}`);
4631
- }
4632
- logStream.end();
3744
+ if (code !== 0) {
3745
+ const errorMessage = `Command exited with code ${_nullishCoalesce$1(code, () => ( 'unknown'))}${signal ? ` and signal ${signal}` : ''}`;
3746
+ logger.error(errorMessage);
3747
+ logger.error(`Check log file for details: ${logFilePath}`);
4633
3748
 
4634
- // 上报错误
4635
- reportError(error, {
3749
+ // 上报命令失败
3750
+ reportError(new Error(errorMessage), {
4636
3751
  command: commandName,
4637
- type: 'child_process_error',
3752
+ exitCode: String(_nullishCoalesce$1(code, () => ( 'unknown'))),
3753
+ signal: _nullishCoalesce$1(signal, () => ( 'none')),
3754
+ logFile: logFilePath,
4638
3755
  });
4639
3756
  reportCommandComplete(commandName, false, Date.now() - cmdStartTime, {
4640
3757
  args: JSON.stringify(options),
4641
- errorCode: 1,
4642
- errorMessage: error.message,
3758
+ errorCode: _nullishCoalesce$1(code, () => ( 1)),
3759
+ errorMessage,
4643
3760
  });
4644
3761
  flushSlardar()
4645
3762
  .then(() => {
4646
- process.exit(1);
3763
+ process.exit(code || 1);
4647
3764
  })
4648
3765
  .catch(() => {
4649
3766
  // Catch any errors in the promise chain to prevent unhandled rejections
4650
- process.exit(1);
3767
+ process.exit(code || 1);
4651
3768
  });
3769
+ } else {
3770
+ logger.success('Command completed successfully');
3771
+ logger.info(`Log file: ${logFilePath}`);
3772
+
3773
+ // 上报命令成功
3774
+ reportCommandComplete(commandName, true, Date.now() - cmdStartTime, {
3775
+ args: JSON.stringify(options),
3776
+ });
3777
+ // flush 由 main 函数统一处理
3778
+ }
3779
+ });
3780
+
3781
+ childProcess.on('error', (error) => {
3782
+ logger.error('Failed to execute command:');
3783
+ logger.error(`Error: ${error.message}`);
3784
+ if (error.stack) {
3785
+ logger.error(`Stack trace:\n${error.stack}`);
3786
+ }
3787
+ logStream.end();
3788
+
3789
+ // 上报错误
3790
+ reportError(error, {
3791
+ command: commandName,
3792
+ type: 'child_process_error',
4652
3793
  });
3794
+ reportCommandComplete(commandName, false, Date.now() - cmdStartTime, {
3795
+ args: JSON.stringify(options),
3796
+ errorCode: 1,
3797
+ errorMessage: error.message,
3798
+ });
3799
+ flushSlardar()
3800
+ .then(() => {
3801
+ process.exit(1);
3802
+ })
3803
+ .catch(() => {
3804
+ // Catch any errors in the promise chain to prevent unhandled rejections
3805
+ process.exit(1);
3806
+ });
4653
3807
  });
4654
3808
  } catch (error) {
4655
3809
  const err = error instanceof Error ? error : new Error(String(error));