@lark-apaas/miaoda-cli 0.1.44 → 0.1.45

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/README.md CHANGED
@@ -52,6 +52,16 @@ miaoda file ls --output json
52
52
 
53
53
  完整命令通过 `miaoda --help` 或 `miaoda <domain> --help` 查看。
54
54
 
55
+ ## 全栈应用的 MCP UI 检查
56
+
57
+ 创建 MCP UI 时,Agent 在 `server/mcp/ui` 中同时生成 `tsconfig.json` 和 `eslint.config.cjs`,修改 UI 或其 shared 依赖后主动运行独立检查。模板和 CLI 不生成 UI 配置,也不将 UI 检查接入主工程 lint/type:check。
58
+
59
+ 主工程保留原 client/server 检查。Agent 从工程根执行 `npx --no-install tsc --noEmit --project server/mcp/ui/tsconfig.json`,再执行 `npx --no-install eslint --config server/mcp/ui/eslint.config.cjs "server/mcp/ui/**/*.{ts,tsx,js,jsx}" --max-warnings 0`;必须检查退出码并修复错误。无需辅助脚本或新增 npm 命令。
60
+
61
+ CLI 不注入 MCP UI 检查命令;主工程保留原有 client/server 检查,UI 由 Agent 按独立配置执行标准 tsc/eslint。
62
+
63
+ 存量 sync 不改写 ESLint 源码;UI 全局排除项由平台 ESLint 预设提供。TS 仅向已有显式 `exclude` 数组追加目录,保留注释和原项;依赖继承且未显式声明 `exclude` 的自定义配置保持不变并提示人工调整。自定义检查命令保留。lint.js 和 build.sh 仍由平台脚本模板维护,不依赖预设包中的脚本执行入口。
64
+
55
65
  ## 打包可本地访问的静态产物
56
66
 
57
67
  `miaoda app pack`(默认 `--mode standalone`)把当前应用构建成一份自包含静态产物,可脱离妙搭平台本地打开。
@@ -159,6 +159,12 @@ exports.SYNC_CONFIG = {
159
159
  to: `npx -y ${fullstack_cli_pin_1.FULLSTACK_CLI_PIN_SPEC} sync --disable-gen-openapi`,
160
160
  ifStartsWith: 'npx -y @lark-apaas/fullstack-cli sync',
161
161
  },
162
+ // MCP UI 使用独立浏览器配置;只补服务端排除项,不生成 UI 目录和配置。
163
+ {
164
+ type: 'typescript-exclude',
165
+ to: 'tsconfig.node.json',
166
+ path: 'server/mcp/ui',
167
+ },
162
168
  // ===== miaoda-cli 本地开发新增规则(fullstack-cli sync 不会执行) =====
163
169
  // M1. scripts.dev:local —— 本地用户绕过沙箱判定直接跑本地链路(npm run dev:local)。
164
170
  // dev.sh 在 MIAODA_DEP_CACHE_DIR 非空时跑 dev.js(沙箱保活)、否则 exec dev-local.js;显式 dev:local
@@ -43,6 +43,7 @@ const jsonc = __importStar(require("jsonc-parser"));
43
43
  const file_ops_1 = require("./file-ops");
44
44
  const merge_json_1 = require("./merge-json");
45
45
  const logger_1 = require("./logger");
