@lark-apaas/miaoda-cli 0.1.17 → 0.1.18-alpha.5fd4656

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 (30) hide show
  1. package/dist/cli/commands/app/index.js +16 -0
  2. package/dist/cli/commands/index.js +10 -0
  3. package/dist/cli/handlers/app/init.js +45 -12
  4. package/dist/cli/handlers/app/migrate.js +5 -1
  5. package/dist/cli/handlers/app/sync.js +5 -0
  6. package/dist/config/sync-configs/design-html.js +25 -0
  7. package/dist/config/sync-configs/index.js +2 -0
  8. package/dist/services/app/init/async-install.js +116 -0
  9. package/dist/services/app/init/index.js +6 -1
  10. package/dist/services/app/init/template.js +1 -0
  11. package/dist/services/deploy/modern/atoms/design-build.js +20 -0
  12. package/dist/services/deploy/modern/atoms/design-upload.js +73 -0
  13. package/dist/services/deploy/modern/atoms/index.js +5 -1
  14. package/dist/services/deploy/modern/atoms/tosutil.js +234 -0
  15. package/dist/services/deploy/modern/atoms/upload.js +4 -127
  16. package/dist/services/deploy/modern/pipelines/design-local.js +47 -0
  17. package/dist/services/deploy/modern/pipelines/index.js +3 -1
  18. package/dist/services/deploy/modern/protocol.js +7 -0
  19. package/dist/services/deploy/modern/run.js +10 -4
  20. package/dist/services/deploy/modern/template-key-map.js +4 -0
  21. package/dist/utils/logs-dir.js +19 -0
  22. package/package.json +1 -1
  23. package/upgrade/templates/design-html/templates/scripts/build.sh +70 -0
  24. package/upgrade/templates/design-stack/templates/.githooks/pre-commit +1 -0
  25. package/upgrade/templates/design-stack/templates/scripts/dev-local.js +69 -10
  26. package/upgrade/templates/design-stack/templates/scripts/hooks/run-precommit.js +36 -0
  27. package/upgrade/templates/nestjs-react-fullstack/templates/.githooks/pre-commit +1 -0
  28. package/upgrade/templates/nestjs-react-fullstack/templates/.spark_project +2 -2
  29. package/upgrade/templates/nestjs-react-fullstack/templates/scripts/dev-local.js +71 -13
  30. package/upgrade/templates/nestjs-react-fullstack/templates/scripts/hooks/run-precommit.js +36 -0
@@ -166,6 +166,7 @@ function registerAppInit(parent) {
166
166
  .option('--template <stack>', `技术栈短名(${index_1.SUPPORTED_STACKS.join(' / ')})`)
167
167
  .option('--conf <json>', 'init 配置 JSON。支持 {"version": "<template 版本>"},默认 latest')
168
168
  .option('--skip-install', '跳过依赖安装', false)
169
+ .option('--async-install', '派发后台进程装依赖并立即返回(与 --skip-install 互斥)', false)
169
170
  .addOption((0, shared_1.appIdOption)())
170
171
  .addHelpText('after', `
171
172
  幂等
@@ -194,17 +195,31 @@ function registerAppInit(parent) {
194
195
  为空都会 fallback npm install。
195
196
  JSON 模式下子进程 stdout 重定向到 stderr,避免污染最终 emit 的 JSON。
196
197
 
198
+ 异步安装(--async-install)
199
+ 跳过同步 npm install,派发一个 detached 后台进程装依赖后立即返回。
200
+ .spark/meta.json 在返回前写出(= 脚手架就绪),install 真实结果由 event marker 表达。
201
+ 仅沙箱(SANDBOX_ID 非空)写 marker(presence 定成败,content 给排障):
202
+ 成功 → /tmp/event/WORKSPACE_READY
203
+ 失败 → /tmp/event/WORKSPACE_FAILED
204
+ 安装日志:/tmp/async_install_dep.std.log
205
+ 失败恢复:靠后续 'miaoda app sync'(dev.sh 入口 / 沙箱 pod 启动会跑)兜底,init 不重试。
206
+ 与 --skip-install 互斥。
207
+
197
208
  JSON 输出
198
209
  已初始化:{"data": {"initialized": false, "reason": "already_initialized", "targetDir": "..."}}
199
210
  新初始化:{"data": {"initialized": true, "template": "...", "templateVersion": "...", "steeringVersion": "...",
200
211
  "appId": "...", "platformStackFound": true, "platformSyncedFiles": [...],
201
212
  "installed": true, "installSource": "cache|npm|skipped", "installHash": "...", ...}}
213
+ async 模式:{"data": {"initialized": true, "asyncInstall": true, "installed": false,
214
+ "installSource": "async", "installPid": 123, "installLogPath": "...",
215
+ "eventReadyPath": "...", "eventFailedPath": "..."}}
202
216
 
203
217
  示例
204
218
  $ miaoda app init --template vite-react
205
219
  $ miaoda app init --template nestjs-react-fullstack --app-id app_xxx
206
220
  $ miaoda app init --template vite-react --conf '{"version": "0.1.0"}'
207
221
  $ miaoda app init --template vite-react --skip-install
222
+ $ miaoda app init --template vite-react --async-install
208
223
  $ MIAODA_DEP_CACHE_DIR=/tmp/dep-cache miaoda app init --template vite-react
209
224
  `);
210
225
  cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
@@ -214,6 +229,7 @@ JSON 输出
214
229
  conf,
215
230
  skipInstall: rawOpts.skipInstall,
216
231
  appId: rawOpts.appId,
232
+ asyncInstall: rawOpts.asyncInstall,
217
233
  });
