@lark-apaas/miaoda-cli 0.1.39-alpha.73a593e → 0.1.39-alpha.ad15159
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 +10 -0
- package/dist/cli/commands/deploy/modern.js +34 -11
- package/dist/cli/handlers/deploy/modern.js +39 -3
- package/dist/config/migrate-configs/vite-react-to-nestjs-react-fullstack.js +8 -1
- package/dist/config/sync-configs/nestjs-react-fullstack.js +40 -0
- package/dist/services/deploy/modern/atoms/local-release.js +3 -0
- package/dist/services/deploy/modern/patch/actions.js +7 -1
- package/dist/services/deploy/modern/patch/index.js +2 -1
- package/dist/services/deploy/modern/pipelines/design-local.js +7 -1
- package/dist/services/deploy/modern/pipelines/local.js +7 -1
- package/dist/utils/http.js +6 -6
- package/dist/utils/sync-rule.js +10 -1
- package/dist/utils/typescript-exclude.js +36 -0
- package/package.json +3 -3
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/build.sh +25 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/lint.js +169 -27
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/typecheck-mcp-ui.js +32 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/tsconfig.mcp-ui.json +33 -0
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 使用平台预设提供的浏览器检查默认值。`npm run lint` 和 `npm run type:check` 自动覆盖 `server/mcp/ui`,模板统一提供 UI 子命令与类型配置;`lint -- --files` 保留按文件选择检查范围。没有 UI 源码时跳过 UI 检查。
|
|
58
|
+
|
|
59
|
+
应用增加 `type:check:mcp-ui` 子命令,与 `type:check:client`、`type:check:server` 一起由 `type:check` 聚合。`scripts/typecheck-mcp-ui.js` 只在 UI 源码存在时调用应用安装的 `tsc --noEmit --project tsconfig.mcp-ui.json`;无 UI 或目录为空时成功跳过。根目录 `tsconfig.mcp-ui.json` 提供浏览器、React JSX 和严格类型检查配置,可按应用需要修改;sync 仅在该文件不存在时创建。已有 `server/mcp/ui/tsconfig.json` 的应用优先沿用原配置,保留自定义 paths/types。
|
|
60
|
+
|
|
61
|
+
UI ESLint 默认配置由平台预设提供,也可用根目录 `eslint.mcp-ui.config.js` / `.cjs` / `.mjs` 定制。`npm run lint` 包含 UI 检查;`--files` 按影响范围检查,shared 变更触发三个环境的类型检查。应用脚本不使用 `node -e` 或 TypeScript 编译器 API。
|
|
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`)把当前应用构建成一份自包含静态产物,可脱离妙搭平台本地打开。
|
|
@@ -18,7 +18,14 @@ function registerDeployCommandsModern(program) {
|
|
|
18
18
|
.option('--dir <path>', '项目目录', '.')
|
|
19
19
|
.option('--skip-build', '跳过 build 步骤(已构建好时使用)', false)
|
|
20
20
|
.option('--skip-release', '不创建/更新本地发布单,发布单状态由外部托管', false)
|
|
21
|
-
.option('--conf <json>', '关联元信息 JSON,仅识别 checkPointVersion / commitID
|
|
21
|
+
.option('--conf <json>', '关联元信息 JSON,仅识别 checkPointVersion / commitID / sessionID / marker / versionLevel 五字段')
|
|
22
|
+
// 这三个 flag 只在父命令声明一份,deploy 与 deploy patch 共用。
|
|
23
|
+
// 不能在 patch 上再声明一遍:commander 遇到父子同名 option 会把值判给父命令,
|
|
24
|
+
// 子命令拿到空数组,`deploy patch --create x` 会静默失效(deploy-patch-wiring 测试可复现)。
|
|
25
|
+
// patch 从 deployCmd.opts() 读,与它今天读 --dir 的方式一致。
|
|
26
|
+
.option('--create <relpath>', '新增文件(可重复)', shared_1.collectRepeatedOption, [])
|
|
27
|
+
.option('--update <relpath>', '修改文件(可重复)', shared_1.collectRepeatedOption, [])
|
|
28
|
+
.option('--delete <relpath>', '删除文件(可重复)', shared_1.collectRepeatedOption, [])
|
|
22
29
|
.addHelpText('after', `
|
|
23
30
|
不要用异步模式或后台模式调用 deploy,否则调用可能提前结束,Agent 会误判发布已完成。
|
|
24
31
|
|
|
@@ -41,14 +48,27 @@ function registerDeployCommandsModern(program) {
|
|
|
41
48
|
外部据 stdout 返回的 version / preReleaseID 自行建单,并据本命令的
|
|
42
49
|
退出码决定把发布单翻成 Finished 还是 Failed —— 失败态由外部负责,CLI 不再兜底。
|
|
43
50
|
该模式下 releaseID / url 输出为 null(CLI 未建单,无从得知)。
|
|
44
|
-
注意:--conf
|
|
51
|
+
注意:--conf 的四个字段唯一消费者是 createLocalRelease,
|
|
45
52
|
本模式下不生效(静默忽略,不报错),需要关联请在外部建单时自行传递。
|
|
46
53
|
|
|
54
|
+
--create / --update / --delete(变更文件清单)
|
|
55
|
+
可重复,值为工程根相对路径(禁绝对路径与 "..")。deploy patch 消费它做增量发布;
|
|
56
|
+
在 deploy 下当前是预留口子——只校验路径并记一行日志,不改变上传行为:
|
|
57
|
+
deploy 仍然全量上传产物,latest 每次被重建为工作区的精确镜像(prune 清理远端多余对象),
|
|
58
|
+
以此保证 git 仓库与 TOS 资源不会不一致。
|
|
59
|
+
后续若实现增量上传,从这里取「要 PUT 哪些文件」即可,调用方无需改动;
|
|
60
|
+
删除仍交给 prune,因为 prune 比对的是完整本地清单,与上传了哪些文件无关。
|
|
61
|
+
|
|
47
62
|
--conf(关联元信息透传)
|
|
48
|
-
值为 JSON string
|
|
63
|
+
值为 JSON string,只识别四个字段,其余 key 忽略:
|
|
49
64
|
checkPointVersion 关联的 checkpoint 版本
|
|
50
65
|
commitID 关联的代码 commit ID
|
|
51
|
-
|
|
66
|
+
sessionID 发起本次发布的页面会话 ID,原样透传不做净化
|
|
67
|
+
marker 本次发布的变更说明,落发布单 description
|
|
68
|
+
四字段均可选;非法 JSON / 非对象 / 字段值非字符串会报错。
|
|
69
|
+
versionLevel:本次发布的版本档位。"major" 声明这是一个大版本,历史记录里强制开新组;
|
|
70
|
+
"minor" 声明是小版本,即使距上次发布超过聚合阈值也不提升。不传则由服务端按
|
|
71
|
+
时间间隔自动判定,与改造前行为一致——只有会主动声明档位的链路需要传。
|
|
52
72
|
|
|
53
73
|
JSON 输出(stdout)
|
|
54
74
|
{"data": {"appId": "...", "version": <n>, "url": "...", "releaseID": "...", "preReleaseID": "..."}}
|
|
@@ -60,6 +80,8 @@ JSON 输出(stdout)
|
|
|
60
80
|
$ miaoda deploy --skip-build
|
|
61
81
|
$ miaoda deploy --skip-release
|
|
62
82
|
$ miaoda deploy --conf '{"checkPointVersion":"v3","commitID":"a1b2c3d"}'
|
|
83
|
+
$ miaoda deploy --conf '{"commitID":"a1b2c3d","sessionID":"<uuid>","marker":"编辑保存:修改 index.html"}'
|
|
84
|
+
$ miaoda deploy --update index.html --create assets/new.js --delete old.html
|
|
63
85
|
`);
|
|
64
86
|
deployCmd.action((0, shared_1.withHelp)(deployCmd, async (rawOpts) => {
|
|
65
87
|
await (0, modern_1.handleDeployModern)({
|
|
@@ -68,17 +90,18 @@ JSON 输出(stdout)
|
|
|
68
90
|
skipBuild: rawOpts.skipBuild,
|
|
69
91
|
skipRelease: rawOpts.skipRelease,
|
|
70
92
|
conf: rawOpts.conf,
|
|
93
|
+
creates: rawOpts.create,
|
|
94
|
+
updates: rawOpts.update,
|
|
95
|
+
deletes: rawOpts.delete,
|
|
71
96
|
});
|
|
72
97
|
}));
|
|
73
98
|
const patchCmd = deployCmd
|
|
74
99
|
.command('patch')
|
|
75
100
|
.description('design-html 增量发布:按文件 create/update/delete,.html 变动时同步 routes.json')
|
|
76
|
-
.option('--create <relpath>', '新增文件(可重复)', shared_1.collectRepeatedOption, [])
|
|
77
|
-
.option('--update <relpath>', '覆盖已有文件(可重复)', shared_1.collectRepeatedOption, [])
|
|
78
|
-
.option('--delete <relpath>', '删除文件(可重复)', shared_1.collectRepeatedOption, [])
|
|
79
101
|
.option('--session-id <id>', '会话 ID,发布时透传给下游(可选)')
|
|
80
102
|
.addHelpText('after', `
|
|
81
|
-
--dir
|
|
103
|
+
--dir / --create / --update / --delete 声明在父命令 deploy 上,本命令共用,用法不变。
|
|
104
|
+
--dir 为项目目录,默认当前目录。
|
|
82
105
|
仅支持 design-html(design_local_deploy)。路径为工程根相对路径(= 服务端 latest 下 key),
|
|
83
106
|
禁绝对路径与 ".."。--create/--update 的本地文件必须存在。改动里含任意 .html(增 / 改 / 删)
|
|
84
107
|
时自动重算并随包更新 routes.json;纯非 .html 改动不动 routes.json。
|
|
@@ -98,9 +121,9 @@ JSON 输出
|
|
|
98
121
|
await (0, index_1.handleDeployPatch)({
|
|
99
122
|
appId: (0, shared_1.resolveAppId)({}),
|
|
100
123
|
dir: deployCmd.opts().dir ?? '.',
|
|
101
|
-
creates:
|
|
102
|
-
updates:
|
|
103
|
-
deletes:
|
|
124
|
+
creates: deployCmd.opts().create,
|
|
125
|
+
updates: deployCmd.opts().update,
|
|
126
|
+
deletes: deployCmd.opts().delete,
|
|
104
127
|
sessionID: rawOpts.sessionId,
|
|
105
128
|
});
|
|
106
129
|
}));
|
|
@@ -4,14 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.parseDeployConf = parseDeployConf;
|
|
7
|
+
exports.parseChangedFiles = parseChangedFiles;
|
|
7
8
|
exports.handleDeployModern = handleDeployModern;
|
|
8
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
9
10
|
const output_1 = require("../../../utils/output");
|
|
10
11
|
const error_1 = require("../../../utils/error");
|
|
11
12
|
const index_1 = require("../../../services/deploy/modern/index");
|
|
12
|
-
const
|
|
13
|
+
const index_2 = require("../../../services/deploy/modern/patch/index");
|
|
14
|
+
const logger_1 = require("../../../utils/logger");
|
|
15
|
+
const CONF_EXPECTED = '期望形如 {"checkPointVersion":"...","commitID":"...","sessionID":"...","marker":"...","versionLevel":"major"} 的 JSON 对象';
|
|
13
16
|
/**
|
|
14
|
-
* 解析 `--conf` JSON string,只取 checkPointVersion / commitID
|
|
17
|
+
* 解析 `--conf` JSON string,只取 checkPointVersion / commitID / sessionID / marker / versionLevel 五个白名单字段。
|
|
15
18
|
* - 未传 → 返回 {}(行为不变)。
|
|
16
19
|
* - 非法 JSON / 非 object / 字段值非 string → 抛 DEPLOY_CONF_INVALID。
|
|
17
20
|
* - 空串字段视为未提供(不进 body,保持可选语义);未知 key 忽略。
|
|
@@ -31,7 +34,13 @@ function parseDeployConf(raw) {
|
|
|
31
34
|
}
|
|
32
35
|
const source = parsed;
|
|
33
36
|
const conf = {};
|
|
34
|
-
for (const key of [
|
|
37
|
+
for (const key of [
|
|
38
|
+
'checkPointVersion',
|
|
39
|
+
'commitID',
|
|
40
|
+
'sessionID',
|
|
41
|
+
'marker',
|
|
42
|
+
'versionLevel',
|
|
43
|
+
]) {
|
|
35
44
|
const value = source[key];
|
|
36
45
|
if (value === undefined)
|
|
37
46
|
continue;
|
|
@@ -43,6 +52,29 @@ function parseDeployConf(raw) {
|
|
|
43
52
|
}
|
|
44
53
|
return conf;
|
|
45
54
|
}
|
|
55
|
+
/**
|
|
56
|
+
* 解析 `--create` / `--update` / `--delete`,校验路径后组成 PatchChangeSet。
|
|
57
|
+
*
|
|
58
|
+
* 复用 deploy patch 的 normalizeRel(同一套校验:禁绝对路径、禁 ".."、统一为 posix 相对路径),
|
|
59
|
+
* 保证两条链路对「变更文件」的接受范围完全一致,调用方从 patch 迁移不会遇到收紧或放宽。
|
|
60
|
+
*
|
|
61
|
+
* 当前是预留口子:deploy 仍然全量上传,这里只校验并记一行日志,不参与上传决策。
|
|
62
|
+
* 三个参数都没传 → 返回 undefined(与不传时行为完全一致,不产生日志)。
|
|
63
|
+
*/
|
|
64
|
+
function parseChangedFiles(opts) {
|
|
65
|
+
const cs = {
|
|
66
|
+
creates: (opts.creates ?? []).map((rel) => (0, index_2.normalizeRel)(rel)),
|
|
67
|
+
updates: (opts.updates ?? []).map((rel) => (0, index_2.normalizeRel)(rel)),
|
|
68
|
+
deletes: (opts.deletes ?? []).map((rel) => (0, index_2.normalizeRel)(rel)),
|
|
69
|
+
};
|
|
70
|
+
const total = cs.creates.length + cs.updates.length + cs.deletes.length;
|
|
71
|
+
if (total === 0)
|
|
72
|
+
return undefined;
|
|
73
|
+
(0, logger_1.log)('deploy', `收到变更文件提示 ${String(total)} 个(create=${String(cs.creates.length)} ` +
|
|
74
|
+
`update=${String(cs.updates.length)} delete=${String(cs.deletes.length)}):` +
|
|
75
|
+
'当前仍为全量上传,未启用增量');
|
|
76
|
+
return cs;
|
|
77
|
+
}
|
|
46
78
|
/**
|
|
47
79
|
* miaoda deploy(modern scene 专用,CLI 表面对齐 openclaw-cli)
|
|
48
80
|
*
|
|
@@ -58,6 +90,10 @@ async function handleDeployModern(opts) {
|
|
|
58
90
|
skipRelease: opts.skipRelease ?? false,
|
|
59
91
|
checkPointVersion: conf.checkPointVersion,
|
|
60
92
|
commitID: conf.commitID,
|
|
93
|
+
sessionID: conf.sessionID,
|
|
94
|
+
marker: conf.marker,
|
|
95
|
+
versionLevel: conf.versionLevel,
|
|
96
|
+
changedFiles: parseChangedFiles(opts),
|
|
61
97
|
});
|
|
62
98
|
(0, output_1.emit)({
|
|
63
99
|
data: {
|
|
@@ -70,6 +70,7 @@ exports.MIGRATE_CONFIG = {
|
|
|
70
70
|
{ type: 'file', from: 'tsconfig.json', to: 'tsconfig.json', overwrite: true },
|
|
71
71
|
{ type: 'file', from: 'tsconfig.app.json', to: 'tsconfig.app.json', overwrite: true },
|
|
72
72
|
{ type: 'file', from: 'tsconfig.node.json', to: 'tsconfig.node.json', overwrite: true },
|
|
73
|
+
{ type: 'file', from: 'tsconfig.mcp-ui.json', to: 'tsconfig.mcp-ui.json', overwrite: false },
|
|
73
74
|
// ESLint 9 flat config —— vite-react 用 eslint.config.mjs 已删,fullstack 用
|
|
74
75
|
// eslint.config.js (commonjs) + extends @lark-apaas/fullstack-presets;user 通常不改
|
|
75
76
|
{ type: 'file', from: 'eslint.config.js', to: 'eslint.config.js', overwrite: true },
|
|
@@ -158,10 +159,16 @@ exports.MIGRATE_CONFIG = {
|
|
|
158
159
|
command: 'tsc --noEmit --project tsconfig.app.json',
|
|
159
160
|
overwrite: true,
|
|
160
161
|
},
|
|
162
|
+
{
|
|
163
|
+
type: 'add-script',
|
|
164
|
+
name: 'type:check:mcp-ui',
|
|
165
|
+
command: 'node ./scripts/typecheck-mcp-ui.js',
|
|
166
|
+
overwrite: true,
|
|
167
|
+
},
|
|
161
168
|
{
|
|
162
169
|
type: 'add-script',
|
|
163
170
|
name: 'type:check',
|
|
164
|
-
command: 'concurrently -n "server,client" -c "blue,green" "npm run type:check:server" "npm run type:check:client"',
|
|
171
|
+
command: 'concurrently -n "server,client,mcp-ui" -c "blue,green,magenta" "npm run type:check:server" "npm run type:check:client" "npm run type:check:mcp-ui"',
|
|
165
172
|
overwrite: true,
|
|
166
173
|
},
|
|
167
174
|
// ----- 平台脚本调用链 -----
|
|
@@ -159,6 +159,46 @@ 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
|
+
},
|
|
168
|
+
{ type: 'file', from: 'tsconfig.mcp-ui.json', to: 'tsconfig.mcp-ui.json', overwrite: false },
|
|
169
|
+
{
|
|
170
|
+
type: 'add-script',
|
|
171
|
+
name: 'type:check:mcp-ui',
|
|
172
|
+
command: 'node ./scripts/typecheck-mcp-ui.js',
|
|
173
|
+
overwrite: false,
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
type: 'patch-script',
|
|
177
|
+
name: 'type:check:mcp-ui',
|
|
178
|
+
to: 'node ./scripts/typecheck-mcp-ui.js',
|
|
179
|
+
ifEquals: [
|
|
180
|
+
'node ./scripts/check.cjs --typecheck-mcp-ui',
|
|
181
|
+
'node ./scripts/lint.js --typecheck-mcp-ui',
|
|
182
|
+
],
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
type: 'patch-script',
|
|
186
|
+
name: 'type:check',
|
|
187
|
+
to: 'concurrently -n "server,client,mcp-ui" -c "blue,green,magenta" "npm run type:check:server" "npm run type:check:client" "npm run type:check:mcp-ui"',
|
|
188
|
+
ifEquals: [
|
|
189
|
+
'node ./scripts/check.cjs --typecheck',
|
|
190
|
+
'node ./scripts/lint.js --typecheck',
|
|
191
|
+
'concurrently -n "server,client" -c "blue,green" "npm run type:check:server" "npm run type:check:client"',
|
|
192
|
+
'concurrently "npm run type:check:server" "npm run type:check:client"',
|
|
193
|
+
'concurrently "npm run type:check:client" "npm run type:check:server"',
|
|
194
|
+
],
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
type: 'patch-script',
|
|
198
|
+
name: 'eslint',
|
|
199
|
+
to: 'eslint . --quiet',
|
|
200
|
+
ifEquals: ['eslint . --quiet --ignore-pattern "server/mcp/ui/**"'],
|
|
201
|
+
},
|
|
162
202
|
// ===== miaoda-cli 本地开发新增规则(fullstack-cli sync 不会执行) =====
|
|
163
203
|
// M1. scripts.dev:local —— 本地用户绕过沙箱判定直接跑本地链路(npm run dev:local)。
|
|
164
204
|
// dev.sh 在 MIAODA_DEP_CACHE_DIR 非空时跑 dev.js(沙箱保活)、否则 exec dev-local.js;显式 dev:local
|
|
@@ -16,6 +16,9 @@ async function createLocalRelease(appId, version, extra) {
|
|
|
16
16
|
version,
|
|
17
17
|
checkPointVersion: extra?.checkPointVersion,
|
|
18
18
|
commitID: extra?.commitID,
|
|
19
|
+
sessionID: extra?.sessionID,
|
|
20
|
+
description: extra?.marker,
|
|
21
|
+
versionLevel: extra?.versionLevel,
|
|
19
22
|
});
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.normalizeRel = normalizeRel;
|
|
6
7
|
exports.buildTosActions = buildTosActions;
|
|
7
8
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
9
|
const node_path_1 = __importDefault(require("node:path"));
|
|
@@ -10,7 +11,12 @@ const error_1 = require("../../../../utils/error");
|
|
|
10
11
|
const index_1 = require("../../../../api/deploy/index");
|
|
11
12
|
const content_1 = require("./content");
|
|
12
13
|
const routes_1 = require("./routes");
|
|
13
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* 校验相对路径:禁绝对路径、禁含 ".." 段;normalize 成 posix 相对。
|
|
16
|
+
*
|
|
17
|
+
* 导出供 `deploy` 的 `--create` / `--update` / `--delete` 复用同一套校验,
|
|
18
|
+
* 让两条链路对「变更文件」的接受范围一致,避免两处实现漂移。
|
|
19
|
+
*/
|
|
14
20
|
function normalizeRel(rel) {
|
|
15
21
|
if (node_path_1.default.isAbsolute(rel) || rel.startsWith('/')) {
|
|
16
22
|
throw new error_1.AppError('ARGS_INVALID', `文件路径不能是绝对路径:${rel}`);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.buildTosActions = void 0;
|
|
3
|
+
exports.normalizeRel = exports.buildTosActions = void 0;
|
|
4
4
|
exports.patchDesignDeploy = patchDesignDeploy;
|
|
5
5
|
const spark_meta_1 = require("../../../../utils/spark-meta");
|
|
6
6
|
const error_1 = require("../../../../utils/error");
|
|
@@ -10,6 +10,7 @@ const template_key_map_1 = require("../template-key-map");
|
|
|
10
10
|
const actions_1 = require("./actions");
|
|
11
11
|
var actions_2 = require("./actions");
|
|
12
12
|
Object.defineProperty(exports, "buildTosActions", { enumerable: true, get: function () { return actions_2.buildTosActions; } });
|
|
13
|
+
Object.defineProperty(exports, "normalizeRel", { enumerable: true, get: function () { return actions_2.normalizeRel; } });
|
|
13
14
|
/**
|
|
14
15
|
* design-html 增量发布:读本地文件组 TosFileAction(改动含 .html 时带重算的
|
|
15
16
|
* routes.json),调后端 applyTosDiff 应用到 latest。scope 限 design_local_deploy。
|
|
@@ -28,7 +28,13 @@ async function designLocalPublishPipeline(opts) {
|
|
|
28
28
|
skipRelease: opts.skipRelease ?? false,
|
|
29
29
|
appId: ctx.appId,
|
|
30
30
|
version: pre.version,
|
|
31
|
-
extra: {
|
|
31
|
+
extra: {
|
|
32
|
+
checkPointVersion: opts.checkPointVersion,
|
|
33
|
+
commitID: opts.commitID,
|
|
34
|
+
sessionID: opts.sessionID,
|
|
35
|
+
marker: opts.marker,
|
|
36
|
+
versionLevel: opts.versionLevel,
|
|
37
|
+
},
|
|
32
38
|
});
|
|
33
39
|
try {
|
|
34
40
|
await (0, index_1.uploadDesignArtifacts)({ projectDir: ctx.projectDir, data });
|
|
@@ -45,7 +45,13 @@ async function localBuildLocalPublishPipeline(opts) {
|
|
|
45
45
|
skipRelease: opts.skipRelease ?? false,
|
|
46
46
|
appId: ctx.appId,
|
|
47
47
|
version: pre.version,
|
|
48
|
-
extra: {
|
|
48
|
+
extra: {
|
|
49
|
+
checkPointVersion: opts.checkPointVersion,
|
|
50
|
+
commitID: opts.commitID,
|
|
51
|
+
sessionID: opts.sessionID,
|
|
52
|
+
marker: opts.marker,
|
|
53
|
+
versionLevel: opts.versionLevel,
|
|
54
|
+
},
|
|
49
55
|
});
|
|
50
56
|
try {
|
|
51
57
|
(0, logger_1.log)('deploy', 'Uploading artifacts...');
|
package/dist/utils/http.js
CHANGED
|
@@ -64,13 +64,13 @@ function createClient(opts) {
|
|
|
64
64
|
* 默认灰度泳道。当前 miaoda CLI 接入的新接口(modern deploy 等)只在该泳道生效,
|
|
65
65
|
* 缺省走这个。MIAODA_CANARY_HEADER 可显式覆盖;设为空字符串则去掉头。
|
|
66
66
|
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
*
|
|
67
|
+
* 联调未合流的服务端改动时,不要改这个默认值 —— 用 MIAODA_CANARY_HEADER 覆盖,
|
|
68
|
+
* 或由 agent_tool 按请求 x-tt-env 注入(详见 applyCanaryHeader 与 agent_tool
|
|
69
|
+
* ExecShellCmd)。改默认值意味着所有沙箱都被带去开发泳道,且历史上多次忘记还原
|
|
70
|
+
* (boe_miaoda_doubao → ppe_miaoda_ccm → boe_miaoda_ccm → boe_debug_detail_lookup
|
|
71
|
+
* → boe_creative_history)。
|
|
72
72
|
*/
|
|
73
|
-
const DEFAULT_CANARY_HEADER = '
|
|
73
|
+
const DEFAULT_CANARY_HEADER = 'prod';
|
|
74
74
|
/**
|
|
75
75
|
* 注入 x-tt-env 小流量头:env 显式设值 > DEFAULT_CANARY_HEADER。
|
|
76
76
|
*
|
package/dist/utils/sync-rule.js
CHANGED
|
@@ -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
|
}
|
|
@@ -215,7 +221,10 @@ function applyPatchScript(rule, targetDir, logPrefix) {
|
|
|
215
221
|
(0, logger_1.log)(logPrefix, ` ○ scripts.${rule.name} (already patched)`);
|
|
216
222
|
return { rule, action: 'noop', path: 'package.json' };
|
|
217
223
|
}
|
|
218
|
-
|
|
224
|
+
const matches = rule.ifEquals
|
|
225
|
+
? rule.ifEquals.includes(current)
|
|
226
|
+
: rule.ifStartsWith !== undefined && current.startsWith(rule.ifStartsWith);
|
|
227
|
+
if (!matches) {
|
|
219
228
|
(0, logger_1.log)(logPrefix, ` ⚠ scripts.${rule.name} customized, skip patch`);
|
|
220
229
|
return { rule, action: 'skipped', path: 'package.json' };
|
|
221
230
|
}
|
|
@@ -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.39-alpha.
|
|
3
|
+
"version": "0.1.39-alpha.ad15159",
|
|
4
4
|
"description": "Miaoda 平台命令行工具,面向 Agent 调用",
|
|
5
5
|
"type": "commonjs",
|
|
6
6
|
"bin": {
|
|
@@ -51,9 +51,9 @@
|
|
|
51
51
|
"prettier": "^3.8.3",
|
|
52
52
|
"tsc-alias": "^1.8.11",
|
|
53
53
|
"tsx": "^4.19.4",
|
|
54
|
-
"typescript": "^5.8.3",
|
|
55
54
|
"vitest": "^4.1.4",
|
|
56
|
-
"xml2js": "^0.6.2"
|
|
55
|
+
"xml2js": "^0.6.2",
|
|
56
|
+
"typescript": "^5.8.3"
|
|
57
57
|
},
|
|
58
58
|
"scripts": {
|
|
59
59
|
"build": "bash scripts/build.sh",
|
|
@@ -210,3 +210,28 @@ else
|
|
|
210
210
|
echo " node_modules: $NODE_MODULES_SIZE"
|
|
211
211
|
echo ""
|
|
212
212
|
fi
|
|
213
|
+
|
|
214
|
+
# 仅成功的全构建生成 backendBuildID;必须在构建、产物准备和依赖裁剪之后。
|
|
215
|
+
# UI/Skill 内容由 SDK 独立计入总 hash;纯前端发布不替换已部署的 backendBuildID。
|
|
216
|
+
if [[ "${only_frontend_change:-false}" != "true" ]]; then
|
|
217
|
+
node - "$ROOT_DIR/dist" <<'NODE'
|
|
218
|
+
const fs = require('node:fs');
|
|
219
|
+
const path = require('node:path');
|
|
220
|
+
const { randomUUID } = require('node:crypto');
|
|
221
|
+
const dist = process.argv[2];
|
|
222
|
+
// 防止 build:server 空跑成功、却没有可交付的服务端入口。
|
|
223
|
+
if (!fs.statSync(path.join(dist, 'server/main.js')).isFile()) {
|
|
224
|
+
throw new Error('Missing server build output: dist/server/main.js');
|
|
225
|
+
}
|
|
226
|
+
// runtime cwd 分别为工程根、dist/(或整包解压到 dist-server/)、旧版 dist/server/。
|
|
227
|
+
// 与 MCP UI/Skill 的交付布局一致:复制产物时保留同一个 UUID,不在启动时生成。
|
|
228
|
+
const files = [
|
|
229
|
+
path.join(dist, 'mcp-build-id'),
|
|
230
|
+
path.join(dist, 'dist/mcp-build-id'),
|
|
231
|
+
path.join(dist, 'server/dist/mcp-build-id'),
|
|
232
|
+
];
|
|
233
|
+
for (const file of files) fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
234
|
+
const buildID = randomUUID();
|
|
235
|
+
for (const file of files) fs.writeFileSync(file, buildID, 'utf8');
|
|
236
|
+
NODE
|
|
237
|
+
fi
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
const path = require('node:path');
|
|
4
|
-
const { spawn } = require('node:child_process');
|
|
5
4
|
const fs = require('node:fs');
|
|
5
|
+
const { spawn } = require('node:child_process');
|
|
6
6
|
|
|
7
7
|
const cwd = process.cwd();
|
|
8
8
|
|
|
@@ -11,30 +11,50 @@ function getBinName(name) {
|
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
function runCommand(command, args) {
|
|
14
|
-
return new Promise(
|
|
14
|
+
return new Promise(resolve => {
|
|
15
15
|
const child = spawn(command, args, {
|
|
16
16
|
cwd,
|
|
17
17
|
stdio: 'inherit',
|
|
18
18
|
shell: false,
|
|
19
19
|
});
|
|
20
20
|
|
|
21
|
-
child.on('close',
|
|
21
|
+
child.on('close', code => resolve(code ?? 1));
|
|
22
22
|
child.on('error', () => resolve(1));
|
|
23
23
|
});
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
// 串行依次执行每个任务,全部跑完后再聚合退出码:
|
|
27
|
+
// 降低并发资源占用、让各任务输出按顺序清晰可读,同时保留“一次暴露所有 lint 问题”的行为。
|
|
28
|
+
async function runTasksSerially(taskSpecs) {
|
|
29
|
+
let exitCode = 0;
|
|
30
|
+
for (const [command, args] of taskSpecs) {
|
|
31
|
+
const code = await runCommand(command, args);
|
|
32
|
+
if (code !== 0) {
|
|
33
|
+
exitCode = 1;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return exitCode;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function normalizeProjectFile(filePath, checkDeleted = false) {
|
|
27
40
|
const absolutePath = path.isAbsolute(filePath)
|
|
28
41
|
? filePath
|
|
29
42
|
: path.resolve(cwd, filePath);
|
|
30
43
|
|
|
31
44
|
if (!fs.existsSync(absolutePath)) {
|
|
32
|
-
|
|
33
|
-
|
|
45
|
+
if (!checkDeleted) {
|
|
46
|
+
console.warn(`[lint] Skip missing file: ${filePath}`);
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
console.log(`[lint] Deleted file: ${filePath}; checking affected projects`);
|
|
34
50
|
}
|
|
35
51
|
|
|
36
52
|
const relativePath = path.relative(cwd, absolutePath);
|
|
37
|
-
if (
|
|
53
|
+
if (
|
|
54
|
+
relativePath === '..' ||
|
|
55
|
+
relativePath.startsWith(`..${path.sep}`) ||
|
|
56
|
+
path.isAbsolute(relativePath)
|
|
57
|
+
) {
|
|
38
58
|
console.warn(`[lint] Skip file outside project: ${filePath}`);
|
|
39
59
|
return null;
|
|
40
60
|
}
|
|
@@ -91,25 +111,106 @@ function canRunStylelint() {
|
|
|
91
111
|
|
|
92
112
|
if (!pkg.scripts || !pkg.scripts.stylelint) return false;
|
|
93
113
|
|
|
94
|
-
return STYLELINT_CONFIG_FILES.some(file =>
|
|
114
|
+
return STYLELINT_CONFIG_FILES.some(file =>
|
|
115
|
+
fs.existsSync(path.join(cwd, file))
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const UI_DIR = 'server/mcp/ui';
|
|
120
|
+
|
|
121
|
+
function uiSourceFiles() {
|
|
122
|
+
const files = [];
|
|
123
|
+
function visit(relative) {
|
|
124
|
+
const absolute = path.join(cwd, relative);
|
|
125
|
+
if (!fs.existsSync(absolute)) return;
|
|
126
|
+
if (fs.lstatSync(absolute).isSymbolicLink()) {
|
|
127
|
+
throw new Error(`MCP UI 检查不支持符号链接: ${relative}`);
|
|
128
|
+
}
|
|
129
|
+
for (const entry of fs.readdirSync(absolute, { withFileTypes: true })) {
|
|
130
|
+
if (['node_modules', 'dist', '.git'].includes(entry.name)) continue;
|
|
131
|
+
const file = `${relative}/${entry.name}`;
|
|
132
|
+
if (entry.isSymbolicLink())
|
|
133
|
+
throw new Error(`MCP UI 检查不支持符号链接: ${file}`);
|
|
134
|
+
if (entry.isDirectory()) visit(file);
|
|
135
|
+
else if (isEslintTarget(file) && !/\.d\.(?:ts|mts|cts)$/.test(file))
|
|
136
|
+
files.push(file);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
visit(UI_DIR);
|
|
140
|
+
return files.sort();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function uiTasks({ types = true, eslint = true, aggregate = false } = {}) {
|
|
144
|
+
const sources = uiSourceFiles();
|
|
145
|
+
if (!sources.length) return [];
|
|
146
|
+
const tasks = [];
|
|
147
|
+
// 只在有 UI 源码时读取聚合命令;普通应用不依赖 MCP 配置或脚本。
|
|
148
|
+
if (types && aggregate) {
|
|
149
|
+
const scripts = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf8')).scripts || {};
|
|
150
|
+
types = !/\bnpm\s+run\s+type:check:mcp-ui(?=[\s"';&|)]|$)/.test(scripts['type:check'] || '');
|
|
151
|
+
}
|
|
152
|
+
if (types) tasks.push([getBinName('npm'), ['run', 'type:check:mcp-ui']]);
|
|
153
|
+
if (eslint) {
|
|
154
|
+
const config = [
|
|
155
|
+
'eslint.mcp-ui.config.js',
|
|
156
|
+
'eslint.mcp-ui.config.cjs',
|
|
157
|
+
'eslint.mcp-ui.config.mjs',
|
|
158
|
+
].find(file => fs.existsSync(path.join(cwd, file)));
|
|
159
|
+
let resolvedConfig = config;
|
|
160
|
+
if (!resolvedConfig) {
|
|
161
|
+
try {
|
|
162
|
+
resolvedConfig =
|
|
163
|
+
require.resolve('@lark-apaas/fullstack-presets/lib/mcp/eslint-ui.js');
|
|
164
|
+
} catch {
|
|
165
|
+
throw new Error(
|
|
166
|
+
'请升级 @lark-apaas/fullstack-presets 以使用 MCP UI 默认 ESLint 配置,或提供 eslint.mcp-ui.config.cjs'
|
|
167
|
+
);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
tasks.push([
|
|
171
|
+
getBinName('npx'),
|
|
172
|
+
[
|
|
173
|
+
'--no-install',
|
|
174
|
+
'eslint',
|
|
175
|
+
'--config',
|
|
176
|
+
resolvedConfig,
|
|
177
|
+
'--quiet',
|
|
178
|
+
'--max-warnings',
|
|
179
|
+
'0',
|
|
180
|
+
...sources,
|
|
181
|
+
],
|
|
182
|
+
]);
|
|
183
|
+
}
|
|
184
|
+
return tasks;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function isUiFile(file) {
|
|
188
|
+
return file.startsWith('server/mcp/ui/');
|
|
95
189
|
}
|
|
96
190
|
|
|
97
191
|
async function runDefaultLint() {
|
|
98
|
-
const
|
|
192
|
+
const taskSpecs = [
|
|
193
|
+
[getBinName('npm'), ['run', 'eslint']],
|
|
194
|
+
[getBinName('npm'), ['run', 'type:check']],
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
taskSpecs.push(...uiTasks({ aggregate: true }));
|
|
99
198
|
|
|
100
199
|
if (canRunStylelint()) {
|
|
101
|
-
|
|
200
|
+
taskSpecs.push([getBinName('npm'), ['run', 'stylelint']]);
|
|
102
201
|
} else {
|
|
103
|
-
console.warn(
|
|
202
|
+
console.warn(
|
|
203
|
+
'[lint] Skip stylelint: missing scripts.stylelint or stylelint config'
|
|
204
|
+
);
|
|
104
205
|
}
|
|
105
206
|
|
|
106
|
-
|
|
107
|
-
process.exit(code);
|
|
207
|
+
process.exit(await runTasksSerially(taskSpecs));
|
|
108
208
|
}
|
|
109
209
|
|
|
110
210
|
async function runSelectiveLint(inputFiles) {
|
|
211
|
+
const hasUiSources = uiSourceFiles().length > 0;
|
|
111
212
|
const normalizedFiles = Array.from(
|
|
112
|
-
new Set(inputFiles.map(normalizeProjectFile).filter(Boolean))
|
|
213
|
+
new Set(inputFiles.map(file => normalizeProjectFile(file, hasUiSources)).filter(Boolean))
|
|
113
214
|
);
|
|
114
215
|
|
|
115
216
|
if (normalizedFiles.length === 0) {
|
|
@@ -117,15 +218,40 @@ async function runSelectiveLint(inputFiles) {
|
|
|
117
218
|
process.exit(0);
|
|
118
219
|
}
|
|
119
220
|
|
|
120
|
-
const eslintFiles = normalizedFiles.filter(
|
|
121
|
-
|
|
221
|
+
const eslintFiles = normalizedFiles.filter(
|
|
222
|
+
file =>
|
|
223
|
+
fs.existsSync(path.join(cwd, file)) &&
|
|
224
|
+
isEslintTarget(file) &&
|
|
225
|
+
(!hasUiSources || (!isUiFile(file) && !file.startsWith('eslint.mcp-ui.config.')))
|
|
226
|
+
);
|
|
227
|
+
const stylelintFiles = normalizedFiles.filter(
|
|
228
|
+
file => fs.existsSync(path.join(cwd, file)) && isStylelintTarget(file)
|
|
229
|
+
);
|
|
122
230
|
const typeCheckFiles = normalizedFiles.filter(isTypeCheckTarget);
|
|
123
231
|
|
|
124
232
|
const clientTypeFiles = [];
|
|
125
233
|
const serverTypeFiles = [];
|
|
234
|
+
const rootConfigChanged = hasUiSources && normalizedFiles.some(file =>
|
|
235
|
+
/^tsconfig(?:\.[^/]*)?\.json$/.test(file)
|
|
236
|
+
);
|
|
237
|
+
if (rootConfigChanged) {
|
|
238
|
+
clientTypeFiles.push('config');
|
|
239
|
+
serverTypeFiles.push('config');
|
|
240
|
+
}
|
|
241
|
+
let checkUi =
|
|
242
|
+
rootConfigChanged ||
|
|
243
|
+
normalizedFiles.some(
|
|
244
|
+
file =>
|
|
245
|
+
isUiFile(file) ||
|
|
246
|
+
file.startsWith('shared/') ||
|
|
247
|
+
file.startsWith('eslint.mcp-ui.config.') ||
|
|
248
|
+
file === 'package.json'
|
|
249
|
+
);
|
|
126
250
|
|
|
127
251
|
for (const filePath of typeCheckFiles) {
|
|
128
|
-
if (filePath
|
|
252
|
+
if (hasUiSources && isUiFile(filePath)) {
|
|
253
|
+
checkUi = true;
|
|
254
|
+
} else if (filePath.startsWith('client/')) {
|
|
129
255
|
clientTypeFiles.push(filePath);
|
|
130
256
|
} else if (filePath.startsWith('server/')) {
|
|
131
257
|
serverTypeFiles.push(filePath);
|
|
@@ -135,36 +261,52 @@ async function runSelectiveLint(inputFiles) {
|
|
|
135
261
|
}
|
|
136
262
|
}
|
|
137
263
|
|
|
138
|
-
const
|
|
264
|
+
const taskSpecs = [];
|
|
139
265
|
|
|
140
266
|
if (eslintFiles.length > 0) {
|
|
141
|
-
|
|
267
|
+
taskSpecs.push([getBinName('npx'), ['eslint', '--quiet', ...eslintFiles]]);
|
|
142
268
|
}
|
|
143
269
|
|
|
144
270
|
if (stylelintFiles.length > 0 && !canRunStylelint()) {
|
|
145
|
-
console.warn(
|
|
271
|
+
console.warn(
|
|
272
|
+
'[lint] Skip stylelint: missing scripts.stylelint or stylelint config'
|
|
273
|
+
);
|
|
146
274
|
} else if (stylelintFiles.length > 0) {
|
|
147
|
-
|
|
275
|
+
taskSpecs.push([
|
|
276
|
+
getBinName('npx'),
|
|
277
|
+
['stylelint', '--quiet', ...stylelintFiles],
|
|
278
|
+
]);
|
|
148
279
|
}
|
|
149
280
|
|
|
150
281
|
if (clientTypeFiles.length > 0) {
|
|
151
|
-
|
|
282
|
+
taskSpecs.push([getBinName('npm'), ['run', 'type:check:client']]);
|
|
152
283
|
}
|
|
153
284
|
|
|
154
285
|
if (serverTypeFiles.length > 0) {
|
|
155
|
-
|
|
286
|
+
taskSpecs.push([getBinName('npm'), ['run', 'type:check:server']]);
|
|
156
287
|
}
|
|
157
288
|
|
|
158
|
-
if (
|
|
289
|
+
if (checkUi) taskSpecs.push(...uiTasks());
|
|
290
|
+
|
|
291
|
+
if (taskSpecs.length === 0) {
|
|
159
292
|
console.log('[lint] No supported files matched for lint');
|
|
160
293
|
process.exit(0);
|
|
161
294
|
}
|
|
162
295
|
|
|
163
|
-
|
|
164
|
-
process.exit(results.some(code => code !== 0) ? 1 : 0);
|
|
296
|
+
process.exit(await runTasksSerially(taskSpecs));
|
|
165
297
|
}
|
|
166
298
|
|
|
167
299
|
async function main() {
|
|
300
|
+
const args = process.argv.slice(2);
|
|
301
|
+
if (args.length === 1 && args[0] === '--eslint-mcp-ui') {
|
|
302
|
+
process.exit(await runTasksSerially(uiTasks({ types: false })));
|
|
303
|
+
return;
|
|
304
|
+
}
|
|
305
|
+
if (args.length && !(args[0] === '--files' && args.length > 1)) {
|
|
306
|
+
throw new Error('用法: lint.js [--files <paths...>]');
|
|
307
|
+
}
|
|
308
|
+
if (args[0] === '--files' && args.slice(1).some(arg => arg.startsWith('--')))
|
|
309
|
+
throw new Error('--files 不可与其他检查模式混用');
|
|
168
310
|
const files = parseFilesArg(process.argv.slice(2));
|
|
169
311
|
if (files === null) {
|
|
170
312
|
await runDefaultLint();
|
|
@@ -179,7 +321,7 @@ async function main() {
|
|
|
179
321
|
await runSelectiveLint(files);
|
|
180
322
|
}
|
|
181
323
|
|
|
182
|
-
main().catch(
|
|
324
|
+
main().catch(error => {
|
|
183
325
|
const message = error instanceof Error ? error.message : String(error);
|
|
184
326
|
console.error(`[lint] Failed to run lint: ${message}`);
|
|
185
327
|
process.exit(1);
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
// MCP UI 是可选浏览器工程;没有源码时跳过,否则直接交给 tsc。
|
|
2
|
+
const fs = require('node:fs');
|
|
3
|
+
const path = require('node:path');
|
|
4
|
+
const { spawnSync } = require('node:child_process');
|
|
5
|
+
|
|
6
|
+
function hasSource(dir) {
|
|
7
|
+
if (!fs.existsSync(dir)) return false;
|
|
8
|
+
return fs.readdirSync(dir, { withFileTypes: true }).some(entry => {
|
|
9
|
+
if (['node_modules', 'dist'].includes(entry.name)) return false;
|
|
10
|
+
if (entry.isDirectory()) return hasSource(path.join(dir, entry.name));
|
|
11
|
+
return entry.isFile() && /\.(?:[cm]?[jt]sx?)$/.test(entry.name);
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
if (hasSource('server/mcp/ui')) {
|
|
16
|
+
// 已有应用的独立 UI 配置继续生效,避免升级覆盖自定义 paths/types。
|
|
17
|
+
const config = fs.existsSync('server/mcp/ui/tsconfig.json')
|
|
18
|
+
? 'server/mcp/ui/tsconfig.json'
|
|
19
|
+
: 'tsconfig.mcp-ui.json';
|
|
20
|
+
const result = spawnSync(
|
|
21
|
+
process.execPath,
|
|
22
|
+
[
|
|
23
|
+
require.resolve('typescript/bin/tsc'),
|
|
24
|
+
'--noEmit',
|
|
25
|
+
'--project',
|
|
26
|
+
config,
|
|
27
|
+
],
|
|
28
|
+
{ stdio: 'inherit' }
|
|
29
|
+
);
|
|
30
|
+
if (result.error) console.error(result.error.message);
|
|
31
|
+
process.exitCode = result.status ?? 1;
|
|
32
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "Bundler",
|
|
6
|
+
"jsx": "react-jsx",
|
|
7
|
+
"types": [],
|
|
8
|
+
"strict": true,
|
|
9
|
+
"checkJs": true,
|
|
10
|
+
"lib": [
|
|
11
|
+
"ES2020",
|
|
12
|
+
"DOM",
|
|
13
|
+
"DOM.Iterable"
|
|
14
|
+
],
|
|
15
|
+
"allowJs": true,
|
|
16
|
+
"skipLibCheck": true,
|
|
17
|
+
"esModuleInterop": true,
|
|
18
|
+
"noEmit": true,
|
|
19
|
+
"baseUrl": ".",
|
|
20
|
+
"paths": {
|
|
21
|
+
"@shared/*": [
|
|
22
|
+
"shared/*"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"include": [
|
|
27
|
+
"server/mcp/ui/**/*"
|
|
28
|
+
],
|
|
29
|
+
"exclude": [
|
|
30
|
+
"node_modules",
|
|
31
|
+
"dist"
|
|
32
|
+
]
|
|
33
|
+
}
|