@lark-apaas/miaoda-cli 0.1.6 → 0.1.7-alpha.785a451
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/dist/api/deploy/schemas.js +3 -0
- package/dist/cli/commands/app/index.js +67 -7
- package/dist/cli/commands/deploy/modern.js +9 -0
- package/dist/cli/commands/index.js +36 -1
- package/dist/cli/commands/skills/index.js +18 -2
- package/dist/cli/handlers/app/index.js +4 -1
- package/dist/cli/handlers/app/init.js +60 -9
- package/dist/cli/handlers/app/sync.js +266 -0
- package/dist/cli/handlers/deploy/modern.js +39 -0
- package/dist/cli/handlers/plugin/plugin-local.js +1 -1
- package/dist/cli/handlers/skills/sync.js +15 -4
- package/dist/config/fullstack-cli-pin.js +13 -0
- package/dist/config/sync-configs/design-stack.js +98 -0
- package/dist/config/sync-configs/index.js +62 -0
- package/dist/config/sync-configs/nestjs-react-fullstack.js +177 -0
- package/dist/config/sync.js +14 -0
- package/dist/services/app/init/install.js +44 -13
- package/dist/services/app/init/template.js +23 -6
- package/dist/services/deploy/modern/atoms/local-release.js +7 -2
- package/dist/services/deploy/modern/pipelines/local.js +4 -1
- package/dist/utils/coding-steering.js +107 -28
- package/dist/utils/file-ops.js +45 -0
- package/dist/utils/githooks.js +55 -0
- package/dist/utils/merge-json.js +63 -0
- package/dist/utils/platform-sync.js +160 -0
- package/dist/utils/sync-rule.js +295 -0
- package/package.json +7 -4
- package/upgrade/templates/README.md +34 -0
- package/upgrade/templates/design-stack/templates/.githooks/pre-commit +4 -0
- package/upgrade/templates/design-stack/templates/scripts/dev-local.js +84 -0
- package/upgrade/templates/design-stack/templates/scripts/dev.sh +26 -0
- package/upgrade/templates/design-stack/templates/scripts/hooks/run-precommit.js +37 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/.githooks/pre-commit +4 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/.gitignore.append +8 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/.spark_project +16 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/drizzle.config.ts +55 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/helper/gen-openapi.ts +34 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/nest-cli.json +25 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/build.sh +207 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/dev-local.js +113 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/dev.js +295 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/dev.sh +26 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/hooks/run-precommit.js +37 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/lint.js +150 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/prune-smart.js +330 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/scripts/run.sh +8 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/server/global.d.ts +19 -0
- package/upgrade/templates/nestjs-react-fullstack/templates/tsconfig.node.json +5 -0
|
@@ -0,0 +1,266 @@
|
|
|
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.handleAppUpgrade = void 0;
|
|
7
|
+
exports.handleAppSync = handleAppSync;
|
|
8
|
+
exports.runStackSync = runStackSync;
|
|
9
|
+
exports.summarizeSyncResults = summarizeSyncResults;
|
|
10
|
+
exports.listManagedDeps = listManagedDeps;
|
|
11
|
+
exports.upgradePlatformDeps = upgradePlatformDeps;
|
|
12
|
+
const node_child_process_1 = require("node:child_process");
|
|
13
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
14
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
15
|
+
const sync_configs_1 = require("../../../config/sync-configs");
|
|
16
|
+
const sync_rule_1 = require("../../../utils/sync-rule");
|
|
17
|
+
const platform_sync_1 = require("../../../utils/platform-sync");
|
|
18
|
+
const githooks_1 = require("../../../utils/githooks");
|
|
19
|
+
const install_1 = require("../../../services/app/init/install");
|
|
20
|
+
const spark_meta_1 = require("../../../utils/spark-meta");
|
|
21
|
+
const error_1 = require("../../../utils/error");
|
|
22
|
+
const output_1 = require("../../../utils/output");
|
|
23
|
+
const logger_1 = require("../../../utils/logger");
|
|
24
|
+
/**
|
|
25
|
+
* 由 `miaoda app sync` / `miaoda app init` 管控的平台包白名单 —— user app 已装这些包时,
|
|
26
|
+
* sync 把 package.json 里的版本规范统一改写成 `"latest"`,npm install 时解析到当前 latest。
|
|
27
|
+
*
|
|
28
|
+
* 白名单外的 @lark-apaas/* 包:sync 不动;user app 未装的包:sync 不引入。
|
|
29
|
+
*
|
|
30
|
+
* 之前曾按表钉死具体 alpha 版本(避免外部 prerelease 回归),上线切 latest 通道后简化为
|
|
31
|
+
* 仅维护包名白名单。若某个包出现回归需要临时止血,把对应行从 `"latest"` 改为具体版本号
|
|
32
|
+
* 即可(用 Record<string, string> 结构而不是 Set,保留这个 escape hatch 的可能性)。
|
|
33
|
+
*/
|
|
34
|
+
const MANAGED_PLATFORM_PACKAGES = {
|
|
35
|
+
'@lark-apaas/auth-sdk': 'latest',
|
|
36
|
+
'@lark-apaas/client-toolkit': 'latest',
|
|
37
|
+
'@lark-apaas/client-toolkit-lite': 'latest',
|
|
38
|
+
'@lark-apaas/dataloom': 'latest',
|
|
39
|
+
'@lark-apaas/db-schema-sync': 'latest',
|
|
40
|
+
'@lark-apaas/express-core': 'latest',
|
|
41
|
+
'@lark-apaas/fullstack-cli': 'latest',
|
|
42
|
+
'@lark-apaas/fullstack-nestjs-core': 'latest',
|
|
43
|
+
'@lark-apaas/fullstack-rspack-preset': 'latest',
|
|
44
|
+
'@lark-apaas/fullstack-vite-preset': 'latest',
|
|
45
|
+
'@lark-apaas/http-client': 'latest',
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* miaoda app sync [--dir <path>]
|
|
49
|
+
*
|
|
50
|
+
* 一站式同步:把当前 stack 的 sync 规则全套 apply 到 user app,对齐沙箱里
|
|
51
|
+
* `npm run upgrade → npx fullstack-cli sync` 的行为,并补 miaoda-cli 本地开发独有规则。
|
|
52
|
+
*
|
|
53
|
+
* 执行流:
|
|
54
|
+
* 1. SyncRule[] 全量 apply —— 来自 src/config/sync-configs/<stack>.ts。每个 stack
|
|
55
|
+
* 自带模板资源在 upgrade/templates/<stack>/templates/。表里没有该 stack 时回退到
|
|
56
|
+
* 旧 platform-sync 兜底(兼容尚未迁移的 stack)。
|
|
57
|
+
* 2. 平台 deps 指 latest —— user app 已装的 @lark-apaas/* 按 MANAGED_PLATFORM_PACKAGES
|
|
58
|
+
* 白名单改写为 "latest"(紧急止血时表里可填具体版本)。
|
|
59
|
+
* 3. activateGitHooks —— 设置 core.hooksPath(template 自带 .githooks/pre-commit)。
|
|
60
|
+
* 4. npm install --no-audit --no-fund --ignore-scripts —— 物化新版本,绕开
|
|
61
|
+
* action-plugin postinstall 在缺平台 env 时的 ENOENT。
|
|
62
|
+
*
|
|
63
|
+
* 需要 .spark/meta.json 已含 stack(走过 `miaoda app init`)。
|
|
64
|
+
* 不动 .agent/skills/(那是 `miaoda skills sync` 的事)。
|
|
65
|
+
*/
|
|
66
|
+
async function handleAppSync(opts) {
|
|
67
|
+
await Promise.resolve();
|
|
68
|
+
const targetDir = node_path_1.default.resolve(opts.dir ?? process.cwd());
|
|
69
|
+
const meta = (0, spark_meta_1.readSparkMeta)(targetDir);
|
|
70
|
+
if (meta.stack === undefined || meta.stack === '') {
|
|
71
|
+
throw new error_1.AppError('SYNC_META_INCOMPLETE', '.spark/meta.json missing stack — run `miaoda app init` first');
|
|
72
|
+
}
|
|
73
|
+
// 1. apply SyncRule[],stack 没注册时回退到旧 platform-sync 兜底;都不识别就报错
|
|
74
|
+
const syncResults = runStackSync(meta.stack, targetDir, 'sync');
|
|
75
|
+
if (!syncResults.stackFound) {
|
|
76
|
+
throw new error_1.AppError('SYNC_STACK_NOT_SUPPORTED', `stack '${meta.stack}' has no SyncConfig and no upgrade/templates/${meta.stack}/ — 该 stack 暂未纳入 sync 范围`, {
|
|
77
|
+
next_actions: [
|
|
78
|
+
`src/config/sync-configs/${meta.stack}.ts 加 SyncConfig 并在 STACK_REGISTRY 注册`,
|
|
79
|
+
],
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
// 2. @lark-apaas/* dep 统一指 latest(按 MANAGED_PLATFORM_PACKAGES 白名单,user app 已装才升)
|
|
83
|
+
const upgradedPackages = upgradePlatformDeps(targetDir, 'sync');
|
|
84
|
+
// 3. activate git hooks(template 自带 .githooks/pre-commit,core.hooksPath 一次性设置)
|
|
85
|
+
const hookActivation = (0, githooks_1.activateGitHooks)(targetDir);
|
|
86
|
+
// 4. npm install —— 跟 init 一样软失败,install 挂了不该阻断 emit / sync 总结输出。
|
|
87
|
+
// 把 user app 装着的**所有**管控包显式作为位置参数传给 npm install,强制 npm 重新
|
|
88
|
+
// resolve lockfile entry。原因:
|
|
89
|
+
// - 即便 package.json spec 已经是 "latest"(没有 from→to 变化),user app 老 lockfile
|
|
90
|
+
// 可能锁着具体老版本 (如 fullstack-vite-preset@1.0.9),纯 `npm install` 会按 lockfile
|
|
91
|
+
// 拉老版,绕过 spec "latest" 的解析意图
|
|
92
|
+
// - 显式 `npm install foo@latest bar@latest` 让 npm 重新解析,lockfile 跟着更新到当前 latest
|
|
93
|
+
// - 没装的管控包不动 (sync 的职责是对齐,不是塞依赖)
|
|
94
|
+
// --ignore-scripts 绕开 action-plugin postinstall 在缺平台 env 时的 ENOENT。
|
|
95
|
+
// --registry 跟 init 钉同一份 npmmirror,避免 user 全局 ~/.npmrc 把 sync 拉到另一个源
|
|
96
|
+
// (详见 services/app/init/install.ts 上的注释)。MIAODA_NPM_REGISTRY env 兜底。
|
|
97
|
+
const installArgs = [
|
|
98
|
+
'install',
|
|
99
|
+
'--no-audit',
|
|
100
|
+
'--no-fund',
|
|
101
|
+
'--ignore-scripts',
|
|
102
|
+
'--registry',
|
|
103
|
+
(0, install_1.resolveNpmInstallRegistry)(),
|
|
104
|
+
];
|
|
105
|
+
for (const name of listManagedDeps(targetDir)) {
|
|
106
|
+
installArgs.push(`${name}@${MANAGED_PLATFORM_PACKAGES[name]}`);
|
|
107
|
+
}
|
|
108
|
+
(0, logger_1.log)('sync', `Running npm ${installArgs.join(' ')}...`);
|
|
109
|
+
let installError;
|
|
110
|
+
try {
|
|
111
|
+
(0, node_child_process_1.execFileSync)('npm', installArgs, {
|
|
112
|
+
cwd: targetDir,
|
|
113
|
+
stdio: (0, output_1.isJsonMode)() ? ['ignore', 'ignore', 'inherit'] : 'inherit',
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
catch (err) {
|
|
117
|
+
installError = err instanceof Error ? err.message : String(err);
|
|
118
|
+
(0, logger_1.log)('sync', `⚠ npm install failed (continuing): ${installError}`);
|
|
119
|
+
}
|
|
120
|
+
(0, output_1.emit)({
|
|
121
|
+
data: {
|
|
122
|
+
stack: meta.stack,
|
|
123
|
+
...summarizeSyncResults(syncResults),
|
|
124
|
+
upgradedPackages,
|
|
125
|
+
gitHooks: hookActivation.action,
|
|
126
|
+
installError,
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
/** 老命令 `miaoda app upgrade` 的 handler 名,保留为 sync handler 的 alias。 */
|
|
131
|
+
exports.handleAppUpgrade = handleAppSync;
|
|
132
|
+
/**
|
|
133
|
+
* 给 stack 应用 sync rules(sync handler + init handler 共用)。
|
|
134
|
+
* stack 在 STACK_REGISTRY 注册时走新 SyncRule 机制,否则回落到旧 platform-sync。
|
|
135
|
+
*
|
|
136
|
+
* stack 完全不识别(既不在 STACK_REGISTRY 也无 upgrade/templates/<stack>/ 目录)时返回
|
|
137
|
+
* `stackFound: false` 而非抛错 —— 让调用方按场景决定:sync handler 抛 AppError,init handler
|
|
138
|
+
* 允许(fresh init 时 stack 还在加注册的过渡期)。
|
|
139
|
+
*/
|
|
140
|
+
function runStackSync(stack, targetDir, logPrefix) {
|
|
141
|
+
const config = (0, sync_configs_1.getSyncConfig)(stack);
|
|
142
|
+
if (config !== null) {
|
|
143
|
+
const sourceRoot = (0, sync_configs_1.getStackTemplatesRoot)(stack);
|
|
144
|
+
if (!node_fs_1.default.existsSync(sourceRoot)) {
|
|
145
|
+
throw new error_1.AppError('SYNC_TEMPLATES_MISSING', `upgrade/templates/${stack}/templates/ not found at ${sourceRoot} — cli 安装异常?`);
|
|
146
|
+
}
|
|
147
|
+
(0, logger_1.log)(logPrefix, `Applying ${config.sync.length.toString()} sync rule(s) for stack '${stack}'`);
|
|
148
|
+
const rulesApplied = (0, sync_rule_1.applySyncRules)(config.sync, { sourceRoot, targetDir, logPrefix });
|
|
149
|
+
return { stackFound: true, rulesApplied };
|
|
150
|
+
}
|
|
151
|
+
// fallback:表外 stack 仍用旧 platform-sync 机制(files/ overlay + patches/ deep merge)
|
|
152
|
+
(0, logger_1.log)(logPrefix, `Stack '${stack}' not in new sync registry, falling back to legacy platform-sync`);
|
|
153
|
+
const legacy = (0, platform_sync_1.syncPlatformControlled)({ stack, targetDir, logPrefix });
|
|
154
|
+
return {
|
|
155
|
+
stackFound: legacy.stackFound,
|
|
156
|
+
rulesApplied: [],
|
|
157
|
+
legacy: { syncedFiles: legacy.syncedFiles, mergedJsonFiles: legacy.mergedJsonFiles },
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
/** 把 SyncRuleResult[] / legacy 输出汇总成 emit data 里的几组字段,便于诊断。 */
|
|
161
|
+
function summarizeSyncResults(result) {
|
|
162
|
+
if (result.legacy !== undefined) {
|
|
163
|
+
return {
|
|
164
|
+
appliedRules: [],
|
|
165
|
+
syncedFiles: result.legacy.syncedFiles,
|
|
166
|
+
mergedJsonFiles: result.legacy.mergedJsonFiles,
|
|
167
|
+
patchedScripts: [],
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
const appliedRules = result.rulesApplied.map((r) => ({
|
|
171
|
+
type: r.rule.type,
|
|
172
|
+
action: r.action,
|
|
173
|
+
path: r.path,
|
|
174
|
+
detail: r.detail,
|
|
175
|
+
}));
|
|
176
|
+
const syncedFiles = [];
|
|
177
|
+
for (const r of result.rulesApplied) {
|
|
178
|
+
if ((r.action === 'synced' || r.action === 'created') && r.path !== undefined) {
|
|
179
|
+
syncedFiles.push(r.path);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
const mergedJsonFiles = [];
|
|
183
|
+
for (const r of result.rulesApplied) {
|
|
184
|
+
if (r.rule.type !== 'merge-json')
|
|
185
|
+
continue;
|
|
186
|
+
if (r.action !== 'merged' && r.action !== 'created')
|
|
187
|
+
continue;
|
|
188
|
+
if (r.path === undefined)
|
|
189
|
+
continue;
|
|
190
|
+
mergedJsonFiles.push({
|
|
191
|
+
path: r.path,
|
|
192
|
+
keys: r.detail !== undefined ? r.detail.split(',').filter(Boolean) : [],
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
const patchedScripts = result.rulesApplied
|
|
196
|
+
.filter((r) => (r.rule.type === 'patch-script' || r.rule.type === 'add-script') &&
|
|
197
|
+
(r.action === 'patched' || r.action === 'created'))
|
|
198
|
+
.map((r) => r.rule.name);
|
|
199
|
+
return { appliedRules, syncedFiles, mergedJsonFiles, patchedScripts };
|
|
200
|
+
}
|
|
201
|
+
function readPackageJson(pkgJsonPath) {
|
|
202
|
+
if (!node_fs_1.default.existsSync(pkgJsonPath))
|
|
203
|
+
return null;
|
|
204
|
+
return JSON.parse(node_fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* 列出 user app 装着的 @lark-apaas/* 管控包 (在 MANAGED_PLATFORM_PACKAGES 白名单内的)。
|
|
208
|
+
* sync handler 用来给 `npm install` 显式列管控包名 + @latest,强制 npm 重新 resolve
|
|
209
|
+
* lockfile entry —— 即便 package.json spec 已经是 "latest" 没 from→to 变化,
|
|
210
|
+
* 老 lockfile 锁的具体老版本(如 fullstack-vite-preset@1.0.9)也会被刷掉。
|
|
211
|
+
*/
|
|
212
|
+
function listManagedDeps(targetDir) {
|
|
213
|
+
const pkg = readPackageJson(node_path_1.default.join(targetDir, 'package.json'));
|
|
214
|
+
if (!pkg)
|
|
215
|
+
return [];
|
|
216
|
+
const names = new Set();
|
|
217
|
+
for (const section of ['dependencies', 'devDependencies']) {
|
|
218
|
+
const deps = pkg[section];
|
|
219
|
+
if (!deps || typeof deps !== 'object')
|
|
220
|
+
continue;
|
|
221
|
+
for (const name of Object.keys(deps)) {
|
|
222
|
+
if (Object.prototype.hasOwnProperty.call(MANAGED_PLATFORM_PACKAGES, name)) {
|
|
223
|
+
names.add(name);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
return [...names];
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* 把 user app package.json 里已装的 @lark-apaas/* 平台包改写成 MANAGED_PLATFORM_PACKAGES
|
|
231
|
+
* 表里的目标 spec(缺省 `"latest"`)。由 `miaoda app sync` 和 `miaoda app init`(template
|
|
232
|
+
* 渲染后、install 之前)共用。
|
|
233
|
+
*
|
|
234
|
+
* - user app 已装的管控包 → 写表里的目标 spec(默认 latest,紧急止血时可改具体版本)
|
|
235
|
+
* - user app 未装的管控包 → 不引入(sync 的职责是对齐,不是塞依赖)
|
|
236
|
+
* - 装着但不在白名单的 @lark-apaas/* 包 → 完全不动
|
|
237
|
+
*
|
|
238
|
+
* @param logPrefix 用于 `[<prefix>] deps.xxx: a → b` 日志前缀;sync 流程传 'sync',init 流程传 'init'。
|
|
239
|
+
*/
|
|
240
|
+
function upgradePlatformDeps(targetDir, logPrefix = 'sync') {
|
|
241
|
+
const pkgJsonPath = node_path_1.default.join(targetDir, 'package.json');
|
|
242
|
+
const pkg = readPackageJson(pkgJsonPath);
|
|
243
|
+
if (!pkg)
|
|
244
|
+
return [];
|
|
245
|
+
const updated = [];
|
|
246
|
+
for (const section of ['dependencies', 'devDependencies']) {
|
|
247
|
+
const deps = pkg[section];
|
|
248
|
+
if (!deps || typeof deps !== 'object')
|
|
249
|
+
continue;
|
|
250
|
+
for (const name of Object.keys(deps)) {
|
|
251
|
+
if (!Object.prototype.hasOwnProperty.call(MANAGED_PLATFORM_PACKAGES, name))
|
|
252
|
+
continue;
|
|
253
|
+
const target = MANAGED_PLATFORM_PACKAGES[name];
|
|
254
|
+
const from = deps[name];
|
|
255
|
+
if (from === target)
|
|
256
|
+
continue;
|
|
257
|
+
deps[name] = target;
|
|
258
|
+
updated.push({ name, from, to: target, section });
|
|
259
|
+
(0, logger_1.log)(logPrefix, ` ${section}.${name}: ${from} → ${target}`);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (updated.length > 0) {
|
|
263
|
+
node_fs_1.default.writeFileSync(pkgJsonPath, JSON.stringify(pkg, null, 2) + '\n', 'utf-8');
|
|
264
|
+
}
|
|
265
|
+
return updated;
|
|
266
|
+
}
|
|
@@ -3,10 +3,46 @@ 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.parseDeployConf = parseDeployConf;
|
|
6
7
|
exports.handleDeployModern = handleDeployModern;
|
|
7
8
|
const node_path_1 = __importDefault(require("node:path"));
|
|
8
9
|
const output_1 = require("../../../utils/output");
|
|
10
|
+
const error_1 = require("../../../utils/error");
|
|
9
11
|
const index_1 = require("../../../services/deploy/modern/index");
|
|
12
|
+
const CONF_EXPECTED = '期望形如 {"checkPointVersion":"...","commitID":"..."} 的 JSON 对象';
|
|
13
|
+
/**
|
|
14
|
+
* 解析 `--conf` JSON string,只取 checkPointVersion / commitID 两个白名单字段。
|
|
15
|
+
* - 未传 → 返回 {}(行为不变)。
|
|
16
|
+
* - 非法 JSON / 非 object / 字段值非 string → 抛 DEPLOY_CONF_INVALID。
|
|
17
|
+
* - 空串字段视为未提供(不进 body,保持可选语义);未知 key 忽略。
|
|
18
|
+
*/
|
|
19
|
+
function parseDeployConf(raw) {
|
|
20
|
+
if (raw === undefined || raw === '')
|
|
21
|
+
return {};
|
|
22
|
+
let parsed;
|
|
23
|
+
try {
|
|
24
|
+
parsed = JSON.parse(raw);
|
|
25
|
+
}
|
|
26
|
+
catch {
|
|
27
|
+
throw new error_1.AppError('DEPLOY_CONF_INVALID', `--conf 不是合法 JSON,${CONF_EXPECTED}`);
|
|
28
|
+
}
|
|
29
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
30
|
+
throw new error_1.AppError('DEPLOY_CONF_INVALID', `--conf 必须是 JSON 对象,${CONF_EXPECTED}`);
|
|
31
|
+
}
|
|
32
|
+
const source = parsed;
|
|
33
|
+
const conf = {};
|
|
34
|
+
for (const key of ['checkPointVersion', 'commitID']) {
|
|
35
|
+
const value = source[key];
|
|
36
|
+
if (value === undefined)
|
|
37
|
+
continue;
|
|
38
|
+
if (typeof value !== 'string') {
|
|
39
|
+
throw new error_1.AppError('DEPLOY_CONF_INVALID', `--conf.${key} 必须是字符串,${CONF_EXPECTED}`);
|
|
40
|
+
}
|
|
41
|
+
if (value !== '')
|
|
42
|
+
conf[key] = value;
|
|
43
|
+
}
|
|
44
|
+
return conf;
|
|
45
|
+
}
|
|
10
46
|
/**
|
|
11
47
|
* miaoda deploy(modern scene 专用,CLI 表面对齐 openclaw-cli)
|
|
12
48
|
*
|
|
@@ -14,10 +50,13 @@ const index_1 = require("../../../services/deploy/modern/index");
|
|
|
14
50
|
*/
|
|
15
51
|
async function handleDeployModern(opts) {
|
|
16
52
|
const projectDir = node_path_1.default.resolve(opts.dir);
|
|
53
|
+
const conf = parseDeployConf(opts.conf);
|
|
17
54
|
const result = await (0, index_1.runModernDeploy)({
|
|
18
55
|
projectDir,
|
|
19
56
|
appId: opts.appId,
|
|
20
57
|
skipBuild: opts.skipBuild ?? false,
|
|
58
|
+
checkPointVersion: conf.checkPointVersion,
|
|
59
|
+
commitID: conf.commitID,
|
|
21
60
|
});
|
|
22
61
|
(0, output_1.emit)({
|
|
23
62
|
data: {
|
|
@@ -76,7 +76,7 @@ function parsePluginName(input) {
|
|
|
76
76
|
if (!match) {
|
|
77
77
|
throw new error_1.AppError('INVALID_PLUGIN_NAME', `Invalid plugin name format: ${input}. Expected: @scope/name or @scope/name@version`, { next_actions: ['示例:@demo/example-plugin 或 @demo/example-plugin@1.2.3'] });
|
|
78
78
|
}
|
|
79
|
-
return { name: match[1], version: match[2]
|
|
79
|
+
return { name: match[1], version: match[2] || 'latest' };
|
|
80
80
|
}
|
|
81
81
|
// ── package.json actionPlugins CRUD ──
|
|
82
82
|
function readPackageJson() {
|
|
@@ -10,10 +10,10 @@ const spark_meta_1 = require("../../../utils/spark-meta");
|
|
|
10
10
|
const error_1 = require("../../../utils/error");
|
|
11
11
|
const output_1 = require("../../../utils/output");
|
|
12
12
|
/**
|
|
13
|
-
* miaoda skills sync [
|
|
13
|
+
* miaoda skills sync [version] [--dir <path>]
|
|
14
14
|
*
|
|
15
|
-
* 从 .spark/meta.json 读 stack
|
|
16
|
-
* 同步到 <dir>/.agent/steering/。需要先跑过 `miaoda app init`。
|
|
15
|
+
* 从 .spark/meta.json 读 stack(+ 默认 steeringVersion),把 coding-steering 包内对应 stack
|
|
16
|
+
* 的 skills + tech.md 同步到 <dir>/.agent/steering/。需要先跑过 `miaoda app init`。
|
|
17
17
|
*/
|
|
18
18
|
async function handleSkillsSync(opts) {
|
|
19
19
|
await Promise.resolve();
|
|
@@ -22,17 +22,28 @@ async function handleSkillsSync(opts) {
|
|
|
22
22
|
if (meta.stack === undefined || meta.stack === '') {
|
|
23
23
|
throw new error_1.AppError('SKILLS_META_INCOMPLETE', '.spark/meta.json missing stack — run `miaoda app init` first');
|
|
24
24
|
}
|
|
25
|
+
const version = opts.version ?? meta.steeringVersion;
|
|
26
|
+
const versionSource = opts.version
|
|
27
|
+
? 'cli-arg'
|
|
28
|
+
: meta.steeringVersion
|
|
29
|
+
? 'meta.json'
|
|
30
|
+
: 'default-latest';
|
|
25
31
|
const result = (0, coding_steering_1.syncCodingSteering)({
|
|
26
32
|
stack: meta.stack,
|
|
27
33
|
targetDir,
|
|
28
|
-
version
|
|
34
|
+
version,
|
|
35
|
+
outputLayout: opts.local === true ? 'flat' : 'nested',
|
|
29
36
|
});
|
|
30
37
|
(0, output_1.emit)({
|
|
31
38
|
data: {
|
|
32
39
|
stack: meta.stack,
|
|
33
40
|
version: result.version,
|
|
41
|
+
versionSource,
|
|
34
42
|
syncedSkills: result.syncedSkills,
|
|
35
43
|
techSynced: result.techSynced,
|
|
44
|
+
...(result.claudeSkillsLink !== undefined
|
|
45
|
+
? { claudeSkillsLink: result.claudeSkillsLink }
|
|
46
|
+
: {}),
|
|
36
47
|
},
|
|
37
48
|
});
|
|
38
49
|
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FULLSTACK_CLI_PIN_SPEC = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* `@lark-apaas/fullstack-cli` 的 npx 调用 spec。
|
|
6
|
+
*
|
|
7
|
+
* 用于 user app `package.json` 里所有 `npx -y @lark-apaas/fullstack-cli ...` 调用 ——
|
|
8
|
+
* sync 规则会按本常量生成 `npx -y <FULLSTACK_CLI_PIN_SPEC> ...`。常量化是为了:
|
|
9
|
+
* - 跟 src/cli/handlers/app/sync.ts 里 MANAGED_PLATFORM_PACKAGES['@lark-apaas/fullstack-cli']
|
|
10
|
+
* 的 spec 保持一致(user app deps 跟 npm script 里 npx 调用同步切渠道)
|
|
11
|
+
* - 紧急止血时改一行就能把所有 user app 的 npm script 改回钉版(例如 `@1.1.49-alpha.0`)
|
|
12
|
+
*/
|
|
13
|
+
exports.FULLSTACK_CLI_PIN_SPEC = '@lark-apaas/fullstack-cli@latest';
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* design-stack 的 sync 规则。
|
|
4
|
+
*
|
|
5
|
+
* design-stack 是 SSR-only 全栈 stack(design scene,MIAODA_APP_TYPE=4),无 nest 后端、
|
|
6
|
+
* 无数据库、无 nestjs-cli。所以规则集是 nestjs-react-fullstack 的子集:只保留
|
|
7
|
+
* scripts/.githooks/ 派生 + 基础 npm scripts,不动 nest-cli.json / drizzle / tsconfig.node.json。
|
|
8
|
+
*
|
|
9
|
+
* dev-local.js 跟 nestjs-react-fullstack 不同(单进程 npm run dev,MIAODA_APP_TYPE=4),
|
|
10
|
+
* 单独维护在 upgrade/templates/design-stack/templates/scripts/dev-local.js。
|
|
11
|
+
*/
|
|
12
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
13
|
+
exports.SYNC_CONFIG = void 0;
|
|
14
|
+
const fullstack_cli_pin_1 = require("../../config/fullstack-cli-pin");
|
|
15
|
+
exports.SYNC_CONFIG = {
|
|
16
|
+
sync: [
|
|
17
|
+
// 1. 派生 scripts/(含 design 专属 dev-local.js + 通用 dev.sh / hooks/run-precommit.js)
|
|
18
|
+
{
|
|
19
|
+
type: 'directory',
|
|
20
|
+
from: 'scripts',
|
|
21
|
+
to: 'scripts',
|
|
22
|
+
overwrite: true,
|
|
23
|
+
},
|
|
24
|
+
// 1a. 同步 .githooks/(hook 入口,可执行 sh)
|
|
25
|
+
{
|
|
26
|
+
type: 'directory',
|
|
27
|
+
from: '.githooks',
|
|
28
|
+
to: '.githooks',
|
|
29
|
+
overwrite: true,
|
|
30
|
+
},
|
|
31
|
+
// 1b. scripts.prepare:npm install 后激活 git hooks
|
|
32
|
+
{
|
|
33
|
+
type: 'add-script',
|
|
34
|
+
name: 'prepare',
|
|
35
|
+
command: 'chmod +x .githooks/pre-commit 2>/dev/null; git config core.hooksPath .githooks 2>/dev/null || true',
|
|
36
|
+
overwrite: false,
|
|
37
|
+
},
|
|
38
|
+
// 1c. scripts.precommit
|
|
39
|
+
{
|
|
40
|
+
type: 'add-script',
|
|
41
|
+
name: 'precommit',
|
|
42
|
+
command: 'node scripts/hooks/run-precommit.js',
|
|
43
|
+
overwrite: false,
|
|
44
|
+
},
|
|
45
|
+
// 2. .gitignore 卫生:移除 package-lock.json + 加 .agent/
|
|
46
|
+
{
|
|
47
|
+
type: 'remove-line',
|
|
48
|
+
to: '.gitignore',
|
|
49
|
+
pattern: 'package-lock.json',
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
type: 'add-line',
|
|
53
|
+
to: '.gitignore',
|
|
54
|
+
line: '.agent/',
|
|
55
|
+
},
|
|
56
|
+
// 3. 老 npm scripts 迁移(裸 fullstack-cli → 钉版 npx)。两条 ifStartsWith 分别覆盖
|
|
57
|
+
// "裸 fullstack-cli"(最老形态)和 "npx 未钉版" 形态,统一升到 FULLSTACK_CLI_PIN_SPEC。
|
|
58
|
+
{
|
|
59
|
+
type: 'patch-script',
|
|
60
|
+
name: 'postinstall',
|
|
61
|
+
to: `npx -y ${fullstack_cli_pin_1.FULLSTACK_CLI_PIN_SPEC} action-plugin init`,
|
|
62
|
+
ifStartsWith: 'fullstack-cli action-plugin init',
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
type: 'patch-script',
|
|
66
|
+
name: 'postinstall',
|
|
67
|
+
to: `npx -y ${fullstack_cli_pin_1.FULLSTACK_CLI_PIN_SPEC} action-plugin init`,
|
|
68
|
+
ifStartsWith: 'npx -y @lark-apaas/fullstack-cli action-plugin init',
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
type: 'patch-script',
|
|
72
|
+
name: 'upgrade',
|
|
73
|
+
to: `npx -y ${fullstack_cli_pin_1.FULLSTACK_CLI_PIN_SPEC} sync --disable-gen-openapi`,
|
|
74
|
+
ifStartsWith: 'fullstack-cli sync',
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
type: 'patch-script',
|
|
78
|
+
name: 'upgrade',
|
|
79
|
+
to: `npx -y ${fullstack_cli_pin_1.FULLSTACK_CLI_PIN_SPEC} sync --disable-gen-openapi`,
|
|
80
|
+
ifStartsWith: 'npx -y @lark-apaas/fullstack-cli sync',
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: 'patch-script',
|
|
84
|
+
name: 'dev',
|
|
85
|
+
to: './scripts/dev.sh',
|
|
86
|
+
ifStartsWith: 'npm run upgrade && ',
|
|
87
|
+
},
|
|
88
|
+
// ===== miaoda-cli 本地开发新增规则 =====
|
|
89
|
+
// M1. scripts.dev:local —— 本地用户绕过 SANDBOX_ID 分发直接跑本地链路(npm run dev:local)。
|
|
90
|
+
{
|
|
91
|
+
type: 'add-script',
|
|
92
|
+
name: 'dev:local',
|
|
93
|
+
command: 'node ./scripts/dev-local.js',
|
|
94
|
+
overwrite: false,
|
|
95
|
+
},
|
|
96
|
+
],
|
|
97
|
+
};
|
|
98
|
+
exports.default = exports.SYNC_CONFIG;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* 按 stack 短名拿到对应的 SyncConfig + 模板源根目录。
|
|
4
|
+
*
|
|
5
|
+
* - 类型化的 SyncConfig 放在 src/config/sync-configs/<stack>.ts(被 tsc 编进 dist 一起发)。
|
|
6
|
+
* - 模板资源文件放在 upgrade/templates/<stack>/templates/(cli 发布时整个目录原样 ship)。
|
|
7
|
+
*
|
|
8
|
+
* handler 拿到 stack 后,先 getSyncConfig 拿 rule 列表,再 getStackTemplatesRoot 拿模板根,
|
|
9
|
+
* 一起喂给 applySyncRules。
|
|
10
|
+
*
|
|
11
|
+
* 新加 stack:
|
|
12
|
+
* 1. src/config/sync-configs/<stack>.ts 里 export default SYNC_CONFIG
|
|
13
|
+
* 2. upgrade/templates/<stack>/templates/ 下放对应资源文件
|
|
14
|
+
* 3. 在 STACK_REGISTRY 里加一条
|
|
15
|
+
*/
|
|
16
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
17
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
18
|
+
};
|
|
19
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
20
|
+
exports.getSyncConfig = getSyncConfig;
|
|
21
|
+
exports.getStackTemplatesRoot = getStackTemplatesRoot;
|
|
22
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
23
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
24
|
+
const nestjs_react_fullstack_1 = __importDefault(require("./nestjs-react-fullstack"));
|
|
25
|
+
const design_stack_1 = __importDefault(require("./design-stack"));
|
|
26
|
+
/**
|
|
27
|
+
* 已纳入新 sync 机制的 stack 注册表。未在表里的 stack(如老 vite-react / html)走旧的
|
|
28
|
+
* `upgrade/templates/<stack>/{files,patches}` 机制,由 sync handler 兜底。
|
|
29
|
+
*/
|
|
30
|
+
const STACK_REGISTRY = {
|
|
31
|
+
'nestjs-react-fullstack': nestjs_react_fullstack_1.default,
|
|
32
|
+
'design-stack': design_stack_1.default,
|
|
33
|
+
};
|
|
34
|
+
/** 返回该 stack 的 SyncConfig;表里没有时返回 null。 */
|
|
35
|
+
function getSyncConfig(stack) {
|
|
36
|
+
return STACK_REGISTRY[stack] ?? null;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* 计算 stack 模板资源根目录(绝对路径)。
|
|
40
|
+
*
|
|
41
|
+
* cli 发布时 upgrade/ 目录整个 ship,编译产物在 dist/ 下若干层。从当前模块向上回溯找含
|
|
42
|
+
* upgrade/ + package.json 的目录就是 cli 根。
|
|
43
|
+
*
|
|
44
|
+
* 跟 utils/platform-sync.ts 同款 pattern;保持对齐避免两边漂移。
|
|
45
|
+
*/
|
|
46
|
+
function getStackTemplatesRoot(stack) {
|
|
47
|
+
return node_path_1.default.join(findCliRoot(), 'upgrade', 'templates', stack, 'templates');
|
|
48
|
+
}
|
|
49
|
+
let cliRootCache = null;
|
|
50
|
+
function findCliRoot() {
|
|
51
|
+
if (cliRootCache !== null)
|
|
52
|
+
return cliRootCache;
|
|
53
|
+
let dir = __dirname;
|
|
54
|
+
while (dir !== node_path_1.default.dirname(dir)) {
|
|
55
|
+
if (node_fs_1.default.existsSync(node_path_1.default.join(dir, 'upgrade')) && node_fs_1.default.existsSync(node_path_1.default.join(dir, 'package.json'))) {
|
|
56
|
+
cliRootCache = dir;
|
|
57
|
+
return dir;
|
|
58
|
+
}
|
|
59
|
+
dir = node_path_1.default.dirname(dir);
|
|
60
|
+
}
|
|
61
|
+
throw new Error(`miaoda-cli root (containing upgrade/) not found from ${__dirname}`);
|
|
62
|
+
}
|