@lark-apaas/miaoda-cli 0.1.3-beta.33b7ba5 → 0.1.4-alpha.9153b4c

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 (32) hide show
  1. package/dist/api/deploy/index.js +10 -1
  2. package/dist/api/deploy/modern-types.js +23 -0
  3. package/dist/api/deploy/modern.js +70 -0
  4. package/dist/cli/commands/app/index.js +83 -2
  5. package/dist/cli/commands/deploy/modern.js +47 -0
  6. package/dist/cli/commands/index.js +31 -5
  7. package/dist/cli/handlers/app/index.js +6 -1
  8. package/dist/cli/handlers/app/init.js +86 -0
  9. package/dist/cli/handlers/deploy/modern.js +32 -0
  10. package/dist/services/app/init/index.js +14 -0
  11. package/dist/services/app/init/install.js +101 -0
  12. package/dist/services/app/init/steering.js +74 -0
  13. package/dist/services/app/init/template.js +85 -0
  14. package/dist/services/deploy/modern/atoms/build.js +35 -0
  15. package/dist/services/deploy/modern/atoms/context.js +30 -0
  16. package/dist/services/deploy/modern/atoms/index.js +15 -0
  17. package/dist/services/deploy/modern/atoms/local-release.js +29 -0
  18. package/dist/services/deploy/modern/atoms/pre-release.js +13 -0
  19. package/dist/services/deploy/modern/atoms/upload.js +90 -0
  20. package/dist/services/deploy/modern/check.js +58 -0
  21. package/dist/services/deploy/modern/constants.js +11 -0
  22. package/dist/services/deploy/modern/index.js +16 -0
  23. package/dist/services/deploy/modern/pipelines/index.js +5 -0
  24. package/dist/services/deploy/modern/pipelines/local.js +66 -0
  25. package/dist/services/deploy/modern/protocol.js +40 -0
  26. package/dist/services/deploy/modern/run-types.js +4 -0
  27. package/dist/services/deploy/modern/run.js +13 -0
  28. package/dist/services/deploy/modern/template-key-map.js +29 -0
  29. package/dist/utils/http.js +8 -2
  30. package/dist/utils/npm-pack.js +55 -0
  31. package/dist/utils/spark-meta.js +48 -0
  32. package/package.json +1 -1
@@ -1,11 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.NodeStatus = exports.nodeStatusFromText = exports.nodeStatusText = exports.errorJobSchema = exports.deployGetSchema = exports.deployHistorySchema = exports.queryPipelineInstance = exports.getErrorLog = exports.listPipelineInstances = exports.createRelease = void 0;
3
+ exports.NodeStatus = exports.nodeStatusFromText = exports.nodeStatusText = exports.errorJobSchema = exports.deployGetSchema = exports.deployHistorySchema = exports.LocalReleaseStatus = exports.getModernLastPublishedVersion = exports.getModernReleaseStatus = exports.createModernRelease = exports.updateLocalRelease = exports.createLocalRelease = exports.preRelease = exports.queryPipelineInstance = exports.getErrorLog = exports.listPipelineInstances = exports.createRelease = void 0;
4
4
  var api_1 = require("./api");
5
5
  Object.defineProperty(exports, "createRelease", { enumerable: true, get: function () { return api_1.createRelease; } });
6
6
  Object.defineProperty(exports, "listPipelineInstances", { enumerable: true, get: function () { return api_1.listPipelineInstances; } });
7
7
  Object.defineProperty(exports, "getErrorLog", { enumerable: true, get: function () { return api_1.getErrorLog; } });
8
8
  Object.defineProperty(exports, "queryPipelineInstance", { enumerable: true, get: function () { return api_1.queryPipelineInstance; } });