46
+ const typescript_exclude_1 = require("./typescript-exclude");
46
47
  /**
47
48
  * 顺序执行 SyncRule[],逐条 apply。一条 rule 报错不影响后续 rule(catch + 记录),最终
48
49
  * 在 handler 层决定是否整体 fail。
@@ -106,6 +107,11 @@ function applyOne(rule, opts) {
106
107
  case 'patch-script': {
107
108
  return applyPatchScript(rule, targetDir, logPrefix);
108
109
  }
110
+ case 'typescript-exclude': {
111
+ const changed = (0, typescript_exclude_1.excludeTypeScriptPath)(node_path_1.default.join(targetDir, rule.to), rule.path);
112
+ (0, logger_1.log)(logPrefix, ` ${changed ? '✓' : '○'} ${rule.to} (exclude ${rule.path})`);
113
+ return { rule, action: changed ? 'patched' : 'noop', path: rule.to };
114
+ }
109
115
  case 'merge-json': {
110
116
  return applyMergeJson(rule, sourceRoot, targetDir, logPrefix);
111
117
  }
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.excludeTypeScriptPath = excludeTypeScriptPath;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const jsonc_parser_1 = require("jsonc-parser");
9
+ const error_1 = require("./error");
10
+ /** Only append to explicit template excludes; never rewrite inherited user configuration. */
11
+ function excludeTypeScriptPath(filename, excludedPath) {
12
+ if (!node_fs_1.default.existsSync(filename))
13
+ return false;
14
+ const source = node_fs_1.default.readFileSync(filename, 'utf8');
15
+ const errors = [];
16
+ const raw = (0, jsonc_parser_1.parse)(source, errors, { allowTrailingComma: true });
17
+ if (errors.length || !raw || typeof raw !== 'object')
18
+ throw new error_1.AppError('INVALID_CONFIG', `Invalid exclude array: ${filename}`);
19
+ if (raw.exclude === undefined) {
20
+ console.warn(`Cannot safely update inherited excludes in ${filename}; add ${excludedPath} to the effective TypeScript exclude configuration.`);
21
+ return false;
22
+ }
23
+ if (!Array.isArray(raw.exclude) || raw.exclude.some((p) => typeof p !== 'string'))
24
+ throw new error_1.AppError('INVALID_CONFIG', `Invalid exclude array: ${filename}`);
25
+ if (raw.exclude.includes(excludedPath))
26
+ return false;
27
+ node_fs_1.default.writeFileSync(filename, (0, jsonc_parser_1.applyEdits)(source, (0, jsonc_parser_1.modify)(source, ['exclude', raw.exclude.length], excludedPath, {
28
+ isArrayInsertion: true,
29
+ formattingOptions: {
30
+ insertSpaces: true,
31
+ tabSize: 2,
32
+ eol: source.includes('\r\n') ? '\r\n' : '\n',
33
+ },
34
+ })));
35
+ return true;
36
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/miaoda-cli",
3
- "version": "0.1.44",
3
+ "version": "0.1.45",
4
4
  "description": "Miaoda 平台命令行工具,面向 Agent 调用",
5
5
  "type": "commonjs",
6
6
  "bin": {
@@ -6,6 +6,13 @@ const fs = require('fs');
6
6
  const path = require('path');
7
7
 
8
8
  const ROOT_DIR = path.resolve(__dirname, '..');
9
+
10
+ // 加载提交的 .env,供读取构建期配置(如 MIAODA_RUNTIME_ENTRIES)。dotenv 不一定安装 → 静默降级。
11
+ try {
12
+ require('dotenv').config({ path: path.join(ROOT_DIR, '.env') });
13
+ } catch {
14
+ // dotenv 未安装:跳过;MIAODA_RUNTIME_ENTRIES 仍可来自真实环境变量
15
+ }
9
16
  const DIST_DIR = path.join(ROOT_DIR, 'dist');
10
17
  const DIST_SERVER_DIR = path.join(DIST_DIR, 'server');
11
18
  const ROOT_PACKAGE_JSON = path.join(ROOT_DIR, 'package.json');
@@ -16,6 +23,36 @@ const OUT_PACKAGE_JSON = path.join(DIST_DIR, 'package.json');
16
23
  // Server 入口文件
17
24
  const SERVER_ENTRY = path.join(DIST_SERVER_DIR, 'main.js');
18
25
 
26
+ // nest sourceRoot(源码根,默认 server):把 .env 里声明的源码路径映射到构建产物位置
27
+ let SOURCE_ROOT = 'server';
28
+ try {
29
+ const nestCli = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, 'nest-cli.json'), 'utf8'));
30
+ if (nestCli.sourceRoot) SOURCE_ROOT = nestCli.sourceRoot;
31
+ } catch {
32
+ // 读不到 nest-cli.json 则用默认 server
33
+ }
34
+ const SRC_TO_OUT_EXT = { '.mts': '.mjs', '.cts': '.cjs', '.ts': '.js' };
35
+
36
+ // 额外 trace 入口:经 new Function/动态 import 加载、nft 无法从 main.js 静态发现的 ESM runtime。
37
+ // 应用在提交的 .env 里用 MIAODA_RUNTIME_ENTRIES 声明【源码路径】(相对项目根,逗号分隔),例如:
38
+ // MIAODA_RUNTIME_ENTRIES=server/modules/ethics/runtime/review-run.mjs,server/modules/ethics/runtime/parse-core.mjs
39
+ // 脚本按 nest sourceRoot 自动映射到 dist/server 下的构建产物(并重写 .mts→.mjs / .ts→.js)。
40
+ const RUNTIME_ENTRIES = (process.env.MIAODA_RUNTIME_ENTRIES || '')
41
+ .split(',')
42
+ .map((s) => s.trim())
43
+ .filter(Boolean)
44
+ .map((srcRel) => {
45
+ const fromSrc = path.relative(SOURCE_ROOT, srcRel); // 去掉 sourceRoot 前缀
46
+ const ext = path.extname(fromSrc);
47
+ const outRel = SRC_TO_OUT_EXT[ext] ? fromSrc.slice(0, -ext.length) + SRC_TO_OUT_EXT[ext] : fromSrc;
48
+ return path.join(DIST_SERVER_DIR, outRel);
49
+ })
50
+ .filter((p) => {
51
+ if (fs.existsSync(p)) return true;
52
+ console.warn(`⚠️ MIAODA_RUNTIME_ENTRIES 入口在构建产物中不存在,已忽略: ${path.relative(ROOT_DIR, p)}`);
53
+ return false;
54
+ });
55
+
19
56
  // Node.js 内置模块列表
20
57
  const BUILTIN_MODULES = new Set([
21
58
  'assert', 'buffer', 'child_process', 'cluster', 'crypto', 'dgram', 'dns',
@@ -145,12 +182,15 @@ async function smartPrune() {
145
182
  }
146
183
 
147
184
  console.log(`📂 分析入口文件: ${path.relative(ROOT_DIR, SERVER_ENTRY)}`);
185
+ if (RUNTIME_ENTRIES.length) {
186
+ console.log(`📎 额外 trace 入口 (${RUNTIME_ENTRIES.length}): ${RUNTIME_ENTRIES.map((p) => path.relative(DIST_SERVER_DIR, p)).join(', ')}`);
187
+ }
148
188
 
149
189
  // 2. 使用 @vercel/nft 追踪依赖
150
190
  console.log('🔎 追踪实际依赖...');
151
191
  const analyzeStart = Date.now();
152
192
 
153
- const { fileList } = await nodeFileTrace([SERVER_ENTRY], {
193
+ const { fileList } = await nodeFileTrace([SERVER_ENTRY, ...RUNTIME_ENTRIES], {
154
194
  base: ROOT_DIR,
155
195
  processCwd: ROOT_DIR,
156
196
  ts: false, // 禁用 TS 解析