@lark-apaas/miaoda-cli 0.1.38 → 0.1.39-alpha.44e04b8
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 +17 -0
- package/dist/cli/commands/deploy/modern.js +34 -11
- package/dist/cli/commands/skills/index.js +1 -1
- package/dist/cli/handlers/app/export.js +3 -3
- package/dist/cli/handlers/app/migrate.js +4 -1
- package/dist/cli/handlers/app/sync.js +2 -2
- package/dist/cli/handlers/db/sql.js +8 -6
- package/dist/cli/handlers/deploy/modern.js +39 -3
- package/dist/cli/handlers/plugin/plugin-local.js +5 -4
- package/dist/config/migrate-configs/vite-react-to-nestjs-react-fullstack.js +13 -2
- package/dist/config/sync-configs/nestjs-react-fullstack.js +50 -0
- package/dist/services/app/init/async-install.js +13 -2
- package/dist/services/app/init/install.js +4 -3
- package/dist/services/deploy/modern/atoms/build.js +3 -3
- package/dist/services/deploy/modern/atoms/local-release.js +3 -0
- package/dist/services/deploy/modern/atoms/tosutil.js +6 -8
- 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/archive.js +103 -0
- package/dist/utils/coding-steering.js +30 -2
- package/dist/utils/eslint-ignore.js +169 -0
- package/dist/utils/file-ops.js +28 -8
- package/dist/utils/http.js +6 -6
- package/dist/utils/npm-pack.js +6 -7
- package/dist/utils/run-npm.js +102 -0
- package/dist/utils/sync-rule.js +24 -1
- package/dist/utils/typescript-exclude.js +62 -0
- package/dist/utils/which.js +27 -0
- package/package.json +7 -3
- package/upgrade/templates/nestjs-react-fullstack/templates/nest-cli.json +1 -10
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/build.sh +25 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/check.cjs +123 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/lint.js +39 -35
package/README.md
CHANGED
|
@@ -52,6 +52,23 @@ miaoda file ls --output json
|
|
|
52
52
|
|
|
53
53
|
完整命令通过 `miaoda --help` 或 `miaoda <domain> --help` 查看。
|
|
54
54
|
|
|
55
|
+
## 全栈应用的 MCP UI 检查
|
|
56
|
+
|
|
57
|
+
`miaoda app sync` 为 `nestjs-react-fullstack` 应用同步 `scripts/lint.js` 和独立 `scripts/check.cjs`,在
|
|
58
|
+
`tsconfig.node.json` 中追加 `server/mcp/ui` 排除项,并保留现有配置及继承的排除路径。
|
|
59
|
+
根 `eslint.config.js` / `.mjs` / `.cjs` 会增量追加 UI 全局忽略,直接 ESLint 调用和编辑器检查均生效。
|
|
60
|
+
迁移不执行配置文件;无法静态识别的自定义导出保留并提示手动补齐。
|
|
61
|
+
已知模板版本的 `type:check`、`eslint` 命令会迁移到独立检查入口;自定义命令保留并提示。
|
|
62
|
+
类型检查及 UI 检查直接调用 `check.cjs`,不会回到 `lint.js`;旧版 sync 覆盖聚合脚本时独立入口仍保留,避免递归。
|
|
63
|
+
|
|
64
|
+
- `npm run type:check`:检查服务端、客户端及 MCP UI。
|
|
65
|
+
- `npm run type:check:mcp-ui` / `npm run eslint:mcp-ui`:单独检查 MCP UI。
|
|
66
|
+
- `npm run lint -- --files <files...>`:按改动文件选择检查范围。
|
|
67
|
+
|
|
68
|
+
同步不会生成 MCP UI 目录。开发 MCP UI 时,由 Agent 按 MCP 指南创建
|
|
69
|
+
`server/mcp/ui/tsconfig.json` 和根目录 `eslint.mcp-ui.config.cjs`。
|
|
70
|
+
没有 UI 源码时跳过独立检查;存在 UI 源码但缺少配置时明确报错。
|
|
71
|
+
|
|
55
72
|
## 打包可本地访问的静态产物
|
|
56
73
|
|
|
57
74
|
`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
|
}));
|
|
@@ -53,7 +53,7 @@ function registerSkillsSync(parent) {
|
|
|
53
53
|
|
|
54
54
|
JSON 输出
|
|
55
55
|
{"data": {"stack": "...", "version": "...", "syncedSkills": [...], "techSynced": true|false,
|
|
56
|
-
"claudeSkillsLink": "created|updated|noop|conflict" // 仅 --local 时}}
|
|
56
|
+
"claudeSkillsLink": "created|updated|noop|conflict|unsupported" // 仅 --local 时}}
|
|
57
57
|
|
|
58
58
|
传 --type 时:
|
|
59
59
|
{"data": {"mode": "sandbox", "type": "...", "packageName": "...", "version": "...",
|
|
@@ -38,11 +38,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
39
|
exports.UPGRADE_MANIFEST_FILENAME = exports.DEFAULT_EXPORT_OUT = void 0;
|
|
40
40
|
exports.handleAppExport = handleAppExport;
|
|
41
|
-
const node_child_process_1 = require("node:child_process");
|
|
42
41
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
43
42
|
const node_os_1 = __importDefault(require("node:os"));
|
|
44
43
|
const node_path_1 = __importDefault(require("node:path"));
|
|
45
44
|
const api = __importStar(require("../../../api/index"));
|
|
45
|
+
const archive_1 = require("../../../utils/archive");
|
|
46
46
|
const error_1 = require("../../../utils/error");
|
|
47
47
|
const output_1 = require("../../../utils/output");
|
|
48
48
|
const logger_1 = require("../../../utils/logger");
|
|
@@ -95,11 +95,11 @@ function extractArchive(archivePath, destDir, head) {
|
|
|
95
95
|
const isZip = head.length >= 4 && head[0] === 0x50 && head[1] === 0x4b;
|
|
96
96
|
const isGzip = head.length >= 2 && head[0] === 0x1f && head[1] === 0x8b;
|
|
97
97
|
if (isZip) {
|
|
98
|
-
(0,
|
|
98
|
+
(0, archive_1.extractZip)(archivePath, destDir);
|
|
99
99
|
return;
|
|
100
100
|
}
|
|
101
101
|
if (isGzip) {
|
|
102
|
-
(0,
|
|
102
|
+
(0, archive_1.extractTarGz)(archivePath, destDir);
|
|
103
103
|
return;
|
|
104
104
|
}
|
|
105
105
|
throw new error_1.AppError('EXPORT_ARCHIVE_INVALID', '无法识别归档格式(非 zip / tar.gz)', {
|
|
@@ -16,6 +16,7 @@ const error_1 = require("../../../utils/error");
|
|
|
16
16
|
const output_1 = require("../../../utils/output");
|
|
17
17
|
const env_1 = require("../../../utils/env");
|
|
18
18
|
const logger_1 = require("../../../utils/logger");
|
|
19
|
+
const run_npm_1 = require("../../../utils/run-npm");
|
|
19
20
|
/**
|
|
20
21
|
* miaoda app migrate --to <stack> [--from <stack>] [--dir <path>]
|
|
21
22
|
*
|
|
@@ -126,7 +127,7 @@ async function handleAppMigrate(opts) {
|
|
|
126
127
|
(0, logger_1.log)('migrate', `Running npm ${installArgs.join(' ')}...`);
|
|
127
128
|
let installError;
|
|
128
129
|
try {
|
|
129
|
-
(0,
|
|
130
|
+
(0, run_npm_1.execNpmSync)(installArgs, {
|
|
130
131
|
cwd: targetDir,
|
|
131
132
|
stdio: (0, output_1.isJsonMode)() ? ['ignore', 'ignore', 'inherit'] : 'inherit',
|
|
132
133
|
});
|
|
@@ -167,6 +168,8 @@ async function handleAppMigrate(opts) {
|
|
|
167
168
|
// - install 挂了不做 —— 新依赖不全, 杀旧 dev 后新 dev 起不来反而更糟,
|
|
168
169
|
// 留着旧进程让用户先处理 installError
|
|
169
170
|
// - 软失败:pkill 无匹配进程退出 1,catch 吞掉
|
|
171
|
+
// - pkill 是 POSIX-only,但整段被 isSandboxEnv() 包着(沙箱恒为 Linux),
|
|
172
|
+
// Windows 本地永远走不到这里,故不需要跨平台封装
|
|
170
173
|
let devRestarted = false;
|
|
171
174
|
if (installError === undefined && (0, env_1.isSandboxEnv)()) {
|
|
172
175
|
(0, logger_1.log)('migrate', '沙箱环境,重启 dev process(平台 supervisor 会自动拉起)...');
|
|
@@ -9,7 +9,6 @@ exports.runStackSync = runStackSync;
|
|
|
9
9
|
exports.summarizeSyncResults = summarizeSyncResults;
|
|
10
10
|
exports.snapshotManagedDepSpecs = snapshotManagedDepSpecs;
|
|
11
11
|
exports.diffManagedSpecs = diffManagedSpecs;
|
|
12
|
-
const node_child_process_1 = require("node:child_process");
|
|
13
12
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
13
|
const node_path_1 = __importDefault(require("node:path"));
|
|
15
14
|
const sync_configs_1 = require("../../../config/sync-configs");
|
|
@@ -19,6 +18,7 @@ const githooks_1 = require("../../../utils/githooks");
|
|
|
19
18
|
const logs_dir_1 = require("../../../utils/logs-dir");
|
|
20
19
|
const install_1 = require("../../../services/app/init/install");
|
|
21
20
|
const spark_meta_1 = require("../../../utils/spark-meta");
|
|
21
|
+
const run_npm_1 = require("../../../utils/run-npm");
|
|
22
22
|
const error_1 = require("../../../utils/error");
|
|
23
23
|
const output_1 = require("../../../utils/output");
|
|
24
24
|
const logger_1 = require("../../../utils/logger");
|
|
@@ -127,7 +127,7 @@ async function handleAppSync(opts) {
|
|
|
127
127
|
(0, logger_1.log)('sync', `Running npm ${installArgs.join(' ')}...`);
|
|
128
128
|
let installError;
|
|
129
129
|
try {
|
|
130
|
-
(0,
|
|
130
|
+
(0, run_npm_1.execNpmSync)(installArgs, {
|
|
131
131
|
cwd: targetDir,
|
|
132
132
|
stdio: (0, output_1.isJsonMode)() ? ['ignore', 'ignore', 'inherit'] : 'inherit',
|
|
133
133
|
});
|
|
@@ -47,7 +47,7 @@ const colors_1 = require("../../../utils/colors");
|
|
|
47
47
|
const fuzzy_match_1 = require("../../../utils/fuzzy-match");
|
|
48
48
|
const index_1 = require("../../../api/db/index");
|
|
49
49
|
const sql_keywords_1 = require("../../../api/db/sql-keywords");
|
|
50
|
-
const
|
|
50
|
+
const run_npm_1 = require("../../../utils/run-npm");
|
|
51
51
|
const node_fs_1 = require("node:fs");
|
|
52
52
|
const node_path_1 = __importDefault(require("node:path"));
|
|
53
53
|
/**
|
|
@@ -140,20 +140,22 @@ async function maybeSyncAgentSchema(results) {
|
|
|
140
140
|
(0, logger_1.debug)(`[db sql] DDL detected, running \`npm run gen:db-schema\` in ${projectRoot}`);
|
|
141
141
|
try {
|
|
142
142
|
await new Promise((resolve) => {
|
|
143
|
-
const proc = (0,
|
|
143
|
+
const proc = (0, run_npm_1.spawnNpm)(['run', 'gen:db-schema'], {
|
|
144
144
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
145
145
|
cwd: projectRoot,
|
|
146
146
|
});
|
|
147
147
|
// --verbose:实时透传到 stderr 看进度;默认完全静默丢弃。
|
|
148
148
|
// 任何路径下子进程输出都不会进入当前 CLI 的 stdout,--json 模式安全。
|
|
149
149
|
// 注:默认路径必须 attach 监听器,否则 pipe 缓冲区写满会反压子进程。
|
|
150
|
+
// `?.`:stdio 走数组形式时 node 类型上 stdout/stderr 可为 null。这里 fd1/fd2
|
|
151
|
+
// 显式给了 'pipe',实际恒非 null;真为 null 也意味着不是 pipe、不存在缓冲反压。
|
|
150
152
|
if (verbose) {
|
|
151
|
-
proc.stdout
|
|
152
|
-
proc.stderr
|
|
153
|
+
proc.stdout?.pipe(process.stderr);
|
|
154
|
+
proc.stderr?.pipe(process.stderr);
|
|
153
155
|
}
|
|
154
156
|
else {
|
|
155
|
-
proc.stdout
|
|
156
|
-
proc.stderr
|
|
157
|
+
proc.stdout?.on('data', drainChunk);
|
|
158
|
+
proc.stderr?.on('data', drainChunk);
|
|
157
159
|
}
|
|
158
160
|
let timedOut = false;
|
|
159
161
|
const timer = setTimeout(() => {
|
|
@@ -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: {
|
|
@@ -56,10 +56,11 @@ exports.readAllCapabilities = readAllCapabilities;
|
|
|
56
56
|
exports.hydrateCapability = hydrateCapability;
|
|
57
57
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
58
58
|
const node_path_1 = __importDefault(require("node:path"));
|
|
59
|
-
const node_child_process_1 = require("node:child_process");
|
|
60
59
|
const node_module_1 = require("node:module");
|
|
60
|
+
const archive_1 = require("../../../utils/archive");
|
|
61
61
|
const error_1 = require("../../../utils/error");
|
|
62
62
|
const logger_1 = require("../../../utils/logger");
|
|
63
|
+
const run_npm_1 = require("../../../utils/run-npm");
|
|
63
64
|
// ── Project paths ──
|
|
64
65
|
function getProjectRoot() {
|
|
65
66
|
return process.cwd();
|
|
@@ -152,7 +153,7 @@ function extractTgzToNodeModules(tgzPath, pluginName) {
|
|
|
152
153
|
}
|
|
153
154
|
node_fs_1.default.mkdirSync(tempDir, { recursive: true });
|
|
154
155
|
try {
|
|
155
|
-
(0,
|
|
156
|
+
(0, archive_1.extractTarGz)(tgzPath, tempDir);
|
|
156
157
|
const extractedDir = node_path_1.default.join(tempDir, 'package');
|
|
157
158
|
if (node_fs_1.default.existsSync(extractedDir)) {
|
|
158
159
|
node_fs_1.default.renameSync(extractedDir, targetDir);
|
|
@@ -191,7 +192,7 @@ function installMissingDeps(deps) {
|
|
|
191
192
|
if (deps.length === 0)
|
|
192
193
|
return;
|
|
193
194
|
(0, logger_1.log)('plugin', `Installing missing dependencies: ${deps.join(', ')}`);
|
|
194
|
-
const result = (0,
|
|
195
|
+
const result = (0, run_npm_1.runNpmSync)(['install', ...deps, '--no-save', '--no-package-lock'], {
|
|
195
196
|
cwd: getProjectRoot(),
|
|
196
197
|
stdio: 'inherit',
|
|
197
198
|
});
|
|
@@ -205,7 +206,7 @@ function installMissingDeps(deps) {
|
|
|
205
206
|
}
|
|
206
207
|
}
|
|
207
208
|
function npmInstall(tgzPath) {
|
|
208
|
-
const result = (0,
|
|
209
|
+
const result = (0, run_npm_1.runNpmSync)(['install', tgzPath, '--no-save', '--no-package-lock', '--ignore-scripts'], { cwd: getProjectRoot(), stdio: 'inherit' });
|
|
209
210
|
if (result.error) {
|
|
210
211
|
throw new error_1.AppError('INTERNAL_NPM_FAILED', `npm install failed: ${result.error.message}`, {
|
|
211
212
|
next_actions: ['确认本机已安装 npm,可 --verbose 查看执行详情'],
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
* + client/index.html 等 fullstack 形态新增的资产)
|
|
15
15
|
* 5. 把 fullstack 必需的 scripts 加到 package.json(dev / build / type:check 等)
|
|
16
16
|
* 6. 从 fullstack template/package.json 取版本号,精确加 NestJS 强依赖白名单
|
|
17
|
-
* (client-toolkit + 几个 @nestjs/* + fullstack-nestjs-core + hbs
|
|
17
|
+
* (client-toolkit + 几个 @nestjs/* + SWC 工具链 + fullstack-nestjs-core + hbs 等;
|
|
18
|
+
* 不全量 merge)
|
|
18
19
|
* 7. codemod 业务代码:@lark-apaas/client-toolkit-lite → @lark-apaas/client-toolkit
|
|
19
20
|
* 8. codemod vite.config:defineConfig(...) → defineConfig(..., { fullstack: true }),
|
|
20
21
|
* 让 coding-preset-vite-react 启用 fullstack dev-proxy / html-output / basename /
|
|
@@ -58,7 +59,9 @@ exports.MIGRATE_CONFIG = {
|
|
|
58
59
|
// ===== 4. fullstack 形态必需资产(覆盖 user 已有同名文件) =====
|
|
59
60
|
// server/ 整目录:main.ts / app.module.ts / common/ / modules/view/ 等(fullstack 必需)
|
|
60
61
|
{ type: 'directory', from: 'server', to: 'server', overwrite: true },
|
|
61
|
-
// nest-cli.json
|
|
62
|
+
// nest-cli.json:整文件采用当前 fullstack template,除 Nest build 入口外还必须带上
|
|
63
|
+
// compilerOptions.builder.type=swc 及配套 typeCheck/options。若这里只保留旧的空配置
|
|
64
|
+
// 或字段级残缺合并,即使 package.json 已补齐 SWC 依赖,Nest 仍会回退到 TSC 构建。
|
|
62
65
|
{ type: 'file', from: 'nest-cli.json', to: 'nest-cli.json', overwrite: true },
|
|
63
66
|
// tsconfig.* —— vite-react 时代 tsconfig 配 include: ["src"],迁完代码搬到 client/src,
|
|
64
67
|
// include 路径不对 + 老 tsconfig.app 还 extends @lark-apaas/coding-presets-react;
|
|
@@ -272,6 +275,14 @@ exports.MIGRATE_CONFIG = {
|
|
|
272
275
|
// nest start / nest build 必需
|
|
273
276
|
'@nestjs/cli',
|
|
274
277
|
'@nestjs/testing',
|
|
278
|
+
// nest-cli.json 会被上面的 file rule 覆盖成 builder.type=swc。Nest CLI 只负责
|
|
279
|
+
// 调度 builder,不会自带 SWC 可执行入口和 native compiler;两者必须一起从
|
|
280
|
+
// 当前目标模板取版本写入迁移后 package.json。若漏掉,前端应用升级成全栈后
|
|
281
|
+
// 配置已经选择 SWC,但全新的依赖安装树里找不到 @swc/cli/@swc/core,冷启动和
|
|
282
|
+
// nest build 会失败;开发机旧 node_modules 偶然残留 SWC 时还可能掩盖该问题。
|
|
283
|
+
// 这里不硬编码版本,保持升级应用与同版本新建全栈应用使用完全相同的 SWC 组合。
|
|
284
|
+
'@swc/cli',
|
|
285
|
+
'@swc/core',
|
|
275
286
|
// server ts 编译
|
|
276
287
|
'ts-node',
|
|
277
288
|
'@types/express',
|
|
@@ -159,6 +159,56 @@ 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
|
+
{ type: 'eslint-ignore', to: 'eslint.config.js', pattern: 'server/mcp/ui/**' },
|
|
163
|
+
{ type: 'eslint-ignore', to: 'eslint.config.mjs', pattern: 'server/mcp/ui/**' },
|
|
164
|
+
{ type: 'eslint-ignore', to: 'eslint.config.cjs', pattern: 'server/mcp/ui/**' },
|
|
165
|
+
// MCP UI 使用独立浏览器配置;仅迁移已知模板命令,不生成 UI 目录或配置。
|
|
166
|
+
{
|
|
167
|
+
type: 'typescript-exclude',
|
|
168
|
+
to: 'tsconfig.node.json',
|
|
169
|
+
path: 'server/mcp/ui',
|
|
170
|
+
},
|
|
171
|
+
{
|
|
172
|
+
type: 'add-script',
|
|
173
|
+
name: 'type:check:mcp-ui',
|
|
174
|
+
command: 'node ./scripts/check.cjs --typecheck-mcp-ui',
|
|
175
|
+
overwrite: false,
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
type: 'add-script',
|
|
179
|
+
name: 'eslint:mcp-ui',
|
|
180
|
+
command: 'node ./scripts/check.cjs --eslint-mcp-ui',
|
|
181
|
+
overwrite: false,
|
|
182
|
+
},
|
|
183
|
+
{
|
|
184
|
+
type: 'patch-script',
|
|
185
|
+
name: 'type:check:mcp-ui',
|
|
186
|
+
to: 'node ./scripts/check.cjs --typecheck-mcp-ui',
|
|
187
|
+
ifEquals: ['node ./scripts/lint.js --typecheck-mcp-ui'],
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
type: 'patch-script',
|
|
191
|
+
name: 'eslint:mcp-ui',
|
|
192
|
+
to: 'node ./scripts/check.cjs --eslint-mcp-ui',
|
|
193
|
+
ifEquals: ['node ./scripts/lint.js --eslint-mcp-ui'],
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
type: 'patch-script',
|
|
197
|
+
name: 'type:check',
|
|
198
|
+
to: 'node ./scripts/check.cjs --typecheck',
|
|
199
|
+
ifEquals: [
|
|
200
|
+
'node ./scripts/lint.js --typecheck',
|
|
201
|
+
'concurrently -n "server,client" -c "blue,green" "npm run type:check:server" "npm run type:check:client"',
|
|
202
|
+
'concurrently "npm run type:check:server" "npm run type:check:client"',
|
|
203
|
+
'concurrently "npm run type:check:client" "npm run type:check:server"',
|
|
204
|
+
],
|
|
205
|
+
},
|
|
206
|
+
{
|
|
207
|
+
type: 'patch-script',
|
|
208
|
+
name: 'eslint',
|
|
209
|
+
to: 'eslint . --quiet --ignore-pattern "server/mcp/ui/**"',
|
|
210
|
+
ifEquals: ['eslint . --quiet', 'eslint .'],
|
|
211
|
+
},
|
|
162
212
|
// ===== miaoda-cli 本地开发新增规则(fullstack-cli sync 不会执行) =====
|
|
163
213
|
// M1. scripts.dev:local —— 本地用户绕过沙箱判定直接跑本地链路(npm run dev:local)。
|
|
164
214
|
// dev.sh 在 MIAODA_DEP_CACHE_DIR 非空时跑 dev.js(沙箱保活)、否则 exec dev-local.js;显式 dev:local
|
|
@@ -11,13 +11,24 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
11
11
|
const node_path_1 = __importDefault(require("node:path"));
|
|
12
12
|
const env_1 = require("../../../utils/env");
|
|
13
13
|
const install_1 = require("./install");
|
|
14
|
-
/**
|
|
14
|
+
/**
|
|
15
|
+
* event marker 目录,写死 `/tmp/event`;写前 mkdir -p。沙箱里 code server 轮询此目录,
|
|
16
|
+
* 路径是与平台约定的契约,**不能换成 os.tmpdir()**。所有读写它的代码路径都被
|
|
17
|
+
* isSandboxEnv() 守着(沙箱恒为 Linux),非沙箱环境不会碰。
|
|
18
|
+
*/
|
|
15
19
|
const EVENT_DIR = '/tmp/event';
|
|
16
20
|
/** 安装成功 marker 名 */
|
|
17
21
|
const MARKER_READY = 'WORKSPACE_READY';
|
|
18
22
|
/** 安装失败 marker 名 */
|
|
19
23
|
const MARKER_FAILED = 'WORKSPACE_FAILED';
|
|
20
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* 后台 worker stdout/stderr 落盘路径,写死 `/tmp/...`。
|
|
26
|
+
*
|
|
27
|
+
* 刻意**不**换成 `os.tmpdir()`:这个路径已经是对外契约 —— 写在 async-install 设计
|
|
28
|
+
* spec 里、`app init` 的 help 文案里、失败 marker 的 `logPath` 字段里,用户与平台
|
|
29
|
+
* 都按它 cat 日志。Windows 上 Node 会把 `/tmp` 解析到当前盘符根下的 `\tmp\`,
|
|
30
|
+
* 目录可写、内容可读,功能不受影响,不值得为此破坏契约。
|
|
31
|
+
*/
|
|
21
32
|
exports.ASYNC_INSTALL_LOG = '/tmp/async_install_dep.std.log';
|
|
22
33
|
function nowIso() {
|
|
23
34
|
return new Date().toISOString();
|
|
@@ -5,12 +5,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.installDependencies = installDependencies;
|
|
7
7
|
exports.resolveNpmInstallRegistry = resolveNpmInstallRegistry;
|
|
8
|
-
const node_child_process_1 = require("node:child_process");
|
|
9
8
|
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
10
9
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
10
|
const node_path_1 = __importDefault(require("node:path"));
|
|
11
|
+
const archive_1 = require("../../../utils/archive");
|
|
12
12
|
const error_1 = require("../../../utils/error");
|
|
13
13
|
const logger_1 = require("../../../utils/logger");
|
|
14
|
+
const run_npm_1 = require("../../../utils/run-npm");
|
|
14
15
|
const DEP_CACHE_ENV = 'MIAODA_DEP_CACHE_DIR';
|
|
15
16
|
/**
|
|
16
17
|
* 依赖安装。
|
|
@@ -94,7 +95,7 @@ function md5File(filePath) {
|
|
|
94
95
|
}
|
|
95
96
|
function extractZip(zipPath, targetDir, stdio) {
|
|
96
97
|
try {
|
|
97
|
-
(0,
|
|
98
|
+
(0, archive_1.extractZip)(zipPath, targetDir, { stdio });
|
|
98
99
|
}
|
|
99
100
|
catch (err) {
|
|
100
101
|
const msg = err instanceof Error ? err.message : String(err);
|
|
@@ -129,5 +130,5 @@ function runNpmInstall(targetDir, stdio, extraPackages) {
|
|
|
129
130
|
args.push(...extraPackages);
|
|
130
131
|
}
|
|
131
132
|
(0, logger_1.log)('init', `npm ${args.join(' ')} in ${targetDir}...`);
|
|
132
|
-
(0,
|
|
133
|
+
(0, run_npm_1.execNpmSync)(args, { cwd: targetDir, stdio });
|
|
133
134
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runBuild = runBuild;
|
|
4
|
-
const node_child_process_1 = require("node:child_process");
|
|
5
4
|
const error_1 = require("../../../../utils/error");
|
|
6
5
|
const logger_1 = require("../../../../utils/logger");
|
|
6
|
+
const run_npm_1 = require("../../../../utils/run-npm");
|
|
7
7
|
const protocol_1 = require("../protocol");
|
|
8
8
|
/**
|
|
9
9
|
* 给 RESOURCE_CDN_PREFIX 补 https:// 协议头:
|
|
@@ -33,7 +33,7 @@ function ensureHttpsScheme(v) {
|
|
|
33
33
|
* 是 runtime 同域相对路径(例如 /app/<appId>/runtime/api/v1/storage/object/<bucket>/),
|
|
34
34
|
* 不补 https scheme,原样注入。顶层 static_cdn_prefix 不再下发。
|
|
35
35
|
*
|
|
36
|
-
* build 失败抛 AppError(
|
|
36
|
+
* build 失败抛 AppError(execNpmSync 自身会 throw,捕获后包一层加错误码)。
|
|
37
37
|
*/
|
|
38
38
|
function runBuild(opts) {
|
|
39
39
|
const staticCred = (0, protocol_1.parsePaasStorageCredential)((0, protocol_1.requireDataKey)(opts.data, protocol_1.DataKey.OUTPUT_STATIC_PAAS_STORAGE_CREDENTIAL), protocol_1.DataKey.OUTPUT_STATIC_PAAS_STORAGE_CREDENTIAL);
|
|
@@ -47,7 +47,7 @@ function runBuild(opts) {
|
|
|
47
47
|
};
|
|
48
48
|
(0, logger_1.log)('deploy', 'Building...');
|
|
49
49
|
try {
|
|
50
|
-
(0,
|
|
50
|
+
(0, run_npm_1.execNpmSync)(['run', 'build'], {
|
|
51
51
|
cwd: opts.projectDir,
|
|
52
52
|
stdio: 'inherit',
|
|
53
53
|
env: buildEnv,
|
|
@@ -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
|
/**
|
|
@@ -18,15 +18,13 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
18
18
|
const node_child_process_1 = require("node:child_process");
|
|
19
19
|
const error_1 = require("../../../../utils/error");
|
|
20
20
|
const logger_1 = require("../../../../utils/logger");
|
|
21
|
+
const which_1 = require("../../../../utils/which");
|
|
21
22
|
function resolveTosutilPath() {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
catch {
|
|
28
|
-
/* fallthrough */
|
|
29
|
-
}
|
|
23
|
+
// 用 whichSync 而不是 shell out `which`:Windows 上没有 `which`(对应 `where`),
|
|
24
|
+
// 且查 PATH 本身不需要子进程。
|
|
25
|
+
const resolved = (0, which_1.whichSync)('tosutil');
|
|
26
|
+
if (resolved !== undefined)
|
|
27
|
+
return resolved;
|
|
30
28
|
throw new error_1.AppError('DEPLOY_TOSUTIL_MISSING', 'tosutil is not installed or not in PATH. modern deploy requires sandbox preinstalled tosutil.');
|
|
31
29
|
}
|
|
32
30
|
function tosutilUploadFromTos(cred) {
|
|
@@ -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 });
|