@lark-apaas/miaoda-cli 0.1.21-alpha.0 → 0.1.21-beta.71cd6c4

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.
@@ -164,6 +164,8 @@ function registerAppInit(parent) {
164
164
  .command('init')
165
165
  .description('初始化应用代码:抓 template 渲染、同步 upgrade/templates、装 .agent/steering/ skills、写 .spark/meta.json。.spark/meta.json 已存在则直接退出')
166
166
  .option('--template <stack>', `技术栈短名(${index_1.SUPPORTED_STACKS.join(' / ')})`)
167
+ .option('--app-type <type>', 'aPaaS 业务类型(html / modern_html / jspage / full_stack);未传 --template 时用于解析技术栈')
168
+ .option('--source-path <path>', '导入源目录:把该目录内容聚合进脚手架 src/(当前仅 --app-type modern_html 生效)')
167
169
  .option('--conf <json>', 'init 配置 JSON。支持 {"version": "<template 版本>"},默认 latest')
168
170
  .option('--skip-install', '跳过依赖安装', false)
169
171
  .option('--async-install', '派发后台进程装依赖并立即返回(与 --skip-install 互斥)', false)
@@ -180,6 +182,17 @@ function registerAppInit(parent) {
180
182
  未提供则不写。运行端命令(deploy / file / plugin 等)仍优先读 MIAODA_APP_ID env,
181
183
  env 未设置时回退到 meta.json.app_id。
182
184
 
185
+ 技术栈解析(--template / --app-type)
186
+ --template 传了 → 直接用(vite-react / html / nestjs-react-fullstack)
187
+ 否则 --app-type 传了 → 查表:html→html, modern_html→html, jspage→vite-react, full_stack→nestjs-react-fullstack
188
+ 两者都没传 → 报错 ARGS_INVALID
189
+
190
+ 源码导入(--source-path)
191
+ 仅 --app-type modern_html 生效:把 <source-path> 整个目录递归聚合进 src/(同名覆盖,叠加保留)。
192
+ 过滤:跳过 node_modules/.git/dist/build 目录,跳过 .env/.env.*/.DS_Store/.npmrc/package.json/lockfiles。
193
+ source-path 不存在 → 报错;非 modern_html 传了 source-path → 警告并忽略,继续正常 init。
194
+ app_type 不写入 .spark/meta.json。
195
+
183
196
  平台同步(upgrade/templates)
184
197
  init 内嵌一次 upgrade/templates 同步(跟 app upgrade 共用同一份 util):
185
198
  从 miaoda-cli 自带的 upgrade/templates/<stack>/ 同步 files/(覆盖)+ patches/
@@ -208,7 +221,8 @@ function registerAppInit(parent) {
208
221
  JSON 输出
209
222
  已初始化:{"data": {"initialized": false, "reason": "already_initialized", "targetDir": "..."}}
210
223
  新初始化:{"data": {"initialized": true, "template": "...", "templateVersion": "...", "steeringVersion": "...",
211
- "appId": "...", "platformStackFound": true, "platformSyncedFiles": [...],
224
+ "appId": "...", "appType": "modern_html", "sourceImported": true, "importedFileCount": 7,
225
+ "platformStackFound": true, "platformSyncedFiles": [...],
212
226
  "installed": true, "installSource": "cache|npm|skipped", "installHash": "...", ...}}
213
227
  async 模式:{"data": {"initialized": true, "asyncInstall": true, "installed": false,
214
228
  "installSource": "async", "installPid": 123, "installLogPath": "...",
@@ -220,12 +234,16 @@ JSON 输出
220
234
  $ miaoda app init --template vite-react --conf '{"version": "0.1.0"}'
221
235
  $ miaoda app init --template vite-react --skip-install
222
236
  $ miaoda app init --template vite-react --async-install
237
+ $ miaoda app init --app-type full_stack --app-id app_demo_xxx
238
+ $ miaoda app init --app-type modern_html --source-path ./my-assets
223
239
  $ MIAODA_DEP_CACHE_DIR=/tmp/dep-cache miaoda app init --template vite-react
224
240
  `);
225
241
  cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
226
242
  const conf = parseInitConf(rawOpts.conf);
227
243
  await (0, index_1.handleAppInit)({
228
244
  template: rawOpts.template,
245
+ appType: rawOpts.appType,
246
+ sourcePath: rawOpts.sourcePath,
229
247
  conf,
230
248
  skipInstall: rawOpts.skipInstall,
231
249
  appId: rawOpts.appId,
@@ -51,23 +51,15 @@ async function handleAppInit(opts) {
51
51
  });
52
52
  return;
53
53
  }
54
- const stack = opts.template;
55
- if (!stack) {
56
- throw new error_1.AppError('ARGS_INVALID', '缺少 --template <stack>', {
57
- next_actions: [`可用 stack:${index_1.SUPPORTED_STACKS.join(', ')}`],
58
- });
59
- }
60
- if (!index_1.SUPPORTED_STACKS.includes(stack)) {
61
- throw new error_1.AppError('ARGS_INVALID', `不支持的 template: ${stack}`, {
62
- next_actions: [`可用 stack:${index_1.SUPPORTED_STACKS.join(', ')}`],
63
- });
64
- }
54
+ const stack = (0, index_1.resolveStack)({ template: opts.template, appType: opts.appType });
65
55
  if (opts.asyncInstall && opts.skipInstall) {
66
56
  throw new error_1.AppError('ARGS_INVALID', '--async-install 与 --skip-install 互斥');
67
57
  }
68
58
  const version = opts.conf?.version;
69
59
  const projectName = node_path_1.default.basename(targetDir);
70
60
  const tplResult = (0, index_1.renderTemplate)({ stack, version, targetDir, projectName });
61
+ // source_path 导入:当前仅 app_type=modern_html 生效。渲染完模板后、装依赖前叠加进 src/。
62
+ const sourceImport = applySourcePathImport(opts, targetDir);
71
63
  // 先建 logs/,防止 user 跑 dev 之前 AI/工具 redirect 到 logs/*.log 因为父目录不存在直接挂
72
64
  // (dev.js / dev-local.js 自己也建 logs/,但只在它启动后;启动前的 shell redirect 会先于此)
73
65
  (0, logs_dir_1.ensureLogsDir)(targetDir);
@@ -135,6 +127,11 @@ async function handleAppInit(opts) {
135
127
  steeringVersion: steeringResult.version,
136
128
  archType: tplResult.archType,
137
129
  appId: opts.appId,
130
+ appType: opts.appType,
131
+ sourcePath: opts.sourcePath,
132
+ sourceImported: sourceImport.sourceImported,
133
+ importedFileCount: sourceImport.importedFileCount,
134
+ sourceIgnoredReason: sourceImport.sourceIgnoredReason,
138
135
  syncedSkills: steeringResult.syncedSkills,
139
136
  techSynced: steeringResult.techSynced,
140
137
  steeringError,
@@ -159,3 +156,25 @@ async function handleAppInit(opts) {
159
156
  },
160
157
  });
161
158
  }
159
+ /**
160
+ * 处理 --source-path 导入:当前仅 app_type=modern_html 生效。
161
+ * - 未传 source_path → 什么都不做
162
+ * - modern_html → 调 importSourceSrc 把源目录聚合进 targetDir/src/
163
+ * - 传了 source_path 但非 modern_html → 警告并忽略,继续正常 init(退出码 0)
164
+ * 终端提示留在 handler 层(service 不做终端输出)。
165
+ */
166
+ function applySourcePathImport(opts, targetDir) {
167
+ // 真值判断:未传或空串都视为"无 source_path"。
168
+ if (!opts.sourcePath) {
169
+ return { sourceImported: false };
170
+ }
171
+ if (opts.appType === 'modern_html') {
172
+ const { importedFileCount } = (0, index_1.importSourceSrc)({ sourcePath: opts.sourcePath, targetDir });
173
+ return { sourceImported: true, importedFileCount };
174
+ }
175
+ (0, logger_1.log)('init', '⚠ source-path ignored: 仅 app_type=modern_html 支持 source_path 导入');
176
+ if (!(0, output_1.isJsonMode)()) {
177
+ process.stdout.write('⚠ 不支持 source_path(仅 app_type=modern_html 生效),已忽略\n');
178
+ }
179
+ return { sourceImported: false, sourceIgnoredReason: 'not_modern_html' };
180
+ }
@@ -161,34 +161,6 @@ async function handleAppMigrate(opts) {
161
161
  }
162
162
  }
163
163
  }
164
- // 预热 vite deps cache (清 .vite 之后, pkill 之前) —— 把 standalone optimize
165
- // 提前在干净状态下跑一次, 写出 _metadata.json + 当前 lockfileHash。下一步 pkill
166
- // 让 supervisor 重启 dev:client, vite 启动检测到 metadata.lockfileHash 跟当前
167
- // lockfile 一致, 跳过 Re-optimize, 也就跳过了 incremental optimization 中
168
- // transform cache 跟 deps middleware metadata 短暂不同步那个窗口(详见
169
- // fullstack-plugin#1152 的 504 fix 讨论)。
170
- //
171
- // 顺序约束:必须在清 .vite 之后(否则 prewarm 写的 metadata 会被清掉, 白干)
172
- // 也必须在 pkill 之前(否则 supervisor 拉起的 vite 已经 cold start, prewarm
173
- // 没机会赶在它前面写 metadata)。
174
- //
175
- // - 只在 SANDBOX_ID 非空时做(本地环境 vite 自带 optimizer 不卡, 没必要拖慢 migrate)
176
- // - 只在 install 成功时做(install 失败时 node_modules 状态不对, optimize 也会挂)
177
- // - 软失败:optimize 挂了不阻断后续 pkill —— 即使没预热成功, supervisor 起的
178
- // vite 自己跑 optimizer 大多数场景能通过, 不要因为这一步丢掉重启 dev 的机会
179
- if (installError === undefined && (0, env_1.isSandboxEnv)()) {
180
- (0, logger_1.log)('migrate', 'Pre-warming vite deps cache (vite optimize --force)...');
181
- try {
182
- (0, node_child_process_1.execFileSync)('npx', ['-y', 'vite', 'optimize', '--force'], {
183
- cwd: targetDir,
184
- stdio: (0, output_1.isJsonMode)() ? ['ignore', 'ignore', 'inherit'] : 'inherit',
185
- timeout: 120_000,
186
- });
187
- }
188
- catch (err) {
189
- (0, logger_1.log)('migrate', `⚠ vite optimize prewarm failed (continuing): ${err instanceof Error ? err.message : String(err)}`);
190
- }
191
- }
192
164
  // 沙箱环境下重启 dev process —— scripts/dev.js 被 fullstack 版本覆盖了,但当前正在
193
165
  // 跑的 node 进程仍按老逻辑工作(只起 vite,没起 nest),整体没切到 fullstack 模式。
194
166
  // 通过 pkill 杀掉 dev.js 主进程,依赖沙箱平台 supervisor 自动重新 exec dev.sh → dev.js,
@@ -70,6 +70,11 @@ exports.MIGRATE_CONFIG = {
70
70
  // ESLint 9 flat config —— vite-react 用 eslint.config.mjs 已删,fullstack 用
71
71
  // eslint.config.js (commonjs) + extends @lark-apaas/fullstack-presets;user 通常不改
72
72
  { type: 'file', from: 'eslint.config.js', to: 'eslint.config.js', overwrite: true },
73
+ // stylelint 配置 —— package.json 加了 `stylelint` script(下面 add-script 那条)
74
+ // + npm run precommit 会调它,缺配置文件时报 `No configuration provided for
75
+ // client/src/index.css`。fullstack template 的 .stylelintrc.js extends
76
+ // @lark-apaas/fullstack-presets 的 stylelintPresetsOfSimple。
77
+ { type: 'file', from: '.stylelintrc.js', to: '.stylelintrc.js', overwrite: true },
73
78
  // client/index.html:HBS 模板(fullstack 用,vite-react 用根 index.html 已删)
74
79
  { type: 'file', from: 'client/index.html', to: 'client/index.html', overwrite: true },
75
80
  // scripts/ 整目录:fullstack 形态启动 / 构建 / lint 所需的 8 个平台脚本(dev.sh /
@@ -274,6 +279,10 @@ exports.MIGRATE_CONFIG = {
274
279
  // (@lark-apaas/fullstack-presets/lib/simple/tsconfig/tsconfig.node.json);
275
280
  // 不装会导致 nest build / type:check:server / vite dev 解析 tsconfig 时 ENOENT
276
281
  '@lark-apaas/fullstack-presets',
282
+ // stylelint —— scripts.stylelint + npm run precommit 会调它,
283
+ // 缺依赖时 CI / 本地 precommit 报 "stylelint: command not found"。
284
+ // 配套的 .stylelintrc.js 由上面 file rule 从 template 搬过来。
285
+ 'stylelint',
277
286
  ],
278
287
  },
279
288
  // ===== 7. 业务代码 codemod:lite → client-toolkit 包名替换 =====
@@ -0,0 +1,67 @@
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.IMPORT_SKIP_FILES = exports.IMPORT_SKIP_DIRS = void 0;
7
+ exports.importSourceSrc = importSourceSrc;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const error_1 = require("../../../utils/error");
11
+ const logger_1 = require("../../../utils/logger");
12
+ /** 导入时跳过的目录名(任意层级命中即整目录跳过)。 */
13
+ exports.IMPORT_SKIP_DIRS = new Set(['node_modules', '.git', 'dist', 'build']);
14
+ /** 导入时跳过的精确文件名。`.env.*`(如 .env.local)另由前缀规则处理。 */
15
+ exports.IMPORT_SKIP_FILES = new Set([
16
+ '.env',
17
+ '.DS_Store',
18
+ '.npmrc',
19
+ 'package.json',
20
+ 'package-lock.json',
21
+ 'yarn.lock',
22
+ 'pnpm-lock.yaml',
23
+ ]);
24
+ function isSkippedFile(name) {
25
+ if (exports.IMPORT_SKIP_FILES.has(name))
26
+ return true;
27
+ // .env.local / .env.production 等一律跳过
28
+ if (name.startsWith('.env.'))
29
+ return true;
30
+ return false;
31
+ }
32
+ /**
33
+ * 把整个 sourcePath 递归拷进 targetDir/src/(叠加,同名覆盖,不删目标已有文件),
34
+ * 按 IMPORT_SKIP_DIRS / IMPORT_SKIP_FILES 过滤。
35
+ * sourcePath 不存在或非目录 → 抛 ARGS_INVALID。
36
+ */
37
+ function importSourceSrc(opts) {
38
+ const sourcePath = node_path_1.default.resolve(opts.sourcePath);
39
+ if (!node_fs_1.default.existsSync(sourcePath) || !node_fs_1.default.statSync(sourcePath).isDirectory()) {
40
+ throw new error_1.AppError('ARGS_INVALID', `--source-path 不存在或不是目录: ${opts.sourcePath}`);
41
+ }
42
+ const destSrc = node_path_1.default.join(opts.targetDir, 'src');
43
+ node_fs_1.default.mkdirSync(destSrc, { recursive: true });
44
+ let count = 0;
45
+ const walk = (relDir) => {
46
+ const absDir = node_path_1.default.join(sourcePath, relDir);
47
+ for (const entry of node_fs_1.default.readdirSync(absDir, { withFileTypes: true })) {
48
+ if (entry.isDirectory()) {
49
+ if (exports.IMPORT_SKIP_DIRS.has(entry.name))
50
+ continue;
51
+ walk(node_path_1.default.join(relDir, entry.name));
52
+ }
53
+ else if (entry.isFile()) {
54
+ if (isSkippedFile(entry.name))
55
+ continue;
56
+ const rel = node_path_1.default.join(relDir, entry.name);
57
+ const dest = node_path_1.default.join(destSrc, rel);
58
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(dest), { recursive: true });
59
+ node_fs_1.default.copyFileSync(node_path_1.default.join(sourcePath, rel), dest);
60
+ count += 1;
61
+ }
62
+ }
63
+ };
64
+ walk('.');
65
+ (0, logger_1.log)('init', `Imported ${String(count)} file(s) from ${sourcePath} → src/`);
66
+ return { importedFileCount: count };
67
+ }
@@ -1,10 +1,17 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.ASYNC_INSTALL_LOG = exports.runAsyncInstallWorker = exports.dispatchAsyncInstall = exports.resolveNpmInstallRegistry = exports.installDependencies = exports.writeSparkMeta = exports.readSparkMeta = exports.TEMPLATE_PACKAGE_BY_STACK = exports.SUPPORTED_STACKS = exports.renderTemplate = void 0;
3
+ exports.ASYNC_INSTALL_LOG = exports.runAsyncInstallWorker = exports.dispatchAsyncInstall = exports.resolveNpmInstallRegistry = exports.installDependencies = exports.writeSparkMeta = exports.readSparkMeta = exports.IMPORT_SKIP_FILES = exports.IMPORT_SKIP_DIRS = exports.importSourceSrc = exports.TEMPLATE_PACKAGE_BY_STACK = exports.STACK_BY_APP_TYPE = exports.APP_TYPES = exports.SUPPORTED_STACKS = exports.resolveStack = exports.renderTemplate = void 0;
4
4
  var template_1 = require("./template");
5
5
  Object.defineProperty(exports, "renderTemplate", { enumerable: true, get: function () { return template_1.renderTemplate; } });
6
+ Object.defineProperty(exports, "resolveStack", { enumerable: true, get: function () { return template_1.resolveStack; } });
6
7
  Object.defineProperty(exports, "SUPPORTED_STACKS", { enumerable: true, get: function () { return template_1.SUPPORTED_STACKS; } });
8
+ Object.defineProperty(exports, "APP_TYPES", { enumerable: true, get: function () { return template_1.APP_TYPES; } });
9
+ Object.defineProperty(exports, "STACK_BY_APP_TYPE", { enumerable: true, get: function () { return template_1.STACK_BY_APP_TYPE; } });
7
10
  Object.defineProperty(exports, "TEMPLATE_PACKAGE_BY_STACK", { enumerable: true, get: function () { return template_1.TEMPLATE_PACKAGE_BY_STACK; } });
11
+ var import_source_1 = require("./import-source");
12
+ Object.defineProperty(exports, "importSourceSrc", { enumerable: true, get: function () { return import_source_1.importSourceSrc; } });
13
+ Object.defineProperty(exports, "IMPORT_SKIP_DIRS", { enumerable: true, get: function () { return import_source_1.IMPORT_SKIP_DIRS; } });
14
+ Object.defineProperty(exports, "IMPORT_SKIP_FILES", { enumerable: true, get: function () { return import_source_1.IMPORT_SKIP_FILES; } });
8
15
  var spark_meta_1 = require("../../../utils/spark-meta");
9
16
  Object.defineProperty(exports, "readSparkMeta", { enumerable: true, get: function () { return spark_meta_1.readSparkMeta; } });
10
17
  Object.defineProperty(exports, "writeSparkMeta", { enumerable: true, get: function () { return spark_meta_1.writeSparkMeta; } });
@@ -3,7 +3,8 @@ 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.SUPPORTED_STACKS = exports.TEMPLATE_PINNED_VERSION_BY_STACK = exports.TEMPLATE_PACKAGE_BY_STACK = void 0;
6
+ exports.APP_TYPES = exports.STACK_BY_APP_TYPE = exports.SUPPORTED_STACKS = exports.TEMPLATE_PINNED_VERSION_BY_STACK = exports.TEMPLATE_PACKAGE_BY_STACK = void 0;
7
+ exports.resolveStack = resolveStack;
7
8
  exports.renderTemplate = renderTemplate;
8
9
  const node_fs_1 = __importDefault(require("node:fs"));
9
10
  const node_path_1 = __importDefault(require("node:path"));
@@ -25,6 +26,51 @@ exports.TEMPLATE_PACKAGE_BY_STACK = {
25
26
  */
26
27
  exports.TEMPLATE_PINNED_VERSION_BY_STACK = {};
27
28
  exports.SUPPORTED_STACKS = Object.keys(exports.TEMPLATE_PACKAGE_BY_STACK);
29
+ /**
30
+ * app_type(业务类型)→ stack 短名映射。仅在调用方未显式传 --template 时用于解析 stack。
31
+ * 与 src/api/app/types.ts 的 AppType 枚举无关,是独立的业务类型概念。
32
+ */
33
+ exports.STACK_BY_APP_TYPE = {
34
+ html: 'html',
35
+ modern_html: 'html',
36
+ jspage: 'vite-react',
37
+ full_stack: 'nestjs-react-fullstack',
38
+ };
39
+ exports.APP_TYPES = Object.keys(exports.STACK_BY_APP_TYPE);
40
+ /**
41
+ * 解析要用的 stack(template-first):
42
+ * 1. template 传了 → 直接用(SUPPORTED_STACKS 校验)
43
+ * 2. 否则 appType 传了 → 按 STACK_BY_APP_TYPE 查表
44
+ * 3. 都没传 → ARGS_INVALID
45
+ * 非法 template / 非法 appType 均抛 ARGS_INVALID。
46
+ */
47
+ function resolveStack(opts) {
48
+ const { template, appType } = opts;
49
+ // 用真值判断(非 undefined/null/空串):空串视为未传,回落到 appType。
50
+ if (template) {
51
+ if (!exports.SUPPORTED_STACKS.includes(template)) {
52
+ throw new error_1.AppError('ARGS_INVALID', `不支持的 template: ${template}`, {
53
+ next_actions: [`可用 stack:${exports.SUPPORTED_STACKS.join(', ')}`],
54
+ });
55
+ }
56
+ return template;
57
+ }
58
+ if (appType) {
59
+ const stack = exports.STACK_BY_APP_TYPE[appType];
60
+ if (!stack) {
61
+ throw new error_1.AppError('ARGS_INVALID', `不支持的 app_type: ${appType}`, {
62
+ next_actions: [`可用 app_type:${exports.APP_TYPES.join(', ')}`],
63
+ });
64
+ }
65
+ return stack;
66
+ }
67
+ throw new error_1.AppError('ARGS_INVALID', '缺少 --template 或 --app-type', {
68
+ next_actions: [
69
+ `可用 stack:${exports.SUPPORTED_STACKS.join(', ')}`,
70
+ `可用 app_type:${exports.APP_TYPES.join(', ')}`,
71
+ ],
72
+ });
73
+ }
28
74
  // template/ 内以下划线开头的占位文件名在渲染时改名。
29
75
  // 原因:
30
76
  // 1. npm pack 强制剥的:.npmrc(防 auth token 泄露)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-apaas/miaoda-cli",
3
- "version": "0.1.21-alpha.0",
3
+ "version": "0.1.21-beta.71cd6c4",
4
4
  "description": "Miaoda 平台命令行工具,面向 Agent 调用",
5
5
  "type": "commonjs",
6
6
  "bin": {