218
234
  }));
219
235
  }
@@ -14,6 +14,7 @@ const index_6 = require("../../cli/commands/skills/index");
14
14
  // 3 = AppType_APPLICATION(全栈应用,当前仅 nestjs-react-fullstack 一个 stack)
15
15
  // 4 = AppType_DESIGN(design-stack,SSR 渲染、无业务逻辑、无数据库)
16
16
  // 7 = miaoda-cli 自定义 modern 占位(后端枚举无 7,沙箱目前不传)
17
+ // 8 = AppType_DESIGN_HTML(design-html,buildless 静态 HTML,命令集同 modern)
17
18
  // 其它(0/1/2/5/6 / 未设)→ default(命令全开,本地 dev / CI / 兼容回退)
18
19
  // stack 维度(nestjs-react-fullstack / vite-react / ...)是正交的,
19
20
  // skills sync 内部按 .spark/meta.json.stack 拉对应 coding-steering/steering/<stack>/ 子目录。
@@ -24,6 +25,8 @@ function resolveScene(appType, _archType) {
24
25
  return 'application';
25
26
  if (appType === '4')
26
27
  return 'design';
28
+ if (appType === '8')
29
+ return 'design-html';
27
30
  return 'default';
28
31
  }
29
32
  const SCENE_REGISTRARS = {
@@ -62,6 +65,13 @@ const SCENE_REGISTRARS = {
62
65
  (0, index_3.registerObservabilityCommands)(p);
63
66
  (0, index_6.registerSkillsCommands)(p);
64
67
  },
68
+ // design-html scene(AppType_DESIGN_HTML=8):buildless 静态 HTML,
69
+ // 命令集与 modern 一致(app init/sync + modern 拆分版 deploy + skills),不挂 db/file/observability。
70
+ 'design-html': (p) => {
71
+ (0, index_4.registerAppCommands)(p, { includeInit: true });
72
+ (0, modern_1.registerDeployCommandsModern)(p);
73
+ (0, index_6.registerSkillsCommands)(p);
74
+ },
65
75
  };
