@lark-apaas/miaoda-cli 0.1.4 → 0.1.5-beta.27ede41

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 (52) hide show
  1. package/README.md +6 -5
  2. package/dist/api/deploy/index.js +13 -1
  3. package/dist/api/deploy/modern-types.js +23 -0
  4. package/dist/api/deploy/modern.js +78 -0
  5. package/dist/api/deploy/plugin-instances-types.js +6 -0
  6. package/dist/api/deploy/plugin-instances.js +22 -0
  7. package/dist/api/observability/api.js +10 -0
  8. package/dist/api/observability/index.js +2 -1
  9. package/dist/cli/commands/app/index.js +84 -2
  10. package/dist/cli/commands/deploy/modern.js +50 -0
  11. package/dist/cli/commands/index.js +33 -5
  12. package/dist/cli/commands/observability/index.js +62 -6
  13. package/dist/cli/commands/shared.js +5 -0
  14. package/dist/cli/commands/skills/index.js +63 -0
  15. package/dist/cli/handlers/app/index.js +6 -1
  16. package/dist/cli/handlers/app/init.js +88 -0
  17. package/dist/cli/handlers/deploy/modern.js +33 -0
  18. package/dist/cli/handlers/observability/helpers.js +4 -0
  19. package/dist/cli/handlers/observability/index.js +3 -1
  20. package/dist/cli/handlers/observability/log.js +8 -2
  21. package/dist/cli/handlers/observability/source-stack.js +389 -0
  22. package/dist/cli/handlers/observability/trace.js +7 -1
  23. package/dist/cli/handlers/skills/index.js +7 -0
  24. package/dist/cli/handlers/skills/status.js +31 -0
  25. package/dist/cli/handlers/skills/sync.js +38 -0
  26. package/dist/services/app/init/index.js +12 -0
  27. package/dist/services/app/init/install.js +101 -0
  28. package/dist/services/app/init/template.js +87 -0
  29. package/dist/services/deploy/modern/atoms/build.js +59 -0
  30. package/dist/services/deploy/modern/atoms/context.js +27 -0
  31. package/dist/services/deploy/modern/atoms/index.js +17 -0
  32. package/dist/services/deploy/modern/atoms/local-release.js +27 -0
  33. package/dist/services/deploy/modern/atoms/pre-release.js +13 -0
  34. package/dist/services/deploy/modern/atoms/save-plugin-instances.js +72 -0
  35. package/dist/services/deploy/modern/atoms/upload.js +246 -0
  36. package/dist/services/deploy/modern/check.js +53 -0
  37. package/dist/services/deploy/modern/constants.js +13 -0
  38. package/dist/services/deploy/modern/index.js +16 -0
  39. package/dist/services/deploy/modern/pipelines/index.js +5 -0
  40. package/dist/services/deploy/modern/pipelines/local.js +75 -0
  41. package/dist/services/deploy/modern/protocol.js +122 -0
  42. package/dist/services/deploy/modern/run-types.js +4 -0
  43. package/dist/services/deploy/modern/run.js +13 -0
  44. package/dist/services/deploy/modern/template-key-map.js +22 -0
  45. package/dist/services/skills/index.js +5 -0
  46. package/dist/services/skills/status.js +37 -0
  47. package/dist/utils/coding-steering.js +85 -0
  48. package/dist/utils/git.js +22 -0
  49. package/dist/utils/http.js +21 -11
  50. package/dist/utils/npm-pack.js +55 -0
  51. package/dist/utils/spark-meta.js +42 -0
  52. package/package.json +1 -1