9
+ var modern_1 = require("./modern");
10
+ Object.defineProperty(exports, "preRelease", { enumerable: true, get: function () { return modern_1.preRelease; } });
11
+ Object.defineProperty(exports, "createLocalRelease", { enumerable: true, get: function () { return modern_1.createLocalRelease; } });
12
+ Object.defineProperty(exports, "updateLocalRelease", { enumerable: true, get: function () { return modern_1.updateLocalRelease; } });
13
+ Object.defineProperty(exports, "createModernRelease", { enumerable: true, get: function () { return modern_1.createRelease; } });
14
+ Object.defineProperty(exports, "getModernReleaseStatus", { enumerable: true, get: function () { return modern_1.getReleaseStatus; } });
15
+ Object.defineProperty(exports, "getModernLastPublishedVersion", { enumerable: true, get: function () { return modern_1.getLastPublishedVersion; } });
16
+ var modern_types_1 = require("./modern-types");
17
+ Object.defineProperty(exports, "LocalReleaseStatus", { enumerable: true, get: function () { return modern_types_1.LocalReleaseStatus; } });
9
18
  var schemas_1 = require("./schemas");
10
19
  Object.defineProperty(exports, "deployHistorySchema", { enumerable: true, get: function () { return schemas_1.deployHistorySchema; } });
11
20
  Object.defineProperty(exports, "deployGetSchema", { enumerable: true, get: function () { return schemas_1.deployGetSchema; } });
