@lark-apaas/miaoda-cli 0.1.3 → 0.1.4-alpha.8b6506a

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.
Files changed (109) hide show
  1. package/dist/api/app/api.js +3 -3
  2. package/dist/api/app/schemas.js +43 -43
  3. package/dist/api/db/api.js +398 -55
  4. package/dist/api/db/client.js +155 -28
  5. package/dist/api/db/index.js +12 -1
  6. package/dist/api/db/parsers.js +20 -20
  7. package/dist/api/db/sql-keywords.js +87 -87
  8. package/dist/api/deploy/api.js +5 -5
  9. package/dist/api/deploy/index.js +12 -1
  10. package/dist/api/deploy/modern-types.js +23 -0
  11. package/dist/api/deploy/modern.js +70 -0
  12. package/dist/api/deploy/plugin-instances-types.js +6 -0
  13. package/dist/api/deploy/plugin-instances.js +30 -0
  14. package/dist/api/deploy/schemas.js +32 -32
  15. package/dist/api/file/api.js +89 -87
  16. package/dist/api/file/client.js +62 -22
  17. package/dist/api/file/detect.js +3 -3
  18. package/dist/api/file/index.js +2 -1
  19. package/dist/api/file/parsers.js +18 -7
  20. package/dist/api/observability/api.js +6 -6
  21. package/dist/api/observability/schemas.js +14 -14
  22. package/dist/api/plugin/api.js +31 -31
  23. package/dist/cli/commands/app/index.js +94 -13
  24. package/dist/cli/commands/db/index.js +602 -54
  25. package/dist/cli/commands/deploy/index.js +28 -28
  26. package/dist/cli/commands/deploy/modern.js +48 -0
  27. package/dist/cli/commands/file/index.js +85 -58
  28. package/dist/cli/commands/index.js +33 -5
  29. package/dist/cli/commands/observability/index.js +69 -69
  30. package/dist/cli/commands/plugin/index.js +27 -27
  31. package/dist/cli/commands/shared.js +10 -10
  32. package/dist/cli/commands/skills/index.js +63 -0
  33. package/dist/cli/handlers/app/index.js +6 -1
  34. package/dist/cli/handlers/app/init.js +84 -0
  35. package/dist/cli/handlers/app/update.js +2 -2
  36. package/dist/cli/handlers/db/_destructive.js +67 -0
  37. package/dist/cli/handlers/db/_env.js +26 -0
  38. package/dist/cli/handlers/db/_operator.js +35 -0
  39. package/dist/cli/handlers/db/audit.js +383 -0
  40. package/dist/cli/handlers/db/changelog.js +160 -0
  41. package/dist/cli/handlers/db/data.js +32 -31
  42. package/dist/cli/handlers/db/index.js +17 -1
  43. package/dist/cli/handlers/db/migration.js +234 -0
  44. package/dist/cli/handlers/db/quota.js +68 -0
  45. package/dist/cli/handlers/db/recovery.js +413 -0
  46. package/dist/cli/handlers/db/schema.js +33 -33
  47. package/dist/cli/handlers/db/sql.js +69 -69
  48. package/dist/cli/handlers/deploy/deploy.js +4 -4
  49. package/dist/cli/handlers/deploy/error-log.js +1 -1
  50. package/dist/cli/handlers/deploy/get.js +3 -3
  51. package/dist/cli/handlers/deploy/modern.js +32 -0
  52. package/dist/cli/handlers/deploy/polling.js +11 -11
  53. package/dist/cli/handlers/file/cp.js +30 -30
  54. package/dist/cli/handlers/file/index.js +3 -1
  55. package/dist/cli/handlers/file/ls.js +5 -5
  56. package/dist/cli/handlers/file/quota.js +66 -0
  57. package/dist/cli/handlers/file/rm.js +32 -30
  58. package/dist/cli/handlers/file/sign.js +3 -3
  59. package/dist/cli/handlers/file/stat.js +10 -9
  60. package/dist/cli/handlers/observability/analytics.js +47 -47
  61. package/dist/cli/handlers/observability/helpers.js +2 -2
  62. package/dist/cli/handlers/observability/log.js +9 -9
  63. package/dist/cli/handlers/observability/metric.js +26 -26
  64. package/dist/cli/handlers/observability/trace.js +5 -5
  65. package/dist/cli/handlers/plugin/plugin-local.js +53 -53
  66. package/dist/cli/handlers/plugin/plugin.js +15 -15
  67. package/dist/cli/handlers/skills/index.js +7 -0
  68. package/dist/cli/handlers/skills/status.js +31 -0
  69. package/dist/cli/handlers/skills/sync.js +38 -0
  70. package/dist/cli/help.js +16 -16
  71. package/dist/main.js +12 -12
  72. package/dist/services/app/init/index.js +12 -0
  73. package/dist/services/app/init/install.js +101 -0
  74. package/dist/services/app/init/template.js +85 -0
  75. package/dist/services/deploy/modern/atoms/build.js +54 -0
  76. package/dist/services/deploy/modern/atoms/context.js +30 -0
  77. package/dist/services/deploy/modern/atoms/index.js +17 -0
  78. package/dist/services/deploy/modern/atoms/local-release.js +27 -0
  79. package/dist/services/deploy/modern/atoms/pre-release.js +13 -0
  80. package/dist/services/deploy/modern/atoms/save-plugin-instances.js +72 -0
  81. package/dist/services/deploy/modern/atoms/upload.js +205 -0
  82. package/dist/services/deploy/modern/check.js +58 -0
  83. package/dist/services/deploy/modern/constants.js +13 -0
  84. package/dist/services/deploy/modern/index.js +16 -0
  85. package/dist/services/deploy/modern/pipelines/index.js +5 -0
  86. package/dist/services/deploy/modern/pipelines/local.js +75 -0
  87. package/dist/services/deploy/modern/protocol.js +43 -0
  88. package/dist/services/deploy/modern/run-types.js +4 -0
  89. package/dist/services/deploy/modern/run.js +13 -0
  90. package/dist/services/deploy/modern/template-key-map.js +29 -0
  91. package/dist/services/skills/index.js +5 -0
  92. package/dist/services/skills/status.js +37 -0
  93. package/dist/utils/args.js +1 -1
  94. package/dist/utils/coding-steering.js +84 -0
  95. package/dist/utils/colors.js +2 -2
  96. package/dist/utils/config.js +2 -2
  97. package/dist/utils/devops-error.js +9 -9
  98. package/dist/utils/error.js +2 -2
  99. package/dist/utils/git.js +4 -4
  100. package/dist/utils/http.js +28 -26
  101. package/dist/utils/index.js +3 -1
  102. package/dist/utils/npm-pack.js +55 -0
  103. package/dist/utils/output.js +67 -45
  104. package/dist/utils/poll.js +35 -0
  105. package/dist/utils/render.js +27 -27
  106. package/dist/utils/spark-meta.js +42 -0
  107. package/dist/utils/spinner.js +46 -0
  108. package/dist/utils/time.js +47 -42
  109. package/package.json +1 -1