@@ -0,0 +1,38 @@
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.handleSkillsSync = handleSkillsSync;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const coding_steering_1 = require("../../../utils/coding-steering");
9
+ const spark_meta_1 = require("../../../utils/spark-meta");
10
+ const error_1 = require("../../../utils/error");
11
+ const output_1 = require("../../../utils/output");
12
+ /**
13
+ * miaoda skills sync [--dir <path>] [--version <ver>]
14
+ *
15
+ * 从 .spark/meta.json 读 stack,把 coding-steering 包内对应 stack 的 skills + tech.md
16
+ * 同步到 <dir>/.agent/steering/。需要先跑过 `miaoda app init`。
17
+ */
18
+ async function handleSkillsSync(opts) {
19
+ await Promise.resolve();
20
+ const targetDir = node_path_1.default.resolve(opts.dir ?? process.cwd());
21
+ const meta = (0, spark_meta_1.readSparkMeta)(targetDir);
22
+ if (meta.stack === undefined || meta.stack === '') {
23
+ throw new error_1.AppError('SKILLS_META_INCOMPLETE', '.spark/meta.json missing stack — run `miaoda app init` first');
24
+ }
25
+ const result = (0, coding_steering_1.syncCodingSteering)({
26
+ stack: meta.stack,
27
+ targetDir,
28
+ version: opts.version,
29
+ });
30
+ (0, output_1.emit)({
31
+ data: {
32
+ stack: meta.stack,
33
+ version: result.version,
34
+ syncedSkills: result.syncedSkills,
35
+ techSynced: result.techSynced,
36
+ },
37
+ });
38
+ }
@@ -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,87 @@
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 时改这里。archType 由 template 包自报,CLI 不维护。 */
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 / .npmrc 吞掉,
20
+ // 模板里用 _gitignore / _npmrc 提交,渲染时再改回)。
21
+ const RENAME_FILES = {
22
+ _gitignore: '.gitignore',
23
+ '_env.local': '.env.local',
24
+ _npmrc: '.npmrc',
25
+ };
26
+ function renderTemplate(opts) {
27
+ const packageName = exports.TEMPLATE_PACKAGE_BY_STACK[opts.stack];
28
+ if (!packageName) {
29
+ throw new error_1.AppError('ARGS_INVALID', `不支持的 template: ${opts.stack}`, {
30
+ next_actions: [`可用 stack:${exports.SUPPORTED_STACKS.join(', ')}`],
31
+ });
32
+ }
33
+ (0, logger_1.log)('init', `Fetching ${packageName}@${opts.version ?? 'latest'}...`);
34
+ const fetched = (0, npm_pack_1.fetchNpmPackage)({ packageName, version: opts.version });
35
+ try {
36
+ const templateDir = node_path_1.default.join(fetched.extractDir, 'template');
37
+ if (!node_fs_1.default.existsSync(templateDir)) {
38
+ throw new error_1.AppError('TEMPLATE_INVALID', `包 ${packageName}@${fetched.version} 缺少 template/ 目录`);
39
+ }
40
+ const pkgJsonPath = node_path_1.default.join(fetched.extractDir, 'package.json');
41
+ let archType;
42
+ if (node_fs_1.default.existsSync(pkgJsonPath)) {
43
+ const pkgJson = JSON.parse(node_fs_1.default.readFileSync(pkgJsonPath, 'utf-8'));
44
+ archType = pkgJson.miaodaTemplate?.archType;
45
+ }
46
+ // 用显式 null/undefined/'' 判,避免把合法值 0 / false 当成"缺失"误抛
47
+ if (archType === undefined || archType === null || archType === '') {
48
+ throw new error_1.AppError('TEMPLATE_INVALID', `包 ${packageName}@${fetched.version} 缺少 package.json.miaodaTemplate.archType`);
49
+ }
50
+ (0, logger_1.log)('init', `Rendering template to ${opts.targetDir}...`);
51
+ copyDir(templateDir, opts.targetDir);
52
+ for (const [from, to] of Object.entries(RENAME_FILES)) {
53
+ const fromPath = node_path_1.default.join(opts.targetDir, from);
54
+ const toPath = node_path_1.default.join(opts.targetDir, to);
55
+ if (node_fs_1.default.existsSync(fromPath))
56
+ node_fs_1.default.renameSync(fromPath, toPath);
57
+ }
58
+ // 仅替换展示性文件;package.json 写固定 name,保证 md5(package.json) 跨渲染稳定,
59
+ // 可被 install 阶段直接当 MIAODA_DEP_CACHE_DIR 的 cache key。
60
+ for (const rel of ['index.html', 'README.md']) {
61
+ const p = node_path_1.default.join(opts.targetDir, rel);
62
+ if (node_fs_1.default.existsSync(p))
63
+ replaceInFile(p, '{{projectName}}', opts.projectName);
64
+ }
65
+ return { version: fetched.version, packageName, archType };
66
+ }
67
+ finally {
68
+ fetched.cleanup();
69
+ }
70
+ }
71
+ function copyDir(src, dest) {
72
+ node_fs_1.default.mkdirSync(dest, { recursive: true });
73
+ for (const entry of node_fs_1.default.readdirSync(src, { withFileTypes: true })) {
74
+ const srcPath = node_path_1.default.join(src, entry.name);
75
+ const destPath = node_path_1.default.join(dest, entry.name);
76
+ if (entry.isDirectory()) {
77
+ copyDir(srcPath, destPath);
78
+ }
79
+ else {
80
+ node_fs_1.default.copyFileSync(srcPath, destPath);
81
+ }
82
+ }
83
+ }
84
+ function replaceInFile(filePath, search, replace) {
85
+ const content = node_fs_1.default.readFileSync(filePath, 'utf-8');
86
+ node_fs_1.default.writeFileSync(filePath, content.replaceAll(search, replace), 'utf-8');
87
+ }
@@ -0,0 +1,59 @@
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
+ * 给 RESOURCE_CDN_PREFIX 补 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
+ * STATIC_CDN_PREFIX 由 runtime 同域提供(相对路径),不走这里。
17
+ */
18
+ function ensureHttpsScheme(v) {
19
+ if (/^https?:\/\//i.test(v))
20
+ return v;
21
+ if (v.startsWith('/'))
22
+ return v;
23
+ if (!v.includes('.'))
24
+ return v;
25
+ return `https://${v}`;
26
+ }
27
+ /**
28
+ * 跑 `npm run build`,把 preRelease 下发的动态配置注入 env:
29
+ * MIAODA_APP_ID / MIAODA_VERSION / MIAODA_STACK
30
+ * MIAODA_RESOURCE_CDN_PREFIX / MIAODA_STATIC_CDN_PREFIX
31
+ *
32
+ * MIAODA_STATIC_CDN_PREFIX 取自 output_static_paas_storage_credential.downloadURLPrefix,
33
+ * 是 runtime 同域相对路径(例如 /app/<appId>/runtime/api/v1/storage/object/<bucket>/),
34
+ * 不补 https scheme,原样注入。顶层 static_cdn_prefix 不再下发。
35
+ *
36
+ * build 失败抛 AppError(execSync 自身会 throw,捕获后包一层加错误码)。
37
+ */
38
+ function runBuild(opts) {
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);
40
+ const buildEnv = {
41
+ ...process.env,
42
+ MIAODA_APP_ID: opts.appId,
43
+ MIAODA_VERSION: opts.version,
44
+ MIAODA_STACK: opts.templateKey,
45
+ MIAODA_RESOURCE_CDN_PREFIX: ensureHttpsScheme((0, protocol_1.requireDataKey)(opts.data, protocol_1.DataKey.RESOURCE_CDN_PREFIX)),
46
+ MIAODA_STATIC_CDN_PREFIX: staticCred.downloadURLPrefix,
47
+ };
48
+ (0, logger_1.log)('deploy', 'Building...');
49
+ try {
50
+ (0, node_child_process_1.execSync)('npm run build', {
51
+ cwd: opts.projectDir,
52
+ stdio: 'inherit',
53
+ env: buildEnv,
54
+ });
55
+ }
56
+ catch (err) {
57
+ throw new error_1.AppError('DEPLOY_BUILD_FAILED', `npm run build failed: ${err.message}`);
58
+ }
59
+ }
@@ -0,0 +1,27 @@
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 拿 stack,
10
+ * 并把 stack 短名映射成后端 templateKey。appId 由调用方从 env 解析后传入。
11
+ *
12
+ * 任何前置失败统一抛 AppError。返回值是后续 atom 唯一信任的入参源。
13
+ */
14
+ function prepareDeployContext(projectDir, appId) {
15
+ (0, check_1.runDeployChecks)(projectDir);
16
+ const meta = (0, spark_meta_1.readSparkMeta)(projectDir);
17
+ if (meta.stack === undefined || meta.stack === '') {
18
+ throw new error_1.AppError('DEPLOY_META_INCOMPLETE', '.spark/meta.json missing stack — run `miaoda app init` first');
19
+ }
20
+ return {
21
+ projectDir,
22
+ appId,
23
+ stack: meta.stack,
24
+ templateKey: (0, template_key_map_1.resolveTemplateKey)(meta.stack),
25
+ meta,
26
+ };
27
+ }
@@ -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
+ }