@@ -0,0 +1,23 @@
1
+ "use strict";
2
+ // modern deploy 接口的请求 / 响应类型(与服务端发布编排接口对齐)。
3
+ // 第一期落地的链路:本地构建 + 本地部署。
4
+ //
5
+ // 接口分两类:
6
+ // - preRelease:所有"本地构建"链路共用,签发动态配置(含 pre-signed TOS upload URL)
7
+ // - localPublish:本地构建 + 本地部署专用(createReleaseLocalPublish + updateReleaseLocalPublish)
8
+ //
9
+ // 远端部署链路(B/C)所需的 release 接口先预留类型骨架,待 B/C 落地时启用。
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.LocalReleaseStatus = void 0;
12
+ /**
13
+ * localPublish 状态枚举(数值,与服务端 IDL 对齐)。
14
+ * CLI updateLocalRelease 只翻 Finished / Failed 两个终态;其它由后端流转。
15
+ */
16
+ exports.LocalReleaseStatus = {
17
+ Unspecified: 0,
18
+ Publishing: 1,
19
+ Finished: 2,
20
+ Failed: 3,
21
+ Canceled: 4,
22
+ Rollback: 5,
23
+ };
@@ -0,0 +1,70 @@
1
+ "use strict";
2
+ // modern deploy 接口客户端。统一走管理端 innerapi(postInnerApi 信封解析)。
3
+ //
4
+ // 第一期落地(本地构建 + 本地部署):
5
+ // - preRelease:拿 pre_release_id + version + 动态配置 data
6
+ // - createLocalRelease / updateLocalRelease:完整本地部署生命周期
7
+ //
8
+ // 远端部署(本地构建+远端部署、远端构建+远端部署)需要 createRelease + getReleaseStatus,
9
+ // 后续 commit 接入;本文件已预留 endpoint 骨架但暂不导出。
10
+ //
11
+ // 约定:所有接口的 appID 走 URL :appID 路径参数,body 不再冗余携带 appID。
12
+ Object.defineProperty(exports, "__esModule", { value: true });
13
+ exports.preRelease = preRelease;
14
+ exports.createLocalRelease = createLocalRelease;
15
+ exports.updateLocalRelease = updateLocalRelease;
16
+ exports.createRelease = createRelease;
17
+ exports.getReleaseStatus = getReleaseStatus;
18
+ exports.getLastPublishedVersion = getLastPublishedVersion;
19
+ const http_1 = require("../../utils/http");
20
+ const devops_error_1 = require("../../utils/devops-error");
21
+ const DEFAULT_ERR_CODE = 'INTERNAL_DEVOPS_ERROR';
22
+ function envelopeOpts(errPrefix) {
23
+ // modern deploy 接口与 default deploy / app 走同一个 lark.apaas.devops PSM,
24
+ // 业务错误码命名空间共享,直接复用 mapDevopsError 把 BAM 码翻为 AppError。
25
+ return {
26
+ errPrefix,
27
+ defaultErrCode: DEFAULT_ERR_CODE,
28
+ mapErr: devops_error_1.mapDevopsError,
29
+ };
30
+ }
31
+ // ── preRelease ──
32
+ /** POST /v1/devops/app/:appID/pre_release */
33
+ async function preRelease(req) {
34
+ const { appID, ...body } = req;
35
+ const url = `/v1/devops/app/${encodeURIComponent(appID)}/pre_release`;
36
+ return (0, http_1.postInnerApi)(url, body, envelopeOpts('Failed to pre-release'));
37
+ }
38
+ // ── localPublish ──
39
+ /** POST /v1/devops/app/:appID/local_publish/release */
40
+ async function createLocalRelease(req) {
41
+ const { appID, ...body } = req;
42
+ const url = `/v1/devops/app/${encodeURIComponent(appID)}/local_publish/release`;
43
+ return (0, http_1.postInnerApi)(url, body, envelopeOpts('Failed to create local release'));
44
+ }
45
+ /** POST /v1/devops/app/:appID/local_publish/update_release */
46
+ async function updateLocalRelease(req) {
47
+ const { appID, ...body } = req;
48
+ const url = `/v1/devops/app/${encodeURIComponent(appID)}/local_publish/update_release`;
49
+ await (0, http_1.postInnerApi)(url, body, envelopeOpts('Failed to update local release'));
50
+ }
51
+ // ── release(远端部署,B / C 链路;本期不导出,类型骨架已就绪) ──
52
+ /** POST /v1/devops/app/:appID/remote_publish/release */
53
+ async function createRelease(req) {
54
+ const { appID, ...body } = req;
55
+ const url = `/v1/devops/app/${encodeURIComponent(appID)}/remote_publish/release`;
56
+ return (0, http_1.postInnerApi)(url, body, envelopeOpts('Failed to create release'));
57
+ }
58
+ /** POST /v1/devops/app/:appID/release/status */
59
+ async function getReleaseStatus(req) {
60
+ const { appID, ...body } = req;
61
+ const url = `/v1/devops/app/${encodeURIComponent(appID)}/release/status`;
62
+ return (0, http_1.postInnerApi)(url, body, envelopeOpts('Failed to query release status'));
63
+ }
64
+ // ── getLastPublishedVersion(URL path 待文档补充) ──
65
+ /** POST /v1/devops/app/:appID/last_published_version(path 待后端确认) */
66
+ async function getLastPublishedVersion(req) {
67
+ const { appID } = req;
68
+ const url = `/v1/devops/app/${encodeURIComponent(appID)}/last_published_version`;
69
+ return (0, http_1.postInnerApi)(url, {}, envelopeOpts('Failed to get last published version'));
70
+ }
@@ -3,8 +3,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.registerAppCommands = registerAppCommands;
4
4
  const shared_1 = require("../../../cli/commands/shared");
5
5
  const index_1 = require("../../../cli/handlers/app/index");