package/dist/cli/help.js CHANGED
@@ -35,7 +35,7 @@ class MiaodaHelp extends commander_1.Help {
35
35
  const usage = cmd.usage();
36
36
  if (usage) {
37
37
  // 去掉末尾的 [flags] / [options],对齐 spec 的 "name <args>" 形态
38
- const argsOnly = usage.replace(/\s*\[(?:flags|options)\]\s*$/i, "").trim();
38
+ const argsOnly = usage.replace(/\s*\[(?:flags|options)\]\s*$/i, '').trim();
39
39
  return argsOnly ? `${cmd.name()} ${argsOnly}` : cmd.name();
40
40
  }
41
41
  return super.subcommandTerm(cmd);
@@ -51,7 +51,7 @@ class MiaodaHelp extends commander_1.Help {
51
51
  if (summary)
52
52
  return summary;
53
53
  const desc = super.subcommandDescription(cmd);
54
- const idx = desc.indexOf("\n");
54
+ const idx = desc.indexOf('\n');
55
55
  return idx === -1 ? desc : desc.slice(0, idx);
56
56
  }
57
57
  /**
@@ -81,7 +81,7 @@ class MiaodaHelp extends commander_1.Help {
81
81
  visibleGlobalOptions(cmd) {
82
82
  const opts = super.visibleGlobalOptions(cmd);
83
83
  if (cmd.parent) {
84
- return opts.filter((o) => o.long !== "--version" && o.short !== "-v");
84
+ return opts.filter((o) => o.long !== '--version' && o.short !== '-v');
85
85
  }
86
86
  return opts;
87
87
  }
@@ -90,7 +90,7 @@ class MiaodaHelp extends commander_1.Help {
90
90
  * spec 不展示这一条,过滤掉。
91
91
  */
92
92
  visibleCommands(cmd) {
93
- return super.visibleCommands(cmd).filter((c) => c.name() !== "help");
93
+ return super.visibleCommands(cmd).filter((c) => c.name() !== 'help');
94
94
  }
95
95
  formatHelp(cmd, helper) {
96
96
  const isRoot = cmd.parent == null;
@@ -98,28 +98,28 @@ class MiaodaHelp extends commander_1.Help {
98
98
  const helpWidth = helper.helpWidth ?? 80;
99
99
  const formatItem = (term, description) => {
100
100
  if (description) {
101
- const padding = " ".repeat(Math.max(termWidth - term.length, 0) + 2);
101
+ const padding = ' '.repeat(Math.max(termWidth - term.length, 0) + 2);
102
102
  return `${term}${padding}${description}`;
103
103
  }
104
104
  return term;
105
105
  };
106
- const formatList = (lines) => lines.map((l) => " " + l).join("\n");
106
+ const formatList = (lines) => lines.map((l) => ' ' + l).join('\n');
107
107
  void helpWidth; // 保留以备后续按宽度自动 wrap,当前直接透传 description
108
108
  const out = [];
109
109
  // 1. 描述
110
110
  const desc = helper.commandDescription(cmd);
111
111
  if (desc) {
112
- out.push(desc, "");
112
+ out.push(desc, '');
113
113
  }
114
114
  // 2. Usage:独立 heading + 缩进
115
- out.push("Usage:", ` ${helper.commandUsage(cmd)}`, "");
115
+ out.push('Usage:', ` ${helper.commandUsage(cmd)}`, '');
116
116
  // 3. Commands(仅父级命令组有,spec 要求 Commands 在 Flags 前)
117
117
  // spec 不展示 Arguments 段,参数说明放在 description 文本里
118
118
  const subs = helper
119
119
  .visibleCommands(cmd)
120
120
  .map((c) => formatItem(helper.subcommandTerm(c), helper.subcommandDescription(c)));
121
121
  if (subs.length) {
122
- out.push("Commands:", formatList(subs), "");
122
+ out.push('Commands:', formatList(subs), '');
123
123
  }
124
124
  // 5. Flags(叶子命令专属 options)
125
125
  // - Root / 父命令组:local options 都视作"会被子命令继承的 globals",渲染到 Global Flags 段
@@ -132,7 +132,7 @@ class MiaodaHelp extends commander_1.Help {
132
132
  .filter((o) => !isHelpOption(o))
133
133
  .map((o) => formatItem(helper.optionTerm(o), helper.optionDescription(o)));
134
134
  if (opts.length) {
135
- out.push("Flags:", formatList(opts), "");
135
+ out.push('Flags:', formatList(opts), '');
136
136
  }
137
137
  }
138
138
  // 6. Global Flags
@@ -162,22 +162,22 @@ class MiaodaHelp extends commander_1.Help {
162
162
  }
163
163
  if (globals.length) {
164
164
  const lines = globals.map((o) => formatItem(helper.optionTerm(o), helper.optionDescription(o)));
165
- out.push("Global Flags:", formatList(lines), "");
165
+ out.push('Global Flags:', formatList(lines), '');
166
166
  }
167
167
  // 7. 父命令底部追加 "Use <cmd> <subcommand> --help" 提示,对齐 spec
168
168
  if (subs.length > 0) {
169
- const path = cmd.name() === "miaoda" ? "miaoda" : commandPath(cmd);
170
- out.push(`Use "${path} <command> --help" for more information about a command.`, "");
169
+ const path = cmd.name() === 'miaoda' ? 'miaoda' : commandPath(cmd);
170
+ out.push(`Use "${path} <command> --help" for more information about a command.`, '');
171
171
  }
172
172
  // 保留末尾换行:commander 用 join('\n') 拼 addHelpText('after') 段,
173
173
  // 这里多留一个 \n,让 Notes / Examples 段与上面段落之间空一行。
174
- return out.join("\n").replace(/\n+$/, "\n");
174
+ return out.join('\n').replace(/\n+$/, '\n');
175
175
  }
176
176
  }
177
177
  exports.MiaodaHelp = MiaodaHelp;
178
178
  /** 判断是否为 -h / --help 选项。 */
179
179
  function isHelpOption(o) {
180
- return o.long === "--help" || o.short === "-h";
180
+ return o.long === '--help' || o.short === '-h';
181
181
  }
182
182
  /** 拼接命令完整路径,例如 db schema -> "miaoda db schema"。 */
183
183
  function commandPath(cmd) {
@@ -187,5 +187,5 @@ function commandPath(cmd) {
187
187
  names.unshift(cur.name());
188
188
  cur = cur.parent;
189
189
  }
190
- return names.join(" ");
190
+ return names.join(' ');
191
191
  }
package/dist/main.js CHANGED
@@ -20,18 +20,18 @@ commander_1.Command.prototype.createHelp = function () {
20
20
  const program = new commander_1.Command();
21
21
  const { version } = package_json_1.default;
22
22
  program
23
- .name("miaoda")
24
- .description("妙搭平台 CLI,提供数据服务、文件存储等命令行操作。")
25
- .usage("<command> [flags]")
26
- .version(version, "-v, --version", "显示版本号")
27
- .option("--json [fields]", "JSON 输出,可选字段级选择")
28
- .addOption(new commander_1.Option("--output <format>", "输出格式(pretty | json,大小写不敏感)")
29
- .choices(["pretty", "json"])
30
- .argParser((0, shared_1.caseInsensitiveChoice)(["pretty", "json"]))
31
- .default("pretty"))
32
- .option("--verbose", "debug 日志到 stderr")
33
- .helpOption("-h, --help", "显示帮助信息")
34
- .hook("preAction", (_thisCmd, actionCmd) => {
23
+ .name('miaoda')
24
+ .description('妙搭平台 CLI,提供数据服务、文件存储等命令行操作。')
25
+ .usage('<command> [flags]')
26
+ .version(version, '-v, --version', '显示版本号')
27
+ .option('--json [fields]', 'JSON 输出,可选字段级选择')
28
+ .addOption(new commander_1.Option('--output <format>', '输出格式(pretty | json,大小写不敏感)')
29
+ .choices(['pretty', 'json'])
30
+ .argParser((0, shared_1.caseInsensitiveChoice)(['pretty', 'json']))
31
+ .default('pretty'))
32
+ .option('--verbose', 'debug 日志到 stderr')
33
+ .helpOption('-h, --help', '显示帮助信息')
34
+ .hook('preAction', (_thisCmd, actionCmd) => {
35
35
  (0, log_id_1.generateLogId)();
36
36
  const opts = actionCmd.optsWithGlobals();
37
37
  (0, config_1.initConfigFromOpts)(opts);
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.installDependencies = exports.writeSparkMeta = exports.readSparkMeta = exports.TEMPLATE_PACKAGE_BY_STACK = exports.SUPPORTED_STACKS = exports.renderTemplate = void 0;
4
+ var template_1 = require("./template");
5
+ Object.defineProperty(exports, "renderTemplate", { enumerable: true, get: function () { return template_1.renderTemplate; } });
6
+ Object.defineProperty(exports, "SUPPORTED_STACKS", { enumerable: true, get: function () { return template_1.SUPPORTED_STACKS; } });
7
+ Object.defineProperty(exports, "TEMPLATE_PACKAGE_BY_STACK", { enumerable: true, get: function () { return template_1.TEMPLATE_PACKAGE_BY_STACK; } });
8
+ var spark_meta_1 = require("../../../utils/spark-meta");
9
+ Object.defineProperty(exports, "readSparkMeta", { enumerable: true, get: function () { return spark_meta_1.readSparkMeta; } });
10
+ Object.defineProperty(exports, "writeSparkMeta", { enumerable: true, get: function () { return spark_meta_1.writeSparkMeta; } });
11
+ var install_1 = require("./install");
12
+ Object.defineProperty(exports, "installDependencies", { enumerable: true, get: function () { return install_1.installDependencies; } });
@@ -0,0 +1,101 @@
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.installDependencies = installDependencies;
7
+ const node_child_process_1 = require("node:child_process");
8
+ const node_crypto_1 = __importDefault(require("node:crypto"));
9
+ const node_fs_1 = __importDefault(require("node:fs"));
10
+ const node_path_1 = __importDefault(require("node:path"));
11
+ const error_1 = require("../../../utils/error");
12
+ const logger_1 = require("../../../utils/logger");
13
+ const DEP_CACHE_ENV = 'MIAODA_DEP_CACHE_DIR';
14
+ /**
15
+ * 依赖安装。
16
+ *
17
+ * 优先级:
18
+ * 1. skip=true → 直接返回 source=skipped
19
+ * 2. MIAODA_DEP_CACHE_DIR 设了 + 目标目录有 package.json:
20
+ * - 算 md5(package.json)(与 CI 生产端 dep-cache pipeline 一致)
21
+ * - 若 ${cache}/${hash}.zip 存在 → unzip 到目标目录
22
+ * - 校验 node_modules/ 非空:通过则 source=cache,否则 fallback npm install
23
+ * - zip 不存在 → fallback npm install,但 hash 仍记录
24
+ * 3. `npm install --no-audit --no-fund`
25
+ *
26
+ * 不做 cache 回写(read-only)。
27
+ */
28
+ function installDependencies(opts) {
29
+ if (opts.skip) {
30
+ (0, logger_1.log)('init', 'Skipping dependency install (--skip-install)');
31
+ return { installed: false, source: 'skipped' };
32
+ }
33
+ const childStdio = stdioFor(opts.quietStdout);
34
+ const cacheDir = process.env[DEP_CACHE_ENV];
35
+ const pkgJsonPath = node_path_1.default.join(opts.targetDir, 'package.json');
36
+ const hasPkg = node_fs_1.default.existsSync(pkgJsonPath);
37
+ if (cacheDir && hasPkg) {
38
+ const hash = md5File(pkgJsonPath);
39
+ const cacheZip = node_path_1.default.join(cacheDir, `${hash}.zip`);
40
+ if (node_fs_1.default.existsSync(cacheZip)) {
41
+ (0, logger_1.log)('init', `Cache hit: ${cacheZip}`);
42
+ extractZip(cacheZip, opts.targetDir, childStdio);
43
+ if (nodeModulesUsable(opts.targetDir)) {
44
+ return { installed: true, source: 'cache', hash, cacheZip };
45
+ }
46
+ (0, logger_1.log)('init', 'Cache zip extracted but node_modules/ missing or empty; falling back to npm install');
47
+ runNpmInstall(opts.targetDir, childStdio);
48
+ return { installed: true, source: 'npm', hash };
49
+ }
50
+ (0, logger_1.log)('init', `Cache miss for ${hash}.zip, falling back to npm install`);
51
+ runNpmInstall(opts.targetDir, childStdio);
52
+ return { installed: true, source: 'npm', hash };
53
+ }
54
+ if (cacheDir && !hasPkg) {
55
+ (0, logger_1.log)('init', `${DEP_CACHE_ENV} set but no package.json in target; running npm install`);
56
+ }
57
+ runNpmInstall(opts.targetDir, childStdio);
58
+ return { installed: true, source: 'npm' };
59
+ }
60
+ function nodeModulesUsable(targetDir) {
61
+ const nm = node_path_1.default.join(targetDir, 'node_modules');
62
+ if (!node_fs_1.default.existsSync(nm))
63
+ return false;
64
+ try {
65
+ return node_fs_1.default.readdirSync(nm).length > 0;
66
+ }
67
+ catch {
68
+ return false;
69
+ }
70
+ }
71
+ function stdioFor(quietStdout) {
72
+ // quietStdout=true:子进程 stdout 重定向到 fd 2(父进程 stderr),
73
+ // 保留 stderr 直出 → 终端仍能看到 npm install 进度,但父进程 stdout 干净,
74
+ // 后续 emit(JSON) 不会被污染。
75
+ return quietStdout ? ['ignore', 2, 'inherit'] : ['ignore', 'inherit', 'inherit'];
76
+ }
77
+ function md5File(filePath) {
78
+ const buf = node_fs_1.default.readFileSync(filePath);
79
+ return node_crypto_1.default.createHash('md5').update(buf).digest('hex');
80
+ }
81
+ function extractZip(zipPath, targetDir, stdio) {
82
+ try {
83
+ (0, node_child_process_1.execFileSync)('unzip', ['-q', '-o', zipPath, '-d', targetDir], { stdio });
84
+ }
85
+ catch (err) {
86
+ const msg = err instanceof Error ? err.message : String(err);
87
+ throw new error_1.AppError('DEP_CACHE_EXTRACT_FAILED', `解压依赖缓存失败 (${zipPath}): ${msg}`, {
88
+ next_actions: [`确认 ${zipPath} 是合法 zip`, `或 unset ${DEP_CACHE_ENV} 走 npm install`],
89
+ });
90
+ }
91
+ }
92
+ function runNpmInstall(targetDir, stdio) {
93
+ (0, logger_1.log)('init', `npm install in ${targetDir}...`);
94
+ try {
95
+ (0, node_child_process_1.execFileSync)('npm', ['install', '--no-audit', '--no-fund'], { cwd: targetDir, stdio });
96
+ }
97
+ catch (err) {
98
+ const msg = err instanceof Error ? err.message : String(err);
99
+ throw new error_1.AppError('NPM_INSTALL_FAILED', `npm install 失败: ${msg}`);
100
+ }
101
+ }
@@ -0,0 +1,85 @@
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.SUPPORTED_STACKS = exports.TEMPLATE_PACKAGE_BY_STACK = void 0;
7
+ exports.renderTemplate = renderTemplate;
8
+ const node_fs_1 = __importDefault(require("node:fs"));
9
+ const node_path_1 = __importDefault(require("node:path"));
10
+ const npm_pack_1 = require("../../../utils/npm-pack");
11
+ const error_1 = require("../../../utils/error");
12
+ const logger_1 = require("../../../utils/logger");
13
+ /** 短名 → 包名映射。新增 stack 时改这里。 */
14
+ exports.TEMPLATE_PACKAGE_BY_STACK = {
15
+ 'vite-react': '@lark-apaas/coding-template-vite-react',
16
+ html: '@lark-apaas/coding-template-html',
17
+ };
18
+ exports.SUPPORTED_STACKS = Object.keys(exports.TEMPLATE_PACKAGE_BY_STACK);
19
+ // template/ 内以下划线开头的占位文件名在渲染时改名(npm publish 会把 .gitignore 吞掉,
20
+ // 模板里用 _gitignore 提交,渲染时再改回)。
21
+ const RENAME_FILES = {
22
+ _gitignore: '.gitignore',
23
+ '_env.local': '.env.local',
24
+ };
25
+ function renderTemplate(opts) {
26
+ const packageName = exports.TEMPLATE_PACKAGE_BY_STACK[opts.stack];
27
+ if (!packageName) {
28
+ throw new error_1.AppError('ARGS_INVALID', `不支持的 template: ${opts.stack}`, {
29
+ next_actions: [`可用 stack:${exports.SUPPORTED_STACKS.join(', ')}`],
30
+ });
31
+ }
32
+ (0, logger_1.log)('init', `Fetching ${packageName}@${opts.version ?? 'latest'}...`);
33
+ const fetched = (0, npm_pack_1.fetchNpmPackage)({ packageName, version: opts.version });
34
+ try {
35
+ const templateDir = node_path_1.default.join(fetched.extractDir, 'template');
36
+ if (!node_fs_1.default.existsSync(templateDir)) {
37
+ throw new error_1.AppError('TEMPLATE_INVALID', `包 ${packageName}@${fetched.version} 缺少 template/ 目录`);
38
+ }
39
+ const pkgJsonPath = node_path_1.default.join(fetched.extractDir, 'package.json');
40
+ let archType = '';
41
+ if (node_fs_1.default.existsSync(pkgJsonPath)) {
42
+ const pkgJson = JSON.parse(node_fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
43
+ archType = pkgJson.miaodaTemplate?.archType ?? '';
44
+ }
45
+ if (!archType) {
46
+ throw new error_1.AppError('TEMPLATE_INVALID', `包 ${packageName}@${fetched.version} 缺少 package.json.miaodaTemplate.archType`);
47
+ }
48
+ (0, logger_1.log)('init', `Rendering template to ${opts.targetDir}...`);
49
+ copyDir(templateDir, opts.targetDir);
50
+ for (const [from, to] of Object.entries(RENAME_FILES)) {
51
+ const fromPath = node_path_1.default.join(opts.targetDir, from);
52
+ const toPath = node_path_1.default.join(opts.targetDir, to);
53
+ if (node_fs_1.default.existsSync(fromPath))
54
+ node_fs_1.default.renameSync(fromPath, toPath);
55
+ }
56
+ // 仅替换展示性文件;package.json 写固定 name,保证 md5(package.json) 跨渲染稳定,
57
+ // 可被 install 阶段直接当 MIAODA_DEP_CACHE_DIR 的 cache key。
58
+ for (const rel of ['index.html', 'README.md']) {
59
+ const p = node_path_1.default.join(opts.targetDir, rel);
60
+ if (node_fs_1.default.existsSync(p))
61
+ replaceInFile(p, '{{projectName}}', opts.projectName);
62
+ }
63
+ return { version: fetched.version, packageName, archType };
64
+ }
65
+ finally {
66
+ fetched.cleanup();
67
+ }
68
+ }
69
+ function copyDir(src, dest) {
70
+ node_fs_1.default.mkdirSync(dest, { recursive: true });
71
+ for (const entry of node_fs_1.default.readdirSync(src, { withFileTypes: true })) {
72
+ const srcPath = node_path_1.default.join(src, entry.name);
73
+ const destPath = node_path_1.default.join(dest, entry.name);
74
+ if (entry.isDirectory()) {
75
+ copyDir(srcPath, destPath);
76
+ }
77
+ else {
78
+ node_fs_1.default.copyFileSync(srcPath, destPath);
79
+ }
80
+ }
81
+ }
82
+ function replaceInFile(filePath, search, replace) {
83
+ const content = node_fs_1.default.readFileSync(filePath, 'utf-8');
84
+ node_fs_1.default.writeFileSync(filePath, content.replaceAll(search, replace), 'utf-8');
85
+ }
@@ -0,0 +1,54 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runBuild = runBuild;
4
+ const node_child_process_1 = require("node:child_process");
5
+ const error_1 = require("../../../../utils/error");
6
+ const logger_1 = require("../../../../utils/logger");
7
+ const protocol_1 = require("../protocol");
8
+ /**
9
+ * 给 CDN 前缀补 https:// 协议头:
10
+ * - 已带 http:// 或 https:// → 原样返回
11
+ * - 以 / 开头(绝对路径)→ 原样返回
12
+ * - 不含 dot(看起来不像域名,比如纯路径 app_xxx/2)→ 原样返回
13
+ * - 其它(典型:lf-xxx.bytetos.com/...)→ 前缀 https://
14
+ *
15
+ * template 的 build script 直接拿来当 vite base / asset URL 用,必须是合法 URL。
16
+ */
17
+ function ensureHttpsScheme(v) {
18
+ if (/^https?:\/\//i.test(v))
19
+ return v;
20
+ if (v.startsWith('/'))
21
+ return v;
22
+ if (!v.includes('.'))
23
+ return v;
24
+ return `https://${v}`;
25
+ }
26
+ /**
27
+ * 跑 `npm run build`,把 preRelease 下发的动态配置注入 env:
28
+ * MIAODA_APP_ID / MIAODA_VERSION / MIAODA_STACK
29
+ * MIAODA_RESOURCE_CDN_PREFIX / MIAODA_STATIC_CDN_PREFIX
30
+ *
31
+ * CDN 前缀缺 scheme 时由 CLI 自动补 https://,template 不用关心。
32
+ * build 失败抛 AppError(execSync 自身会 throw,捕获后包一层加错误码)。
33
+ */
34
+ function runBuild(opts) {
35
+ const buildEnv = {
36
+ ...process.env,
37
+ MIAODA_APP_ID: opts.appId,
38
+ MIAODA_VERSION: opts.version,
39
+ MIAODA_STACK: opts.templateKey,
40
+ MIAODA_RESOURCE_CDN_PREFIX: ensureHttpsScheme((0, protocol_1.requireDataKey)(opts.data, protocol_1.DataKey.RESOURCE_CDN_PREFIX)),
41
+ MIAODA_STATIC_CDN_PREFIX: ensureHttpsScheme((0, protocol_1.requireDataKey)(opts.data, protocol_1.DataKey.STATIC_CDN_PREFIX)),
42
+ };
43
+ (0, logger_1.log)('deploy', 'Building...');
44
+ try {
45
+ (0, node_child_process_1.execSync)('npm run build', {
46
+ cwd: opts.projectDir,
47
+ stdio: 'inherit',
48
+ env: buildEnv,
49
+ });
50
+ }
51
+ catch (err) {
52
+ throw new error_1.AppError('DEPLOY_BUILD_FAILED', `npm run build failed: ${err.message}`);
53
+ }
54
+ }
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.prepareDeployContext = prepareDeployContext;
4
+ const error_1 = require("../../../../utils/error");
5
+ const spark_meta_1 = require("../../../../utils/spark-meta");
6
+ const check_1 = require("../check");
7
+ const template_key_map_1 = require("../template-key-map");
8
+ /**
9
+ * 准备 deploy 上下文:跑前置检查、读 .spark/meta.json、抽出 appId / stack,
10
+ * 并把 stack 短名映射成后端 templateKey。
11
+ *
12
+ * 任何前置失败统一抛 AppError。返回值是后续 atom 唯一信任的入参源。
13
+ */
14
+ function prepareDeployContext(projectDir) {
15
+ (0, check_1.runDeployChecks)(projectDir);
16
+ const meta = (0, spark_meta_1.readSparkMeta)(projectDir);
17
+ if (meta.appId === undefined || meta.appId === '') {
18
+ throw new error_1.AppError('DEPLOY_META_INCOMPLETE', '.spark/meta.json missing appId — run `miaoda app init` first');
19
+ }
20
+ if (meta.stack === undefined || meta.stack === '') {
21
+ throw new error_1.AppError('DEPLOY_META_INCOMPLETE', '.spark/meta.json missing stack — run `miaoda app init` first');
22
+ }
23
+ return {
24
+ projectDir,
25
+ appId: meta.appId,
26
+ stack: meta.stack,
27
+ templateKey: (0, template_key_map_1.resolveTemplateKey)(meta.stack),
28
+ meta,
29
+ };
30
+ }
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.savePluginInstances = exports.LocalReleaseStatus = exports.finalizeLocalRelease = exports.createLocalRelease = exports.uploadArtifacts = exports.runBuild = exports.preRelease = exports.prepareDeployContext = void 0;
4
+ var context_1 = require("./context");
5
+ Object.defineProperty(exports, "prepareDeployContext", { enumerable: true, get: function () { return context_1.prepareDeployContext; } });
6
+ var pre_release_1 = require("./pre-release");
7
+ Object.defineProperty(exports, "preRelease", { enumerable: true, get: function () { return pre_release_1.preRelease; } });
8
+ var build_1 = require("./build");
9
+ Object.defineProperty(exports, "runBuild", { enumerable: true, get: function () { return build_1.runBuild; } });
10
+ var upload_1 = require("./upload");
11
+ Object.defineProperty(exports, "uploadArtifacts", { enumerable: true, get: function () { return upload_1.uploadArtifacts; } });
12
+ var local_release_1 = require("./local-release");
13
+ Object.defineProperty(exports, "createLocalRelease", { enumerable: true, get: function () { return local_release_1.createLocalRelease; } });
14
+ Object.defineProperty(exports, "finalizeLocalRelease", { enumerable: true, get: function () { return local_release_1.finalizeLocalRelease; } });
15
+ Object.defineProperty(exports, "LocalReleaseStatus", { enumerable: true, get: function () { return local_release_1.LocalReleaseStatus; } });
16
+ var save_plugin_instances_1 = require("./save-plugin-instances");
17
+ Object.defineProperty(exports, "savePluginInstances", { enumerable: true, get: function () { return save_plugin_instances_1.savePluginInstances; } });
@@ -0,0 +1,27 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.LocalReleaseStatus = void 0;
4
+ exports.createLocalRelease = createLocalRelease;
5
+ exports.finalizeLocalRelease = finalizeLocalRelease;
6
+ const index_1 = require("../../../../api/deploy/index");
7
+ Object.defineProperty(exports, "LocalReleaseStatus", { enumerable: true, get: function () { return index_1.LocalReleaseStatus; } });
8
+ const logger_1 = require("../../../../utils/logger");
9
+ /**
10
+ * 创建本地发布单(加锁;不挂 pipeline)。
11
+ * 返回的 releaseId 必须由 finalizeLocalRelease 翻为终态,否则发布单一直挂着。
12
+ */
13
+ async function createLocalRelease(appId, version) {
14
+ return (0, index_1.createLocalRelease)({ appID: appId, version });
15
+ }
16
+ /**
17
+ * 把本地发布单翻为终态。Finished / Failed 由 pipeline 视上下游结果决定。
18
+ * 失败不抛(避免覆盖上游真错误),但记一行 stderr 让排查可定位。
19
+ */
20
+ async function finalizeLocalRelease(appId, releaseId, status) {
21
+ try {
22
+ await (0, index_1.updateLocalRelease)({ appID: appId, releaseID: releaseId, status });
23
+ }
24
+ catch (err) {
25
+ (0, logger_1.log)('deploy', `finalize release(${releaseId}, status=${String(status)}) failed: ${err.message}`);
26
+ }
27
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.preRelease = preRelease;
4
+ const index_1 = require("../../../../api/deploy/index");
5
+ /**
6
+ * 调用 preRelease,拿到 pre_release_id、version 和动态配置 data。
7
+ *
8
+ * 失败由 api 层抛 HttpError / AppError 透传上去;atom 本身不再做语义包装,
9
+ * 让 pipeline 层决定怎么向 handler 报告。
10
+ */
11
+ async function preRelease(appId, templateKey) {
12
+ return (0, index_1.preRelease)({ appID: appId, templateKey });
13
+ }
@@ -0,0 +1,72 @@
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.savePluginInstances = savePluginInstances;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const index_1 = require("../../../../api/deploy/index");
10
+ const logger_1 = require("../../../../utils/logger");
11
+ const constants_1 = require("../constants");
12
+ function isValidCapability(o) {
13
+ if (typeof o !== 'object' || o === null)
14
+ return false;
15
+ const id = o.id;
16
+ return typeof id === 'string' && id.length > 0;
17
+ }
18
+ /**
19
+ * 扫描 dist/output_capabilities/*.json,过滤出合法 capability,批量上报到 studio_server。
20
+ *
21
+ * 合法判定:能 JSON.parse + 顶层 id 是非空字符串。其它字段后端校验。
22
+ * 目录不存在 / 没有合法 capability → 直接返回 0,pipeline 当作 no-op。
23
+ * batchSave 调用失败 → 透传抛 AppError,pipeline 兜底 finalizeLocalRelease(Failed)。
24
+ */
25
+ async function savePluginInstances(opts) {
26
+ const dir = node_path_1.default.join(opts.projectDir, constants_1.DIST_DIR, constants_1.OUTPUT_CAPABILITIES_DIR);
27
+ if (!node_fs_1.default.existsSync(dir)) {
28
+ (0, logger_1.debug)(`save-plugin-instances: ${dir} not present, skipping`);
29
+ return { saved: 0, skipped: 0 };
30
+ }
31
+ const files = node_fs_1.default.readdirSync(dir).filter((f) => f.endsWith('.json'));
32
+ const instances = [];
33
+ let skipped = 0;
34
+ for (const f of files) {
35
+ const full = node_path_1.default.join(dir, f);
36
+ let raw;
37
+ try {
38
+ raw = node_fs_1.default.readFileSync(full, 'utf-8');
39
+ }
40
+ catch (err) {
41
+ (0, logger_1.debug)(`save-plugin-instances: read ${f} failed: ${err.message}`);
42
+ skipped++;
43
+ continue;
44
+ }
45
+ let parsed;
46
+ try {
47
+ parsed = JSON.parse(raw);
48
+ }
49
+ catch {
50
+ (0, logger_1.debug)(`save-plugin-instances: ${f} is not valid JSON, skipping`);
51
+ skipped++;
52
+ continue;
53
+ }
54
+ if (!isValidCapability(parsed)) {
55
+ (0, logger_1.debug)(`save-plugin-instances: ${f} missing 'id' string, skipping`);
56
+ skipped++;
57
+ continue;
58
+ }
59
+ instances.push({ id: parsed.id, content: JSON.stringify(parsed) });
60
+ }
61
+ if (instances.length === 0) {
62
+ (0, logger_1.log)('deploy', `No capability to register (${String(files.length)} files scanned, all skipped)`);
63
+ return { saved: 0, skipped };
64
+ }
65
+ (0, logger_1.log)('deploy', `Registering ${String(instances.length)} capability instance(s)...`);
66
+ await (0, index_1.batchSavePluginInstances)({
67
+ appID: opts.appId,
68
+ appVersion: opts.version,
69
+ pluginInstances: instances,
70
+ });
71
+ return { saved: instances.length, skipped };
72
+ }