66
76
  function readEnv(name) {
67
77
  const v = process.env[name]?.trim();
@@ -10,6 +10,7 @@ const index_1 = require("../../../services/app/init/index");
10
10
  const coding_steering_1 = require("../../../utils/coding-steering");
11
11
  const logger_1 = require("../../../utils/logger");
12
12
  const githooks_1 = require("../../../utils/githooks");
13
+ const logs_dir_1 = require("../../../utils/logs-dir");
13
14
  const error_1 = require("../../../utils/error");
14
15
  const output_1 = require("../../../utils/output");
15
16
  /**
@@ -60,9 +61,15 @@ async function handleAppInit(opts) {
60
61
  next_actions: [`可用 stack:${index_1.SUPPORTED_STACKS.join(', ')}`],
61
62
  });
62
63
  }
64
+ if (opts.asyncInstall && opts.skipInstall) {
65
+ throw new error_1.AppError('ARGS_INVALID', '--async-install 与 --skip-install 互斥');
66
+ }
63
67
  const version = opts.conf?.version;
64
68
  const projectName = node_path_1.default.basename(targetDir);
65
69
  const tplResult = (0, index_1.renderTemplate)({ stack, version, targetDir, projectName });
70
+ // 先建 logs/,防止 user 跑 dev 之前 AI/工具 redirect 到 logs/*.log 因为父目录不存在直接挂
71
+ // (dev.js / dev-local.js 自己也建 logs/,但只在它启动后;启动前的 shell redirect 会先于此)
72
+ (0, logs_dir_1.ensureLogsDir)(targetDir);
66
73
  // skills 同步软失败:拉不到 coding-steering 包不该阻断 writeSparkMeta /
67
74
  // activateGitHooks(之前会让 .spark/meta.json 没写,下次 init 半渲染状态又得重跑全套)。
68
75
  // 按运行环境(SANDBOX_ID)分流 outputLayout,不绑 stack:
@@ -86,13 +93,17 @@ async function handleAppInit(opts) {
86
93
  (0, logger_1.log)('init', `⚠ skills sync failed (continuing init): ${steeringError}`);
87
94
  steeringResult = { version: 'unknown', syncedSkills: [], techSynced: false };
88
95
  }
89
- // 装模板钉死的依赖。不带 <pkg>@latest 位置参数,因此 npm 不会改写 package.json/lockfile
96
+ // 依赖安装:async 模式留到 meta 落盘后派发后台进程(不等装完),同步模式当场装。
97
+ // 同步:装模板钉死的依赖。不带 <pkg>@latest 位置参数,因此 npm 不会改写 package.json/lockfile
90
98
  // —— "升管控包到 latest" 是 sync 的职责,在 dev.sh 入口跑 `miaoda app sync` 时触发。
91
- const installResult = (0, index_1.installDependencies)({
92
- targetDir,
93
- skip: opts.skipInstall,
94
- quietStdout: (0, output_1.isJsonMode)(),
95
- });
99
+ let installResult;
100
+ if (!opts.asyncInstall) {
101
+ installResult = (0, index_1.installDependencies)({
102
+ targetDir,
103
+ skip: opts.skipInstall,
104
+ quietStdout: (0, output_1.isJsonMode)(),
105
+ });
106
+ }
96
107
  // template 自带 .githooks/pre-commit;如果用户项目已 git init,顺便设上 core.hooksPath。
97
108
  const hookActivation = (0, githooks_1.activateGitHooks)(targetDir);
98
109
  (0, index_1.writeSparkMeta)(targetDir, {
@@ -101,8 +112,18 @@ async function handleAppInit(opts) {
101
112
  archType: tplResult.archType,
102
113
  app_id: opts.appId,
103
114
  });
115
+ // async 模式:meta 已落盘(= 脚手架就绪),再派发后台安装并立即返回(不等装完)。
116
+ let asyncDispatch;
117
+ if (opts.asyncInstall) {
118
+ asyncDispatch = (0, index_1.dispatchAsyncInstall)({ targetDir });
119
+ }
104
120
  if (!(0, output_1.isJsonMode)()) {
105
- process.stdout.write(`✓ Initialized ${stack} (template ${tplResult.version}, steering ${steeringResult.version}, install ${installResult.source}) in ${targetDir}\n`);
121
+ if (opts.asyncInstall && asyncDispatch) {
122
+ process.stdout.write(`✓ Initialized ${stack} (template ${tplResult.version}, steering ${steeringResult.version}, install dispatched in background pid ${String(asyncDispatch.pid)}) in ${targetDir}\n`);
123
+ }
124
+ else if (installResult) {
125
+ process.stdout.write(`✓ Initialized ${stack} (template ${tplResult.version}, steering ${steeringResult.version}, install ${installResult.source}) in ${targetDir}\n`);
126
+ }
106
127
  }
107
128
  (0, output_1.emit)({
108
129
  data: {
@@ -117,11 +138,23 @@ async function handleAppInit(opts) {
117
138
  techSynced: steeringResult.techSynced,
118
139
  steeringError,
119
140
  gitHooks: hookActivation.action,
120
- installed: installResult.installed,
121
- installSource: installResult.source,
122
- installHash: installResult.hash,
123
- cacheZip: installResult.cacheZip,
124
- installError: installResult.error,
141
+ ...(opts.asyncInstall && asyncDispatch
142
+ ? {
143
+ asyncInstall: true,
144
+ installed: false,
145
+ installSource: 'async',
146
+ installPid: asyncDispatch.pid,
147
+ installLogPath: asyncDispatch.logPath,
148
+ eventReadyPath: asyncDispatch.eventReadyPath,
149
+ eventFailedPath: asyncDispatch.eventFailedPath,
150
+ }
151
+ : {
152
+ installed: installResult?.installed,
153
+ installSource: installResult?.source,
154
+ installHash: installResult?.hash,
155
+ cacheZip: installResult?.cacheZip,
156
+ installError: installResult?.error,
157
+ }),
125
158
  },
126
159
  });
127
160
  }
@@ -139,9 +139,13 @@ async function handleAppMigrate(opts) {
139
139
  // 通过 pkill 杀掉 dev.js 主进程,依赖沙箱平台 supervisor 自动重新 exec dev.sh → dev.js,
140
140
  // 新进程用新 deps + 新 scripts,正常进入 fullstack 模式。
141
141
  // - 只在 SANDBOX_ID 非空时做(本地环境用户进程混杂,主动 pkill 风险大)
142
+ // - install 挂了不做 —— 新依赖不全, 杀旧 dev 后新 dev 起不来反而更糟,
143
+ // 留着旧进程让用户先处理 installError
142
144
  // - 软失败:pkill 无匹配进程退出 1,catch 吞掉
143
145
  let devRestarted = false;
144
- if (process.env.SANDBOX_ID !== undefined && process.env.SANDBOX_ID !== '') {
146
+ if (installError === undefined &&
147
+ process.env.SANDBOX_ID !== undefined &&
148
+ process.env.SANDBOX_ID !== '') {
145
149
  (0, logger_1.log)('migrate', '沙箱环境,重启 dev process(平台 supervisor 会自动拉起)...');
146
150
  try {
147
151
  (0, node_child_process_1.execFileSync)('pkill', ['-f', 'node.*scripts/dev\\.js'], { stdio: 'ignore' });
@@ -16,6 +16,7 @@ const sync_configs_1 = require("../../../config/sync-configs");
16
16
  const sync_rule_1 = require("../../../utils/sync-rule");
17
17
  const platform_sync_1 = require("../../../utils/platform-sync");
18
18
  const githooks_1 = require("../../../utils/githooks");
19
+ const logs_dir_1 = require("../../../utils/logs-dir");
19
20
  const install_1 = require("../../../services/app/init/install");
20
21
  const spark_meta_1 = require("../../../utils/spark-meta");
21
22
  const error_1 = require("../../../utils/error");
@@ -81,6 +82,10 @@ async function handleAppSync(opts) {
81
82
  }
82
83
  // 2. activate git hooks(template 自带 .githooks/pre-commit,core.hooksPath 一次性设置)
83
84
  const hookActivation = (0, githooks_1.activateGitHooks)(targetDir);
85
+ // logs/ 兜底:dev.js / dev-local.js 启动后才 mkdir,启动前任何想 redirect 到 logs/*.log 的
86
+ // shell 命令都会因父目录不存在直接 ENOENT 挂掉。sync 也建一次,长期使用的 user app 老仓库
87
+ // 第一次升级也能拿到这个目录。
88
+ (0, logs_dir_1.ensureLogsDir)(targetDir);
84
89
  // 3. snapshot 当前 user app 装的管控包版本,供 install 后 diff 用
85
90
  const beforeSpecs = snapshotManagedDepSpecs(targetDir);
86
91
  // 4. npm install —— 跟 init 一样软失败,install 挂了不该阻断 emit / sync 总结输出。
@@ -0,0 +1,25 @@
1
+ "use strict";
2
+ /**
3
+ * design-html 的 sync 规则。
4
+ *
5
+ * design-html 是 buildless 静态 HTML stack(design-html scene,MIAODA_APP_TYPE=8),
6
+ * 无依赖、无 nest 后端、无数据库、无 dev 链路,只有一个打包脚本 scripts/build.sh
7
+ * (源码即产物:rsync 到 dist/output + 生成 routes.json)。
8
+ *
9
+ * 所以 sync 只做一件事:把 cli 自带的 scripts/build.sh 派生覆盖到 user app,
10
+ * 让 build 脚本可独立于模板版本更新。不同步 .githooks、不加 dev:local、
11
+ * 不动 .gitignore(卫生项直接在 coding 仓模板 _gitignore 里维护)。
12
+ */
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.SYNC_CONFIG = void 0;
15
+ exports.SYNC_CONFIG = {
16
+ sync: [
17
+ {
18
+ type: 'directory',
19
+ from: 'scripts',
20
+ to: 'scripts',
21
+ overwrite: true,
22
+ },
23
+ ],
24
+ };
25
+ exports.default = exports.SYNC_CONFIG;
@@ -23,6 +23,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
23
23
  const node_path_1 = __importDefault(require("node:path"));
24
24
  const nestjs_react_fullstack_1 = __importDefault(require("./nestjs-react-fullstack"));
25
25
  const design_stack_1 = __importDefault(require("./design-stack"));
26
+ const design_html_1 = __importDefault(require("./design-html"));
26
27
  /**
27
28
  * 已纳入新 sync 机制的 stack 注册表。未在表里的 stack(如老 vite-react / html)走旧的
28
29
  * `upgrade/templates/<stack>/{files,patches}` 机制,由 sync handler 兜底。
@@ -30,6 +31,7 @@ const design_stack_1 = __importDefault(require("./design-stack"));
30
31
  const STACK_REGISTRY = {
31
32
  'nestjs-react-fullstack': nestjs_react_fullstack_1.default,
32
33
  'design-stack': design_stack_1.default,
34
+ 'design-html': design_html_1.default,
33
35
  };
34
36
  /** 返回该 stack 的 SyncConfig;表里没有时返回 null。 */
35
37
  function getSyncConfig(stack) {
@@ -0,0 +1,116 @@
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.ASYNC_INSTALL_LOG = void 0;
7
+ exports.isSandboxEnv = isSandboxEnv;
8
+ exports.runAsyncInstallWorker = runAsyncInstallWorker;
9
+ exports.dispatchAsyncInstall = dispatchAsyncInstall;
10
+ const node_child_process_1 = require("node:child_process");
11
+ const node_fs_1 = __importDefault(require("node:fs"));
12
+ const node_path_1 = __importDefault(require("node:path"));
13
+ const install_1 = require("./install");
14
+ /** event marker 目录,写死;写前 mkdir -p。沙箱里 code server 轮询此目录。 */
15
+ const EVENT_DIR = '/tmp/event';
16
+ /** 安装成功 marker 名 */
17
+ const MARKER_READY = 'WORKSPACE_READY';
18
+ /** 安装失败 marker 名 */
19
+ const MARKER_FAILED = 'WORKSPACE_FAILED';
20
+ /** 后台 worker stdout/stderr 落盘路径 */
21
+ exports.ASYNC_INSTALL_LOG = '/tmp/async_install_dep.std.log';
22
+ /** 沙箱判定:SANDBOX_ID 非空。与 init handler 的 outputLayout 分流口径一致。 */
23
+ function isSandboxEnv() {
24
+ return process.env.SANDBOX_ID !== undefined && process.env.SANDBOX_ID !== '';
25
+ }
26
+ function nowIso() {
27
+ return new Date().toISOString();
28
+ }
29
+ function clearStaleMarkers(eventDir) {
30
+ for (const name of [MARKER_READY, MARKER_FAILED]) {
31
+ const p = node_path_1.default.join(eventDir, name);
32
+ if (node_fs_1.default.existsSync(p))
33
+ node_fs_1.default.rmSync(p, { force: true });
34
+ }
35
+ }
36
+ function writeMarker(eventDir, name, content) {
37
+ node_fs_1.default.mkdirSync(eventDir, { recursive: true });
38
+ node_fs_1.default.writeFileSync(node_path_1.default.join(eventDir, name), JSON.stringify(content) + '\n', 'utf-8');
39
+ }
40
+ /**
41
+ * 后台 install worker 体:跑 installDependencies,按结果写 marker(仅沙箱)。
42
+ * 由 dispatchAsyncInstall 通过 `node -e` 在 detached 子进程里调用(见下)。
43
+ * presence 定成败:成功写 WORKSPACE_READY,失败写 WORKSPACE_FAILED;content 给排障。
44
+ */
45
+ function runAsyncInstallWorker(opts) {
46
+ const eventDir = opts.eventDir ?? EVENT_DIR;
47
+ const sandbox = isSandboxEnv();
48
+ if (sandbox)
49
+ clearStaleMarkers(eventDir);
50
+ const start = Date.now();
51
+ let result;
52
+ try {
53
+ result = (0, install_1.installDependencies)({ targetDir: opts.targetDir, quietStdout: false });
54
+ }
55
+ catch (err) {
56
+ const msg = err instanceof Error ? err.message : String(err);
57
+ if (sandbox) {
58
+ writeMarker(eventDir, MARKER_FAILED, {
59
+ ts: nowIso(),
60
+ error: msg,
61
+ logPath: exports.ASYNC_INSTALL_LOG,
62
+ });
63
+ }
64
+ return;
65
+ }
66
+ if (!sandbox)
67
+ return;
68
+ if (result.source === 'failed') {
69
+ writeMarker(eventDir, MARKER_FAILED, {
70
+ ts: nowIso(),
71
+ error: result.error ?? 'npm install failed',
72
+ logPath: exports.ASYNC_INSTALL_LOG,
73
+ });
74
+ }
75
+ else {
76
+ writeMarker(eventDir, MARKER_READY, {
77
+ ts: nowIso(),
78
+ durationMs: Date.now() - start,
79
+ source: result.source,
80
+ });
81
+ }
82
+ }
83
+ /**
84
+ * 派发 detached 后台进程跑依赖安装并立即返回。
85
+ * 用 `node -e` 直接 require 本模块(编译产物)调 runAsyncInstallWorker,
86
+ * 不挂任何 CLI 子命令、对外不暴露;worker 内复用 installDependencies(cache / registry / fallback)。
87
+ * 沙箱下派发前清旧 marker;stdout/stderr 重定向到 logPath。
88
+ */
89
+ function dispatchAsyncInstall(opts) {
90
+ const eventDir = opts.eventDir ?? EVENT_DIR;
91
+ const logPath = opts.logPath ?? exports.ASYNC_INSTALL_LOG;
92
+ const sandbox = isSandboxEnv();
93
+ if (sandbox)
94
+ clearStaleMarkers(eventDir);
95
+ // targetDir 走 argv 传,不拼进代码串(免转义);只把本模块路径 JSON 内联进去。
96
+ const workerCode = `require(${JSON.stringify(__filename)}).runAsyncInstallWorker({ targetDir: process.argv[1] })`;
97
+ node_fs_1.default.mkdirSync(node_path_1.default.dirname(logPath), { recursive: true });
98
+ const fd = node_fs_1.default.openSync(logPath, 'w');
99
+ try {
100
+ const child = (0, node_child_process_1.spawn)(process.execPath, ['-e', workerCode, opts.targetDir], {
101
+ detached: true,
102
+ stdio: ['ignore', fd, fd],
103
+ });
104
+ child.unref();
105
+ return {
106
+ pid: child.pid,
107
+ logPath,
108
+ eventReadyPath: sandbox ? node_path_1.default.join(eventDir, MARKER_READY) : undefined,
109
+ eventFailedPath: sandbox ? node_path_1.default.join(eventDir, MARKER_FAILED) : undefined,
110
+ };
111
+ }
112
+ finally {
113
+ // 父进程关掉自己那份 fd;子进程已通过 stdio dup 持有独立副本。
114
+ node_fs_1.default.closeSync(fd);
115
+ }
116
+ }
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- 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.isSandboxEnv = exports.runAsyncInstallWorker = exports.dispatchAsyncInstall = exports.resolveNpmInstallRegistry = exports.installDependencies = exports.writeSparkMeta = exports.readSparkMeta = exports.TEMPLATE_PACKAGE_BY_STACK = exports.SUPPORTED_STACKS = 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
6
  Object.defineProperty(exports, "SUPPORTED_STACKS", { enumerable: true, get: function () { return template_1.SUPPORTED_STACKS; } });
@@ -11,3 +11,8 @@ Object.defineProperty(exports, "writeSparkMeta", { enumerable: true, get: functi
11
11
  var install_1 = require("./install");
12
12
  Object.defineProperty(exports, "installDependencies", { enumerable: true, get: function () { return install_1.installDependencies; } });
13
13
  Object.defineProperty(exports, "resolveNpmInstallRegistry", { enumerable: true, get: function () { return install_1.resolveNpmInstallRegistry; } });
14
+ var async_install_1 = require("./async-install");
15
+ Object.defineProperty(exports, "dispatchAsyncInstall", { enumerable: true, get: function () { return async_install_1.dispatchAsyncInstall; } });
16
+ Object.defineProperty(exports, "runAsyncInstallWorker", { enumerable: true, get: function () { return async_install_1.runAsyncInstallWorker; } });
17
+ Object.defineProperty(exports, "isSandboxEnv", { enumerable: true, get: function () { return async_install_1.isSandboxEnv; } });
18
+ Object.defineProperty(exports, "ASYNC_INSTALL_LOG", { enumerable: true, get: function () { return async_install_1.ASYNC_INSTALL_LOG; } });
@@ -15,6 +15,7 @@ exports.TEMPLATE_PACKAGE_BY_STACK = {
15
15
  'vite-react': '@lark-apaas/coding-template-vite-react',
16
16
  html: '@lark-apaas/coding-template-html',
17
17
  'nestjs-react-fullstack': '@lark-apaas/coding-template-nestjs-react-fullstack',
18
+ 'design-html': '@lark-apaas/coding-template-design-html',
18
19
  };
19
20
  /**
20
21
  * 短名 → template 包钉版表。renderTemplate 默认按这张表取版本号,表外 stack 跟 npm latest。
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runDesignBuild = runDesignBuild;
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
+ /**
8
+ * design-html 本地构建:跑 `npm run build`(模板里即 bash scripts/build.sh)。
9
+ * build.sh 读 process.env.CLIENT_BASE_PATH(平台预置),子进程继承 env 即透传,
10
+ * 不需要 CDN 凭证、不注入 MIAODA_*。
11
+ */
12
+ function runDesignBuild(opts) {
13
+ (0, logger_1.log)('deploy', 'Building (design)...');
14
+ try {
15
+ (0, node_child_process_1.execSync)('npm run build', { cwd: opts.projectDir, stdio: 'inherit', env: process.env });
16
+ }
17
+ catch (err) {
18
+ throw new error_1.AppError('DEPLOY_BUILD_FAILED', `npm run build failed: ${err.message}`);
19
+ }
20
+ }
@@ -0,0 +1,73 @@
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.uploadDesignArtifacts = uploadDesignArtifacts;
7
+ const node_fs_1 = __importDefault(require("node:fs"));
8
+ const node_path_1 = __importDefault(require("node:path"));
9
+ const error_1 = require("../../../../utils/error");
10
+ const logger_1 = require("../../../../utils/logger");
11
+ const constants_1 = require("../constants");
12
+ const protocol_1 = require("../protocol");
13
+ const tosutil_1 = require("./tosutil");
14
+ /** 递归收集 dir 下相对路径(posix 分隔),与 -flat 上传后远端 key 对齐。 */
15
+ function listLocalRelKeys(dir) {
16
+ const out = new Set();
17
+ const walk = (cur, rel) => {
18
+ for (const entry of node_fs_1.default.readdirSync(cur, { withFileTypes: true })) {
19
+ const childRel = rel ? `${rel}/${entry.name}` : entry.name;
20
+ if (entry.isDirectory())
21
+ walk(node_path_1.default.join(cur, entry.name), childRel);
22
+ else
23
+ out.add(childRel);
24
+ }
25
+ };
26
+ walk(dir, '');
27
+ return out;
28
+ }
29
+ /**
30
+ * design 部署上传(省带宽):
31
+ * 1. 本地 dist/output ──cp(local→tos)──▶ output_backup_path/ (唯一一次上传)
32
+ * 2. output_backup_path ──cp(tos→tos,同凭证同桶)──▶ output_latest_path (服务端复制)
33
+ * 3. ls latest → 跟本地 diff → rm latest 里源端已无的对象 (精确镜像)
34
+ * design-html 只产 dist/output,无 output_resource / output_static / capability。
35
+ */
36
+ async function uploadDesignArtifacts(opts) {
37
+ await Promise.resolve();
38
+ const tosutilPath = (0, tosutil_1.resolveTosutilPath)();
39
+ const outputDir = node_path_1.default.join(opts.projectDir, constants_1.DIST_DIR, constants_1.OUTPUT_DIR);
40
+ if (!node_fs_1.default.existsSync(outputDir)) {
41
+ throw new error_1.AppError('DEPLOY_NO_BUILD_OUTPUT', `Required directory missing: ${constants_1.DIST_DIR}/${constants_1.OUTPUT_DIR}`);
42
+ }
43
+ // 空产物防呆:本地一个文件都没有时,绝不继续(否则 latest 会被整体 prune 清空)。
44
+ const localKeys = listLocalRelKeys(outputDir);
45
+ if (localKeys.size === 0) {
46
+ throw new error_1.AppError('DEPLOY_UPLOAD_EMPTY', `No files under ${constants_1.DIST_DIR}/${constants_1.OUTPUT_DIR} to upload — check build output.`);
47
+ }
48
+ const cred = (0, protocol_1.parseTosUploadCredential)((0, protocol_1.requireDataKey)(opts.data, protocol_1.DataKey.OUTPUT_ALL_TOS_UPLOAD_CREDENTIAL), protocol_1.DataKey.OUTPUT_ALL_TOS_UPLOAD_CREDENTIAL);
49
+ const backupPath = (0, protocol_1.requireDataKey)(opts.data, protocol_1.DataKey.OUTPUT_BACKUP_PATH);
50
+ const latestPath = (0, protocol_1.requireDataKey)(opts.data, protocol_1.DataKey.OUTPUT_LATEST_PATH);
51
+ const base = (0, tosutil_1.tosutilUploadFromTos)(cred); // 鉴权 + bucket
52
+ // 1. 本地 → backup(version):唯一一次本地上传
53
+ (0, logger_1.log)('deploy', `Uploading ${constants_1.OUTPUT_DIR} → ${backupPath}...`);
54
+ (0, tosutil_1.uploadDirWithCredential)(tosutilPath, outputDir, { ...base, prefix: backupPath }, 'output_backup');
55
+ // 2. backup → latest:服务端 tos→tos cp(不占带宽)
56
+ (0, logger_1.log)('deploy', `Server-side copy ${backupPath} → ${latestPath}...`);
57
+ (0, tosutil_1.copyTosToTos)(tosutilPath, base, backupPath, latestPath);
58
+ // 3. latest 精确镜像:删源端已不存在的对象。
59
+ // 安全阀:远端有对象但跟本地零重合时,判定为 key 形态/ls 解析不匹配,跳过 prune——
60
+ // 宁可漏删(latest 残留旧文件)也绝不误删整目录(数据丢失)。design-html 的 build.sh
61
+ // 必产稳定名的 routes.json / *.html,正常重合不会为空,零重合即异常信号。
62
+ const remoteKeys = (0, tosutil_1.listRemoteKeys)(tosutilPath, { ...base, prefix: latestPath });
63
+ const overlap = remoteKeys.filter((k) => localKeys.has(k));
64
+ if (remoteKeys.length > 0 && overlap.length === 0) {
65
+ (0, logger_1.log)('deploy', `⚠ latest 远端 ${String(remoteKeys.length)} 个对象与本地零重合,疑似 key 形态不匹配,跳过 prune(避免误删)`);
66
+ return;
67
+ }
68
+ const staleKeys = remoteKeys.filter((k) => !localKeys.has(k));
69
+ if (staleKeys.length > 0) {
70
+ (0, logger_1.log)('deploy', `Pruning ${String(staleKeys.length)} stale object(s) from latest...`);
71
+ (0, tosutil_1.removeRemoteKeys)(tosutilPath, { ...base, prefix: latestPath }, staleKeys);
72
+ }
73
+ }
@@ -1,12 +1,16 @@
1
1
  "use strict";
2
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;
3
+ exports.savePluginInstances = exports.LocalReleaseStatus = exports.finalizeLocalRelease = exports.createLocalRelease = exports.uploadArtifacts = exports.uploadDesignArtifacts = exports.runDesignBuild = exports.runBuild = exports.preRelease = exports.prepareDeployContext = void 0;
4
4
  var context_1 = require("./context");
5
5
  Object.defineProperty(exports, "prepareDeployContext", { enumerable: true, get: function () { return context_1.prepareDeployContext; } });
6
6
  var pre_release_1 = require("./pre-release");
7
7
  Object.defineProperty(exports, "preRelease", { enumerable: true, get: function () { return pre_release_1.preRelease; } });
8
8
  var build_1 = require("./build");
9
9
  Object.defineProperty(exports, "runBuild", { enumerable: true, get: function () { return build_1.runBuild; } });
10
+ var design_build_1 = require("./design-build");
11
+ Object.defineProperty(exports, "runDesignBuild", { enumerable: true, get: function () { return design_build_1.runDesignBuild; } });
12
+ var design_upload_1 = require("./design-upload");
13
+ Object.defineProperty(exports, "uploadDesignArtifacts", { enumerable: true, get: function () { return design_upload_1.uploadDesignArtifacts; } });
10
14
  var upload_1 = require("./upload");
11
15
  Object.defineProperty(exports, "uploadArtifacts", { enumerable: true, get: function () { return upload_1.uploadArtifacts; } });
12
16
  var local_release_1 = require("./local-release");