6
- function registerAppCommands(program) {
7
- const appCmd = program.command('app').description('应用元数据管理:查看 / 修改');
6
+ const error_1 = require("../../../utils/error");
7
+ function registerAppCommands(program, opts = {}) {
8
+ const description = opts.includeInit
9
+ ? '应用元数据管理:查看 / 修改 / 初始化'
10
+ : '应用元数据管理:查看 / 修改';
11
+ const appCmd = program.command('app').description(description);
8
12
  appCmd.action(() => {
9
13
  appCmd.outputHelp();
10
14
  });
@@ -15,6 +19,9 @@ function registerAppCommands(program) {
15
19
  `);
16
20
  registerAppGet(appCmd);
17
21
  registerAppUpdate(appCmd);
22
+ if (opts.includeInit) {
23
+ registerAppInit(appCmd);
24
+ }
18
25
  }
19
26
  function registerAppGet(parent) {
20
27
  const cmd = parent
@@ -60,3 +67,77 @@ JSON 输出
60
67
  });
61
68
  }));
62
69
  }
70
+ function registerAppInit(parent) {
71
+ const cmd = parent
72
+ .command('init')
73
+ .description('初始化应用代码:抓 template 渲染、装 .agent/steering/ skills、写 .spark/meta.json。.spark/meta.json 已存在则直接退出')
74
+ .addOption((0, shared_1.appIdOption)().hideHelp())
75
+ .option('--template <stack>', `技术栈短名(${index_1.SUPPORTED_STACKS.join(' / ')})`)
76
+ .option('--conf <json>', 'init 配置 JSON。支持 {"version": "<template 版本>"} / {"steeringVersion": "<steering 版本>"},均默认 latest')
77
+ .option('--skip-install', '跳过依赖安装', false)
78
+ .addHelpText('after', `
79
+ 幂等
80
+ .spark/meta.json 已存在时直接退出(initialized=false, reason=already_initialized),
81
+ 不再触发 template 渲染、不重新拉 skills、不安装依赖。
82
+ meta.json 在 init 流程最后才写,写了 = 已完整成功一次;
83
+ 上一次失败留下半渲染状态时(package.json 在但 meta.json 没在)允许重跑。
84
+
85
+ 依赖安装
86
+ 默认:npm install --no-audit --no-fund
87
+ --skip-install: 跳过
88
+ MIAODA_DEP_CACHE_DIR 环境变量:
89
+ 若设了且 CWD 含 package.json,先算 md5(package.json),
90
+ 命中 \${MIAODA_DEP_CACHE_DIR}/\${hash}.zip 则 unzip 复用缓存;未命中或解压后 node_modules
91
+ 为空都会 fallback npm install。
92
+ JSON 模式下子进程 stdout 重定向到 stderr,避免污染最终 emit 的 JSON。
93
+
94
+ JSON 输出
95
+ 已初始化:{"data": {"initialized": false, "reason": "already_initialized", "targetDir": "..."}}
96
+ 新初始化:{"data": {"initialized": true, "template": "...", "templateVersion": "...", "steeringVersion": "...",
97
+ "installed": true, "installSource": "cache|npm|skipped", "installHash": "...", ...}}
98
+
99
+ 示例
100
+ $ miaoda app init --template vite-react
101
+ $ miaoda app init --template vite-react --conf '{"version": "0.1.0-alpha.1"}'
102
+ $ miaoda app init --template vite-react --conf '{"steeringVersion": "0.1.0"}'
103
+ $ miaoda app init --template vite-react --skip-install
104
+ $ MIAODA_DEP_CACHE_DIR=/tmp/dep-cache miaoda app init --template vite-react
105
+ `);
106
+ cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
107
+ (0, shared_1.rejectCliOverride)(cmd, 'appId');
108
+ const conf = parseInitConf(rawOpts.conf);
109
+ await (0, index_1.handleAppInit)({
110
+ appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
111
+ template: rawOpts.template,
112
+ conf,
113
+ skipInstall: rawOpts.skipInstall,
114
+ });
115
+ }));
116
+ }
117
+ function parseInitConf(raw) {
118
+ if (raw === undefined)
119
+ return undefined;
120
+ let parsed;
121
+ try {
122
+ parsed = JSON.parse(raw);
123
+ }
124
+ catch (err) {
125
+ const msg = err instanceof Error ? err.message : String(err);
126
+ throw new error_1.AppError('ARGS_INVALID', `--conf 必须是合法 JSON:${msg}`);
127
+ }
128
+ if (!isPlainObject(parsed)) {
129
+ throw new error_1.AppError('ARGS_INVALID', '--conf 必须是 JSON 对象');
130
+ }
131
+ const version = parsed.version;
132
+ if (version !== undefined && typeof version !== 'string') {
133
+ throw new error_1.AppError('ARGS_INVALID', '--conf.version 必须是字符串');
134
+ }
135
+ const steeringVersion = parsed.steeringVersion;
136
+ if (steeringVersion !== undefined && typeof steeringVersion !== 'string') {
137
+ throw new error_1.AppError('ARGS_INVALID', '--conf.steeringVersion 必须是字符串');
138
+ }
139
+ return { version, steeringVersion };
140
+ }
141
+ function isPlainObject(v) {
142
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
143
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.registerDeployCommandsModern = registerDeployCommandsModern;
4
+ const shared_1 = require("../../../cli/commands/shared");
5
+ const modern_1 = require("../../../cli/handlers/deploy/modern");
6
+ /**
7
+ * modern scene (MIAODA_APP_TYPE=7) 专用 deploy 注册。
8
+ * 仅披露顶层 `miaoda deploy`,CLI 表面对齐 openclaw-cli:`--dir / --skip-build`。
9
+ *
10
+ * 与 default 的 registerDeployCommands 完全平行:两套实现 100% 隔离,
11
+ * 不共享分支、不互相依赖。后端调用与编排在 src/services/deploy/modern/。
12
+ */
13
+ function registerDeployCommandsModern(program) {
14
+ const deployCmd = program
15
+ .command('deploy')
16
+ .description('触发 modern 应用发布:本地构建 + 本地部署(preRelease + localPublish)')
17
+ .option('--dir <path>', '项目目录', '.')
18
+ .option('--skip-build', '跳过 build 步骤(已构建好时使用)', false)
19
+ .addHelpText('after', `
20
+ 不要用异步模式或后台模式调用 deploy,否则调用可能提前结束,Agent 会误判发布已完成。
21
+
22
+ 前置要求
23
+ - 项目已 init(.spark/meta.json 含 appId / template)
24
+ - node_modules 已安装
25
+ - package.json 含 build script
26
+ - 沙箱内预装 tosutil(用于把构建产物上传到 TOS)
27
+
28
+ 流程概览(stderr 进度提示)
29
+ 1. prepareContext 2. preRelease 3. build
30
+ 4. createLocalRelease 5. uploadArtifacts
31
+ 6. finalizeLocalRelease(Success|Failed)
32
+
33
+ JSON 输出(stdout)
34
+ {"data": {"appId": "...", "version": <n>, "url": "...", "releaseID": "...", "preReleaseID": "..."}}
35
+
36
+ 示例
37
+ $ miaoda deploy
38
+ $ miaoda deploy --dir ./my-app
39
+ $ miaoda deploy --skip-build
40
+ `);
41
+ deployCmd.action((0, shared_1.withHelp)(deployCmd, async (rawOpts) => {
42
+ await (0, modern_1.handleDeployModern)({
43
+ dir: rawOpts.dir ?? '.',
44
+ skipBuild: rawOpts.skipBuild,
45
+ });
46
+ }));
47
+ }
@@ -1,15 +1,41 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveScene = resolveScene;
3
4
  exports.registerCommands = registerCommands;
4
5
  const index_1 = require("../../cli/commands/file/index");
5
6
  const index_2 = require("../../cli/commands/db/index");
6
7
  const index_3 = require("../../cli/commands/observability/index");
7
8
  const index_4 = require("../../cli/commands/app/index");
8
9
  const index_5 = require("../../cli/commands/deploy/index");
10
+ const modern_1 = require("../../cli/commands/deploy/modern");
11
+ function resolveScene(appType, _archType) {
12
+ if (appType === '7')
13
+ return 'modern';
14
+ return 'default';
15
+ }
16
+ const SCENE_REGISTRARS = {
17
+ default: (p) => {
18
+ (0, index_4.registerAppCommands)(p);
19
+ (0, index_5.registerDeployCommands)(p);
20
+ (0, index_2.registerDbCommands)(p);
21
+ (0, index_1.registerFileCommands)(p);
22
+ (0, index_3.registerObservabilityCommands)(p);
23
+ },
24
+ modern: (p) => {
25
+ (0, index_4.registerAppCommands)(p, { includeInit: true });
26
+ (0, modern_1.registerDeployCommandsModern)(p);
27
+ },
28
+ };
29
+ function readEnv(name) {
30
+ const v = process.env[name]?.trim();
31
+ return v !== undefined && v !== '' ? v : undefined;
32
+ }
9
33
  function registerCommands(program) {
10
- (0, index_1.registerFileCommands)(program);
11
- (0, index_2.registerDbCommands)(program);
12
- (0, index_3.registerObservabilityCommands)(program);
13
- (0, index_4.registerAppCommands)(program);
14
- (0, index_5.registerDeployCommands)(program);
34
+ const appType = readEnv('MIAODA_APP_TYPE');
35
+ const archType = readEnv('MIAODA_APP_ARCH_TYPE');
36
+ const scene = resolveScene(appType, archType);
37
+ SCENE_REGISTRARS[scene](program);
38
+ if (process.env.MIAODA_VERBOSE === '1' || process.argv.includes('--verbose')) {
39
+ process.stderr.write(`[miaoda] scene=${scene} app_type=${appType ?? '-'} arch_type=${archType ?? '-'}\n`);
40
+ }
15
41
  }
@@ -1,7 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.handleAppUpdate = exports.handleAppGet = void 0;
3
+ exports.SUPPORTED_STACKS = exports.handleAppInit = exports.handleAppUpdate = exports.handleAppGet = void 0;
4
4
  var get_1 = require("./get");
5
5
  Object.defineProperty(exports, "handleAppGet", { enumerable: true, get: function () { return get_1.handleAppGet; } });
6
6
  var update_1 = require("./update");
7
7
  Object.defineProperty(exports, "handleAppUpdate", { enumerable: true, get: function () { return update_1.handleAppUpdate; } });
8
+ var init_1 = require("./init");
9
+ Object.defineProperty(exports, "handleAppInit", { enumerable: true, get: function () { return init_1.handleAppInit; } });
10
+ // commands 层渲染 help 时需要这份枚举;从 handler barrel 转发,避免 commands → services 越界
11
+ var index_1 = require("../../../services/app/init/index");
12
+ Object.defineProperty(exports, "SUPPORTED_STACKS", { enumerable: true, get: function () { return index_1.SUPPORTED_STACKS; } });
@@ -0,0 +1,86 @@
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.handleAppInit = handleAppInit;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const index_1 = require("../../../services/app/init/index");
10
+ const error_1 = require("../../../utils/error");
11
+ const output_1 = require("../../../utils/output");
12
+ /** miaoda app init [--template <stack>] [--conf <json>] */
13
+ async function handleAppInit(opts) {
14
+ await Promise.resolve();
15
+ const targetDir = opts.targetDir ?? process.cwd();
16
+ const metaPath = node_path_1.default.join(targetDir, '.spark', 'meta.json');
17
+ // 幂等:.spark/meta.json 是 init 流程最后才写的,存在即代表"已完整成功一次"。
18
+ // 用 package.json 判幂等会让 install 失败、CWD 留下半渲染状态的应用永远 init 不了。
19
+ if (node_fs_1.default.existsSync(metaPath)) {
20
+ if (!(0, output_1.isJsonMode)()) {
21
+ process.stdout.write(`Already initialized: ${metaPath}\n`);
22
+ }
23
+ (0, output_1.emit)({
24
+ data: {
25
+ initialized: false,
26
+ reason: 'already_initialized',
27
+ targetDir,
28
+ },
29
+ });
30
+ return;
31
+ }
32
+ const stack = opts.template;
33
+ if (!stack) {
34
+ throw new error_1.AppError('ARGS_INVALID', '缺少 --template <stack>', {
35
+ next_actions: [`可用 stack:${index_1.SUPPORTED_STACKS.join(', ')}`],
36
+ });
37
+ }
38
+ if (!index_1.SUPPORTED_STACKS.includes(stack)) {
39
+ throw new error_1.AppError('ARGS_INVALID', `不支持的 template: ${stack}`, {
40
+ next_actions: [`可用 stack:${index_1.SUPPORTED_STACKS.join(', ')}`],
41
+ });
42
+ }
43
+ const version = opts.conf?.version;
44
+ const projectName = node_path_1.default.basename(targetDir);
45
+ const tplResult = (0, index_1.renderTemplate)({ stack, version, targetDir, projectName });
46
+ const steeringResult = (0, index_1.syncSteering)({
47
+ stack,
48
+ targetDir,
49
+ version: opts.conf?.steeringVersion,
50
+ });
51
+ const installResult = (0, index_1.installDependencies)({
52
+ targetDir,
53
+ skip: opts.skipInstall,
54
+ quietStdout: (0, output_1.isJsonMode)(),
55
+ });
56
+ (0, index_1.writeSparkMeta)(targetDir, {
57
+ appId: opts.appId,
58
+ template: stack,
59
+ templatePackage: tplResult.packageName,
60
+ templateVersion: tplResult.version,
61
+ steeringVersion: steeringResult.version,
62
+ archType: tplResult.archType,
63
+ installSource: installResult.source,
64
+ installHash: installResult.hash,
65
+ });
66
+ if (!(0, output_1.isJsonMode)()) {
67
+ process.stdout.write(`✓ Initialized ${stack} (template ${tplResult.version}, steering ${steeringResult.version}, install ${installResult.source}) in ${targetDir}\n`);
68
+ }
69
+ (0, output_1.emit)({
70
+ data: {
71
+ initialized: true,
72
+ appId: opts.appId,
73
+ template: stack,
74
+ templatePackage: tplResult.packageName,
75
+ templateVersion: tplResult.version,
76
+ steeringVersion: steeringResult.version,
77
+ archType: tplResult.archType,
78
+ syncedSkills: steeringResult.syncedSkills,
79
+ techSynced: steeringResult.techSynced,
80
+ installed: installResult.installed,
81
+ installSource: installResult.source,
82
+ installHash: installResult.hash,
83
+ cacheZip: installResult.cacheZip,
84
+ },
85
+ });
86
+ }
@@ -0,0 +1,32 @@
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.handleDeployModern = handleDeployModern;
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const output_1 = require("../../../utils/output");
9
+ const index_1 = require("../../../services/deploy/modern/index");
10
+ /**
11
+ * miaoda deploy(modern scene 专用,CLI 表面对齐 openclaw-cli)
12
+ *
13
+ * 进程信息走 stderr(log_id / step),最终结果通过 emit 输出 envelope 到 stdout。
14
+ */
15
+ async function handleDeployModern(opts) {
16
+ const projectDir = node_path_1.default.resolve(opts.dir);
17
+ const result = await (0, index_1.runModernDeploy)({
18
+ projectDir,
19
+ skipBuild: opts.skipBuild ?? false,
20
+ });
21
+ (0, output_1.emit)({
22
+ data: {
23
+ appId: result.appId,
24
+ version: result.version,
25
+ url: result.url,
26
+ releaseID: result.releaseID,
27
+ preReleaseID: result.preReleaseID,
28
+ },
29
+ next_cursor: null,
30
+ has_more: false,
31
+ });
32
+ }
@@ -0,0 +1,14 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.installDependencies = exports.writeSparkMeta = exports.readSparkMeta = exports.syncSteering = 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 steering_1 = require("./steering");
9
+ Object.defineProperty(exports, "syncSteering", { enumerable: true, get: function () { return steering_1.syncSteering; } });
10
+ var spark_meta_1 = require("../../../utils/spark-meta");
11
+ Object.defineProperty(exports, "readSparkMeta", { enumerable: true, get: function () { return spark_meta_1.readSparkMeta; } });
12
+ Object.defineProperty(exports, "writeSparkMeta", { enumerable: true, get: function () { return spark_meta_1.writeSparkMeta; } });
13
+ var install_1 = require("./install");
14
+ 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,74 @@
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.syncSteering = syncSteering;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const npm_pack_1 = require("../../../utils/npm-pack");
10
+ const logger_1 = require("../../../utils/logger");
11
+ const STEERING_PACKAGE = '@lark-apaas/coding-steering';
12
+ function syncSteering(opts) {
13
+ (0, logger_1.log)('init', `Fetching ${STEERING_PACKAGE}@${opts.version ?? 'latest'}...`);
14
+ const fetched = (0, npm_pack_1.fetchNpmPackage)({
15
+ packageName: STEERING_PACKAGE,
16
+ version: opts.version,
17
+ });
18
+ try {
19
+ const steeringRoot = node_path_1.default.join(fetched.extractDir, 'steering');
20
+ const stackDir = node_path_1.default.join(steeringRoot, opts.stack);
21
+ const commonSkillsDir = node_path_1.default.join(steeringRoot, '_common', 'skills');
22
+ const dstRoot = node_path_1.default.join(opts.targetDir, '.agent', 'steering');
23
+ const dstSkillsDir = node_path_1.default.join(dstRoot, 'skills');
24
+ node_fs_1.default.mkdirSync(dstSkillsDir, { recursive: true });
25
+ let techSynced = false;
26
+ const techSrc = node_path_1.default.join(stackDir, 'tech.md');
27
+ if (node_fs_1.default.existsSync(techSrc)) {
28
+ node_fs_1.default.copyFileSync(techSrc, node_path_1.default.join(dstRoot, 'tech.md'));
29
+ techSynced = true;
30
+ }
31
+ const synced = [];
32
+ // 先拷 _common/skills/,再拷 <stack>/skills/。stack 同名覆盖 common(设计 §8.4)
33
+ if (node_fs_1.default.existsSync(commonSkillsDir)) {
34
+ for (const name of node_fs_1.default.readdirSync(commonSkillsDir)) {
35
+ const src = node_path_1.default.join(commonSkillsDir, name);
36
+ if (!node_fs_1.default.statSync(src).isDirectory())
37
+ continue;
38
+ copyDir(src, node_path_1.default.join(dstSkillsDir, name));
39
+ synced.push(`_common/skills/${name}`);
40
+ }
41
+ }
42
+ const stackSkillsDir = node_path_1.default.join(stackDir, 'skills');
43
+ if (node_fs_1.default.existsSync(stackSkillsDir)) {
44
+ for (const name of node_fs_1.default.readdirSync(stackSkillsDir)) {
45
+ const src = node_path_1.default.join(stackSkillsDir, name);
46
+ if (!node_fs_1.default.statSync(src).isDirectory())
47
+ continue;
48
+ const dst = node_path_1.default.join(dstSkillsDir, name);
49
+ if (node_fs_1.default.existsSync(dst))
50
+ node_fs_1.default.rmSync(dst, { recursive: true, force: true });
51
+ copyDir(src, dst);
52
+ synced.push(`${opts.stack}/skills/${name}`);
53
+ }
54
+ }
55
+ (0, logger_1.log)('init', `Steering synced: ${String(synced.length)} skill(s), tech.md ${techSynced ? 'yes' : 'no'}`);
56
+ return { version: fetched.version, syncedSkills: synced, techSynced };
57
+ }
58
+ finally {
59
+ fetched.cleanup();
60
+ }
61
+ }
62
+ function copyDir(src, dest) {
63
+ node_fs_1.default.mkdirSync(dest, { recursive: true });
64
+ for (const entry of node_fs_1.default.readdirSync(src, { withFileTypes: true })) {
65
+ const srcPath = node_path_1.default.join(src, entry.name);
66
+ const destPath = node_path_1.default.join(dest, entry.name);
67
+ if (entry.isDirectory()) {
68
+ copyDir(srcPath, destPath);
69
+ }
70
+ else {
71
+ node_fs_1.default.copyFileSync(srcPath, destPath);
72
+ }
73
+ }
74
+ }