@lark-apaas/miaoda-cli 0.1.29 → 0.1.31-alpha.ceaafa0
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.
- package/README.md +6 -6
- package/dist/api/app/api.js +50 -5
- package/dist/cli/commands/app/index.js +16 -11
- package/dist/cli/commands/deploy/modern.js +13 -0
- package/dist/cli/commands/index.js +8 -5
- package/dist/cli/commands/registry/index.js +10 -8
- package/dist/cli/commands/skills/index.js +21 -1
- package/dist/cli/handlers/app/export.js +62 -14
- package/dist/cli/handlers/deploy/modern.js +1 -0
- package/dist/cli/handlers/registry/add.js +3 -2
- package/dist/cli/handlers/registry/list.js +3 -8
- package/dist/cli/handlers/registry/shared.js +8 -4
- package/dist/cli/handlers/skills/sync.js +104 -0
- package/dist/config/migrate-configs/html-to-design-html.js +56 -0
- package/dist/config/migrate-configs/index.js +2 -0
- package/dist/services/app/init/template.js +4 -0
- package/dist/services/deploy/modern/atoms/index.js +5 -1
- package/dist/services/deploy/modern/atoms/release-tracker.js +43 -0
- package/dist/services/deploy/modern/pipelines/design-local.js +9 -8
- package/dist/services/deploy/modern/pipelines/local.js +13 -12
- package/dist/services/registry/index.js +2 -1
- package/dist/utils/coding-steering.js +10 -0
- package/dist/utils/devops-error.js +19 -5
- package/dist/utils/file-ops.js +81 -0
- package/dist/utils/npm-pack.js +33 -0
- package/dist/utils/sandbox-skills.js +271 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,16 +46,16 @@ miaoda file ls --output json
|
|
|
46
46
|
| `miaoda file ...` | 文件操作:上传、下载、元数据、签名下载、批量删除 |
|
|
47
47
|
| `miaoda db ...` | 数据操作:SQL 执行、表结构查询、数据导入导出 |
|
|
48
48
|
| `miaoda observability ...` | 线上日志、链路、前端源码堆栈反查、监控指标、运营指标 |
|
|
49
|
-
| `miaoda comment ...` |
|
|
50
|
-
| `miaoda registry ...` | 组件 registry:列出 / copy-in
|
|
49
|
+
| `miaoda comment ...` | 应用评论:查看评论列表、解决评论(仅 design-html 场景开放) |
|
|
50
|
+
| `miaoda registry ...` | 组件 registry:列出 / copy-in SDK 文件(本地执行) |
|
|
51
51
|
|
|
52
52
|
完整命令通过 `miaoda --help` 或 `miaoda <domain> --help` 查看。
|
|
53
53
|
|
|
54
|
-
## 组件 registry
|
|
54
|
+
## 组件 registry
|
|
55
55
|
|
|
56
|
-
`miaoda registry`
|
|
56
|
+
`miaoda registry` 提供 shadcn 式的「组件 registry」:把 SDK 文件**拷进**当前项目(copy-in),而非作为依赖安装。registry 是独立 npm 包,按运行时分发(design-html / html → buildless runtime → `@lark-apaas/coding-registry-buildless`)。
|
|
57
57
|
|
|
58
|
-
|
|
58
|
+
前置:当前工作目录已走过 `miaoda app init`,且 `.spark/meta.json` 的 `stack` 有对应 registry 包(当前支持 `design-html` / `html`);其余 stack 执行时报 `REGISTRY_STACK_UNSUPPORTED` 并提示支持列表。meta 一律从 cwd 读取,不随 `--dir` 走。
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
61
|
# 列出可用 SDK 条目(输出各文件 USAGE 注释块原文,便于 Agent 直接读用法)
|
|
@@ -76,7 +76,7 @@ miaoda registry add deck-slide --overwrite # 目标已存在时覆盖(
|
|
|
76
76
|
- **拍平落地**:所有文件**拍平**到项目根(`scripts/deck-slide.js` → 项目 `deck-slide.js`,不保留子目录);闭包内同名 basename 撞车时后者覆盖前者。
|
|
77
77
|
- **skip-by-disk**:项目已有同名(拍平后的 basename)文件默认跳过,`--overwrite` 才覆盖;不写任何安装记账文件,项目文件树即状态。
|
|
78
78
|
- **add 回执带 usage**:`add` 结果的 `entries` 只列**你请求的条目**(不含被 `Depends` 传递拉进来的依赖——依赖照常落地在 `added`/`skipped`,但那是"管道"),逐条给出 `usage`(USAGE 块原文)与 `dependsOn`,Agent `add` 完即可直接读用法,无需再 `registry list`。请求的条目即使已存在被跳过也回带 usage(`status` 标 `added`/`skipped`,据此判断磁盘上是 registry 版还是你改过的旧版)。`--json` 结构为 `{registryVersion, added, skipped, dryRun, entries}`;pretty(默认)输出 `registry@<ver>` + `added:`/`skipped:` 逗号列表 + 各请求条目的 USAGE 块,稳定 label、无 JSON 数组、无截断,对 Agent 与人同样可读。
|
|
79
|
-
- `--version <ver>` 指定 registry 包版本或 dist-tag(缺省 `latest
|
|
79
|
+
- `--version <ver>` 指定 registry 包版本或 dist-tag(缺省 `latest`);`add` 的 `--dir <path>` 指定 copy-in 落地目录(缺省当前目录),不影响 `.spark/meta.json` 读取。
|
|
80
80
|
|
|
81
81
|
## 全局参数
|
|
82
82
|
|
package/dist/api/app/api.js
CHANGED
|
@@ -5,6 +5,7 @@ exports.updateAppMeta = updateAppMeta;
|
|
|
5
5
|
exports.getCodeArchive = getCodeArchive;
|
|
6
6
|
const http_1 = require("../../utils/http");
|
|
7
7
|
const devops_error_1 = require("../../utils/devops-error");
|
|
8
|
+
const error_1 = require("../../utils/error");
|
|
8
9
|
const DEFAULT_ERR_CODE = 'INTERNAL_DEVOPS_ERROR';
|
|
9
10
|
function envelopeOpts(errPrefix) {
|
|
10
11
|
return {
|
|
@@ -24,22 +25,66 @@ async function updateAppMeta(req) {
|
|
|
24
25
|
// 路径已带 appID,body 里也保留以匹配 BAM IDL 的 sensitive:"no" 透传约定
|
|
25
26
|
return (0, http_1.postInnerApi)(url, req, envelopeOpts('Failed to update app'));
|
|
26
27
|
}
|
|
28
|
+
function mapExportError(bizCode, message, source) {
|
|
29
|
+
// token 是敏感令牌,错误信息只截前 8 位辅助定位
|
|
30
|
+
const sourceDesc = source.sourceAppID
|
|
31
|
+
? `应用 ${source.sourceAppID}`
|
|
32
|
+
: `分享 token ${(source.sourceMetaToken ?? '').slice(0, 8)}...`;
|
|
33
|
+
switch (bizCode) {
|
|
34
|
+
// 身份换取失败,或非源应用协作者(服务端 DOWNLOAD 鉴权不过)
|
|
35
|
+
case devops_error_1.BizErrCode.STUDIO_UNAUTHENTICATED:
|
|
36
|
+
return new error_1.AppError('EXPORT_PERMISSION_DENIED', `无权导出 ${sourceDesc}:${message}`, {
|
|
37
|
+
next_actions: [
|
|
38
|
+
`确认你是源应用的协作者;不是的话请让应用所有者添加协作者后重试`,
|
|
39
|
+
'这是权限问题,不是凭证配置或服务端抖动:不要重复执行本命令,也不要尝试自行签发 token',
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
// meta_token 无效或未绑定妙搭应用(服务端 ResolveAppIDByMetaToken 反查失败)
|
|
43
|
+
case devops_error_1.BizErrCode.STUDIO_APP_NOT_FOUND:
|
|
44
|
+
return new error_1.AppError('EXPORT_APP_NOT_FOUND', `${sourceDesc} 未找到对应应用:${message}`, {
|
|
45
|
+
next_actions: [
|
|
46
|
+
'核对分享 token / 链接是否完整(创意应用 URL 形如 https://<tenant>.feishu.cn/page/<token>)',
|
|
47
|
+
'确认目标是创意应用(只有创意应用有分享 token);这是目标不存在问题:不要重复执行本命令,请向用户确认',
|
|
48
|
+
],
|
|
49
|
+
});
|
|
50
|
+
// 应用不存在(错误 ID)
|
|
51
|
+
case devops_error_1.BizErrCode.DEVOPS_APP_NOT_FOUND:
|
|
52
|
+
case devops_error_1.BizErrCode.DEVOPS_APP_NOT_FOUND_ALT:
|
|
53
|
+
return new error_1.AppError('EXPORT_APP_NOT_FOUND', `${sourceDesc} 不存在或不可访问:${message}`, {
|
|
54
|
+
next_actions: [
|
|
55
|
+
'核对源应用 ID(应用页 URL 形如 /app/app_xxx,该段才是应用 ID)',
|
|
56
|
+
'这是目标不存在问题:不要重复执行本命令,请向用户确认正确的应用 ID',
|
|
57
|
+
],
|
|
58
|
+
});
|
|
59
|
+
default:
|
|
60
|
+
return (0, devops_error_1.mapDevopsError)(bizCode, message);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
27
63
|
/**
|
|
28
|
-
* GET /api/v1/studio/innerapi/apps/:appID/code-archive
|
|
64
|
+
* GET /api/v1/studio/innerapi/apps/:appID/code-archive — 导出源应用源码归档。
|
|
29
65
|
* 走 studio_server inner 通道(force 身份,服务侧 force→lark 换取后按 DOWNLOAD 鉴权,
|
|
30
|
-
*
|
|
66
|
+
* 鉴权对象是源应用),与 comment 域同一 inner 信封;创意应用升级链路(app export)消费。
|
|
31
67
|
*
|
|
32
68
|
* path :appID 必须是当前沙箱应用:网关 apaas_force_admin_plugin 强校验 URL appID 与沙箱
|
|
33
69
|
* AK 绑定应用一致(不一致 400 "appID in url is illegal"),跨应用导出目标因此走 query。
|
|
70
|
+
* 源应用定位二选一:sourceAppID(app_xxx),或 sourceMetaToken(创意应用分享 URL
|
|
71
|
+
* /page/<token> 的 CCM token,服务端反查 appID 后按同一口径鉴权)。
|
|
34
72
|
*/
|
|
35
73
|
async function getCodeArchive(params) {
|
|
36
|
-
const { appID, sourceAppID, checkpointID } = params;
|
|
37
|
-
|
|
38
|
-
|
|
74
|
+
const { appID, sourceAppID, sourceMetaToken, checkpointID } = params;
|
|
75
|
+
if (!sourceAppID === !sourceMetaToken) {
|
|
76
|
+
throw new error_1.AppError('ARGS_INVALID', 'sourceAppID 与 sourceMetaToken 必须恰传其一');
|
|
77
|
+
}
|
|
78
|
+
const sourceQuery = sourceAppID
|
|
79
|
+
? `sourceAppID=${encodeURIComponent(sourceAppID)}`
|
|
80
|
+
: `sourceMetaToken=${encodeURIComponent(sourceMetaToken ?? '')}`;
|
|
81
|
+
let url = `/api/v1/studio/innerapi/apps/${encodeURIComponent(appID)}/code-archive?` + sourceQuery;
|
|
39
82
|
if (checkpointID !== undefined && checkpointID > 0) {
|
|
40
83
|
url += `&checkPointID=${String(checkpointID)}`;
|
|
41
84
|
}
|
|
42
85
|
return (0, http_1.getInnerApi)(url, {
|
|
43
86
|
errPrefix: 'Failed to export code archive',
|
|
87
|
+
defaultErrCode: DEFAULT_ERR_CODE,
|
|
88
|
+
mapErr: (bizCode, message) => mapExportError(bizCode, message, { sourceAppID, sourceMetaToken }),
|
|
44
89
|
});
|
|
45
90
|
}
|
|
@@ -122,32 +122,37 @@ JSON 输出
|
|
|
122
122
|
}
|
|
123
123
|
function registerAppExport(parent) {
|
|
124
124
|
const cmd = parent
|
|
125
|
-
.command('export <app-id>')
|
|
126
|
-
.description('
|
|
125
|
+
.command('export <app-id-or-token>')
|
|
126
|
+
.description('导出指定应用的源码到本工程(创意应用升级:拉取创意稿作只读参考物);接受应用 ID(app_xxx)或创意应用分享 token')
|
|
127
127
|
.option('--out <dir>', '输出目录(相对当前目录)', 'source_package/creative')
|
|
128
128
|
.option('--checkpoint <n>', '按检查点版本导出;缺省为最新提交(HEAD)')
|
|
129
129
|
.addHelpText('after', `
|
|
130
130
|
说明
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
131
|
+
从服务端导出源应用(可以不是当前应用)的源码归档并解压到 --out 目录,导出完成后写
|
|
132
|
+
--out/.upgrade-manifest.json(含 sourceAppId / 导出时间 / 文件数),供升级链路幂等
|
|
133
|
+
判定与 skill 发现使用。调用者须为源应用协作者(服务端 DOWNLOAD 鉴权)。
|
|
134
|
+
<app-id-or-token> 两种形态等价:应用 ID(app_xxx)或创意应用分享 token。拿到的是
|
|
135
|
+
分享链接(https://<tenant>.feishu.cn/page/<token>)时,提取其中 /page/ 后的 <token> 段传入。
|
|
136
|
+
请求以当前应用(MIAODA_APP_ID)为路径主体、源应用走 sourceAppID / sourceMetaToken 参数。
|
|
135
137
|
|
|
136
138
|
幂等
|
|
137
|
-
--out 已存在 manifest
|
|
139
|
+
--out 已存在 manifest 且源定位(sourceAppId / sourceMetaToken)一致时跳过导出
|
|
140
|
+
(skipped=true),重复调用安全。
|
|
138
141
|
|
|
139
142
|
JSON 输出
|
|
140
143
|
{"data": {"appId": "...", "out": "source_package/creative", "fileCount": <n>,
|
|
141
144
|
"archiveName": "...", "skipped": false}}
|
|
142
145
|
跳过时:{"data": {"appId": "...", "out": "...", "skipped": true, "reason": "already_exported"}}
|
|
146
|
+
appId 始终是实际导出的源应用 ID(传 token 时为服务端反查结果)。
|
|
143
147
|
|
|
144
148
|
失败
|
|
145
149
|
源应用无可导出内容(从未提交过代码)→ EXPORT_EMPTY_ARCHIVE,请先在源应用完成一轮创作。
|
|
146
|
-
|
|
150
|
+
非源应用协作者 → EXPORT_PERMISSION_DENIED,权限问题,重试无效,需应用所有者加协作者。
|
|
151
|
+
应用 ID 不存在,或 token 未找到对应应用 → EXPORT_APP_NOT_FOUND,核对入参,重试无效。
|
|
147
152
|
|
|
148
153
|
示例
|
|
149
154
|
$ miaoda app export app_demo_source
|
|
150
|
-
$ miaoda app export
|
|
155
|
+
$ miaoda app export DemoPageTokenAbCdEf123456789
|
|
151
156
|
$ miaoda app export app_demo_source --checkpoint 3
|
|
152
157
|
`);
|
|
153
158
|
cmd.action((0, shared_1.withHelp)(cmd, async (appId, rawOpts) => {
|
|
@@ -217,7 +222,7 @@ function registerAppInit(parent) {
|
|
|
217
222
|
.command('init')
|
|
218
223
|
.description('初始化应用代码:抓 template 渲染、同步 upgrade/templates、装 .agent/steering/ skills、写 .spark/meta.json。.spark/meta.json 已存在则直接退出')
|
|
219
224
|
.option('--template <stack>', `技术栈短名(${index_1.SUPPORTED_STACKS.join(' / ')})`)
|
|
220
|
-
.option('--app-type <type>', 'aPaaS 业务类型(html / modern_html / jspage / full_stack);未传 --template 时用于解析技术栈')
|
|
225
|
+
.option('--app-type <type>', 'aPaaS 业务类型(html / modern_html / frontend / jspage / full_stack);未传 --template 时用于解析技术栈')
|
|
221
226
|
.option('--source-path <path>', '导入源目录:把该目录内容聚合进脚手架 src/(当前仅 --app-type modern_html 生效)')
|
|
222
227
|
.option('--conf [json]', 'init 配置 JSON。支持 {"version": "<template 版本>"},默认 latest')
|
|
223
228
|
.option('--skip-install', '跳过依赖安装', false)
|
|
@@ -237,7 +242,7 @@ function registerAppInit(parent) {
|
|
|
237
242
|
|
|
238
243
|
技术栈解析(--template / --app-type)
|
|
239
244
|
--template 传了 → 直接用(vite-react / html / design-html / nestjs-react-fullstack)
|
|
240
|
-
否则 --app-type 传了 → 查表:html→design-html, modern_html→html, jspage→vite-react, full_stack→nestjs-react-fullstack
|
|
245
|
+
否则 --app-type 传了 → 查表:html→design-html, modern_html→html, frontend/jspage→vite-react, full_stack→nestjs-react-fullstack
|
|
241
246
|
两者都没传 → 报错 ARGS_INVALID
|
|
242
247
|
|
|
243
248
|
源码导入(--source-path)
|
|
@@ -17,6 +17,7 @@ function registerDeployCommandsModern(program) {
|
|
|
17
17
|
.description('触发 modern 应用发布:本地构建 + 本地部署(preRelease + localPublish)')
|
|
18
18
|
.option('--dir <path>', '项目目录', '.')
|
|
19
19
|
.option('--skip-build', '跳过 build 步骤(已构建好时使用)', false)
|
|
20
|
+
.option('--skip-release', '不创建/更新本地发布单,发布单状态由外部托管', false)
|
|
20
21
|
.option('--conf <json>', '关联元信息 JSON,仅识别 checkPointVersion / commitID 两字段')
|
|
21
22
|
.addHelpText('after', `
|
|
22
23
|
不要用异步模式或后台模式调用 deploy,否则调用可能提前结束,Agent 会误判发布已完成。
|
|
@@ -34,6 +35,15 @@ function registerDeployCommandsModern(program) {
|
|
|
34
35
|
6. savePluginInstances(扫描 dist/output_capabilities/*.json 批量注册)
|
|
35
36
|
7. finalizeLocalRelease(Finished|Failed)
|
|
36
37
|
|
|
38
|
+
--skip-release(发布单外部托管)
|
|
39
|
+
跳过第 4、7 步(createLocalRelease / updateLocalRelease),其余步骤不变:
|
|
40
|
+
preRelease 仍照常签发 version,构建、上传、capability 注册照常执行。
|
|
41
|
+
外部据 stdout 返回的 version / preReleaseID 自行建单,并据本命令的
|
|
42
|
+
退出码决定把发布单翻成 Finished 还是 Failed —— 失败态由外部负责,CLI 不再兜底。
|
|
43
|
+
该模式下 releaseID / url 输出为 null(CLI 未建单,无从得知)。
|
|
44
|
+
注意:--conf 的 checkPointVersion / commitID 唯一消费者是 createLocalRelease,
|
|
45
|
+
本模式下不生效(静默忽略,不报错),需要关联请在外部建单时自行传递。
|
|
46
|
+
|
|
37
47
|
--conf(关联元信息透传)
|
|
38
48
|
值为 JSON string,只识别两个字段,其余 key 忽略:
|
|
39
49
|
checkPointVersion 关联的 checkpoint 版本
|
|
@@ -42,11 +52,13 @@ function registerDeployCommandsModern(program) {
|
|
|
42
52
|
|
|
43
53
|
JSON 输出(stdout)
|
|
44
54
|
{"data": {"appId": "...", "version": <n>, "url": "...", "releaseID": "...", "preReleaseID": "..."}}
|
|
55
|
+
--skip-release 时 url 与 releaseID 为 null,字段本身仍在。
|
|
45
56
|
|
|
46
57
|
示例
|
|
47
58
|
$ miaoda deploy
|
|
48
59
|
$ miaoda deploy --dir ./my-app
|
|
49
60
|
$ miaoda deploy --skip-build
|
|
61
|
+
$ miaoda deploy --skip-release
|
|
50
62
|
$ miaoda deploy --conf '{"checkPointVersion":"v3","commitID":"a1b2c3d"}'
|
|
51
63
|
`);
|
|
52
64
|
deployCmd.action((0, shared_1.withHelp)(deployCmd, async (rawOpts) => {
|
|
@@ -54,6 +66,7 @@ JSON 输出(stdout)
|
|
|
54
66
|
dir: rawOpts.dir ?? '.',
|
|
55
67
|
appId: (0, shared_1.resolveAppId)({}),
|
|
56
68
|
skipBuild: rawOpts.skipBuild,
|
|
69
|
+
skipRelease: rawOpts.skipRelease,
|
|
57
70
|
conf: rawOpts.conf,
|
|
58
71
|
});
|
|
59
72
|
}));
|
|
@@ -31,6 +31,9 @@ function resolveScene(appType, _archType) {
|
|
|
31
31
|
return 'design-html';
|
|
32
32
|
return 'default';
|
|
33
33
|
}
|
|
34
|
+
// registry(shadcn 式 copy-in SDK)挂在全部 scene:scene 路由不感知 registry 的可用性,
|
|
35
|
+
// stack 是否有对应 registry 包由运行时映射表(services/registry STACK_RUNTIME)校验,
|
|
36
|
+
// 无映射的 stack 执行时报 REGISTRY_STACK_UNSUPPORTED 并提示支持列表。
|
|
34
37
|
const SCENE_REGISTRARS = {
|
|
35
38
|
default: (p) => {
|
|
36
39
|
// init / upgrade / skills 都挂出来:本地用户不需要 export MIAODA_APP_TYPE=3
|
|
@@ -42,13 +45,13 @@ const SCENE_REGISTRARS = {
|
|
|
42
45
|
(0, index_1.registerFileCommands)(p);
|
|
43
46
|
(0, index_3.registerObservabilityCommands)(p);
|
|
44
47
|
(0, index_6.registerSkillsCommands)(p);
|
|
45
|
-
(0,
|
|
48
|
+
(0, index_8.registerRegistryCommands)(p);
|
|
46
49
|
},
|
|
47
50
|
modern: (p) => {
|
|
48
51
|
(0, index_4.registerAppCommands)(p, { includeInit: true });
|
|
49
52
|
(0, modern_1.registerDeployCommandsModern)(p);
|
|
50
53
|
(0, index_6.registerSkillsCommands)(p);
|
|
51
|
-
(0,
|
|
54
|
+
(0, index_8.registerRegistryCommands)(p);
|
|
52
55
|
},
|
|
53
56
|
// application scene(AppType_APPLICATION=3):
|
|
54
57
|
// 在 default 命令集基础上加 skills(按 meta.json.stack 拉对应 stack 技能)
|
|
@@ -60,7 +63,7 @@ const SCENE_REGISTRARS = {
|
|
|
60
63
|
(0, index_1.registerFileCommands)(p);
|
|
61
64
|
(0, index_3.registerObservabilityCommands)(p);
|
|
62
65
|
(0, index_6.registerSkillsCommands)(p);
|
|
63
|
-
(0,
|
|
66
|
+
(0, index_8.registerRegistryCommands)(p);
|
|
64
67
|
},
|
|
65
68
|
// design scene(AppType_DESIGN=4):design-stack 仅 SSR 渲染、无后端业务逻辑、
|
|
66
69
|
// 无数据库、无 UGC 文件。命令集裁掉 db / file,保留 deploy / observability / app(init) / skills。
|
|
@@ -69,11 +72,11 @@ const SCENE_REGISTRARS = {
|
|
|
69
72
|
(0, index_5.registerDeployCommands)(p);
|
|
70
73
|
(0, index_3.registerObservabilityCommands)(p);
|
|
71
74
|
(0, index_6.registerSkillsCommands)(p);
|
|
72
|
-
(0,
|
|
75
|
+
(0, index_8.registerRegistryCommands)(p);
|
|
73
76
|
},
|
|
74
77
|
// design-html scene(AppType_DESIGN_HTML=8):buildless 静态 HTML,
|
|
75
78
|
// 命令集与 modern 一致(app init/sync + modern 拆分版 deploy + skills),不挂 db/file/observability。
|
|
76
|
-
//
|
|
79
|
+
// comment(应用评论)仅 design-html 开放。
|
|
77
80
|
'design-html': (p) => {
|
|
78
81
|
(0, index_4.registerAppCommands)(p, { includeInit: true });
|
|
79
82
|
(0, modern_1.registerDeployCommandsModern)(p);
|
|
@@ -4,7 +4,8 @@ exports.registerRegistryCommands = registerRegistryCommands;
|
|
|
4
4
|
const shared_1 = require("../../../cli/commands/shared");
|
|
5
5
|
const index_1 = require("../../../cli/handlers/registry/index");
|
|
6
6
|
/**
|
|
7
|
-
* miaoda registry
|
|
7
|
+
* miaoda registry:「组件 registry」(shadcn 式 copy-in),供有对应 registry 包的 stack 使用
|
|
8
|
+
* (当前 design-html / html,见 services/registry 的 STACK_RUNTIME)。
|
|
8
9
|
*
|
|
9
10
|
* registry 是独立 npm 包,按 runtime 命名分发(buildless runtime → @lark-apaas/coding-registry-buildless)。
|
|
10
11
|
* SDK 文件放在包根的 components/ · scripts/ · stylesheets/ 三个目录下(这三个顶层目录里的代码文件
|
|
@@ -15,7 +16,7 @@ const index_1 = require("../../../cli/handlers/registry/index");
|
|
|
15
16
|
function registerRegistryCommands(program) {
|
|
16
17
|
const registryCmd = program
|
|
17
18
|
.command('registry')
|
|
18
|
-
.description('组件 registry:列出 / copy-in
|
|
19
|
+
.description('组件 registry:列出 / copy-in SDK 文件')
|
|
19
20
|
.usage('<command> [flags]');
|
|
20
21
|
registryCmd.action(() => {
|
|
21
22
|
registryCmd.outputHelp();
|
|
@@ -32,7 +33,9 @@ function registerRegistryCommands(program) {
|
|
|
32
33
|
- add 落地:拍平到项目根(包根 scripts/x.js → 项目 x.js);同名 basename 撞车时后者覆盖前者
|
|
33
34
|
|
|
34
35
|
前置
|
|
35
|
-
|
|
36
|
+
当前工作目录已走过 'miaoda app init',且 .spark/meta.json 的 stack 有对应
|
|
37
|
+
registry 包(当前支持 design-html / html);其余 stack 执行时报 REGISTRY_STACK_UNSUPPORTED
|
|
38
|
+
meta 一律从 cwd 读取,add 的 --dir 只影响 copy-in 落地目录
|
|
36
39
|
`);
|
|
37
40
|
registerRegistryList(registryCmd);
|
|
38
41
|
registerRegistryAdd(registryCmd);
|
|
@@ -41,7 +44,6 @@ function registerRegistryList(parent) {
|
|
|
41
44
|
const cmd = parent
|
|
42
45
|
.command('list')
|
|
43
46
|
.description('列出 registry 可用 SDK 条目,输出各文件的 USAGE 块原文')
|
|
44
|
-
.option('--dir <path>', '项目目录,默认当前目录', '.')
|
|
45
47
|
.option('--version <ver>', 'registry 包版本或 dist-tag,缺省 latest')
|
|
46
48
|
.addHelpText('after', `
|
|
47
49
|
输出
|
|
@@ -57,7 +59,6 @@ JSON 输出
|
|
|
57
59
|
`);
|
|
58
60
|
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
59
61
|
await (0, index_1.handleRegistryList)({
|
|
60
|
-
dir: rawOpts.dir,
|
|
61
62
|
version: rawOpts.version,
|
|
62
63
|
});
|
|
63
64
|
}));
|
|
@@ -67,7 +68,7 @@ function registerRegistryAdd(parent) {
|
|
|
67
68
|
.command('add')
|
|
68
69
|
.description('把一个或多个 SDK 文件 copy-in 到当前项目(拍平到项目根,含 Depends 闭包)')
|
|
69
70
|
.argument('<token...>', 'name / 完整文件名 / relPath,可传多个;同名多匹配全拉,Depends 自动传递解析')
|
|
70
|
-
.option('--dir <path>', '
|
|
71
|
+
.option('--dir <path>', 'copy-in 落地目录,默认当前目录(stack 一律读 cwd 的 .spark/meta.json)', '.')
|
|
71
72
|
.option('--version <ver>', 'registry 包版本或 dist-tag,缺省 latest')
|
|
72
73
|
.option('--overwrite', '目标文件已存在时覆盖(默认跳过以保护用户改动)', false)
|
|
73
74
|
.option('--dry-run', '只报告将写 / 将跳哪些文件,不落盘', false)
|
|
@@ -75,8 +76,9 @@ function registerRegistryAdd(parent) {
|
|
|
75
76
|
行为
|
|
76
77
|
- token 寻址:name(foo)/ 完整文件名(foo.css)/ relPath(stylesheets/foo.css)任一形式
|
|
77
78
|
- 多匹配全拉:token 命中多个同名条目时全部 copy-in;想只要其一就写完整文件名 / relPath
|
|
78
|
-
- 解析 USAGE 块 Depends 传递闭包,把 SDK
|
|
79
|
-
(包根 scripts/deck-stage.js →
|
|
79
|
+
- 解析 USAGE 块 Depends 传递闭包,把 SDK 文件**拍平**拷到落地目录(--dir,默认当前目录)
|
|
80
|
+
(包根 scripts/deck-stage.js → 落地 deck-stage.js;同名 basename 撞车时后者覆盖前者)
|
|
81
|
+
- stack 一律读当前工作目录的 .spark/meta.json,--dir 不影响 meta 读取
|
|
80
82
|
- skip-by-disk:目标文件已存在默认跳过;--overwrite 才覆盖
|
|
81
83
|
- 不写任何安装记账文件(项目文件树即状态)
|
|
82
84
|
|
|
@@ -21,11 +21,16 @@ function registerSkillsCommands(program) {
|
|
|
21
21
|
function registerSkillsSync(parent) {
|
|
22
22
|
const cmd = parent
|
|
23
23
|
.command('sync')
|
|
24
|
-
.description('同步 coding-steering
|
|
24
|
+
.description('同步 skills 到 user app(默认 coding-steering;带 --type 时走 sandbox-skills)')
|
|
25
25
|
.option('--dir <path>', '项目目录,默认当前目录', '.')
|
|
26
26
|
.option('--version <ver>', 'coding-steering 包版本或 dist-tag,缺省 latest')
|
|
27
27
|
.option('--local', '走本地 dev / agent 输出形态:拷到 .agents/skills 平铺 + 创建 .claude/skills 软链。' +
|
|
28
28
|
'不传时退回老形态:拷到 .agent/skills/steering/<stack>/skills/(跟沙箱端 update-skills.sh 对齐)', false)
|
|
29
|
+
.option('--type <type>', '走 sandbox 分支:miaoda | miaoda-modern | miaoda-design | openclaw。有值时忽略 --local,' +
|
|
30
|
+
'从 @lark-apaas/coding-<pkg>-sandbox-skills 拉包平铺写入,并额外同步 steering ' +
|
|
31
|
+
'到 .agent/skills/steering/<stack>/(--version 对后者仍生效)')
|
|
32
|
+
.option('--tgz-key <key>', '配合 --type 使用;老 TCC skillsUpdateTgzKey 值透传,' +
|
|
33
|
+
'从 skills_versions/all_<X>.tgz 反推出 lane 名 X,再过滤出 -alpha.<X> 版本;缺省走 @latest')
|
|
29
34
|
.addHelpText('after', `
|
|
30
35
|
前置要求
|
|
31
36
|
当前目录(或 --dir)已走过 'miaoda app init'(.spark/meta.json 含 stack)
|
|
@@ -41,21 +46,36 @@ function registerSkillsSync(parent) {
|
|
|
41
46
|
.agents/tech.md
|
|
42
47
|
.claude/skills → ../.agents/skills(软链,让 Claude Code 识别同一份)
|
|
43
48
|
|
|
49
|
+
传 --type(沙箱链路,两部分都同步):
|
|
50
|
+
.agent/skills/<skill>/... ← 业务线 skill,平铺
|
|
51
|
+
.agent/skills/steering/<stack>/skills/<skill>/ ← steering,同上面默认形态
|
|
52
|
+
(openclaw 的业务线部分落 skills/ 而非 .agent/skills/)
|
|
53
|
+
|
|
44
54
|
JSON 输出
|
|
45
55
|
{"data": {"stack": "...", "version": "...", "syncedSkills": [...], "techSynced": true|false,
|
|
46
56
|
"claudeSkillsLink": "created|updated|noop|conflict" // 仅 --local 时}}
|
|
47
57
|
|
|
58
|
+
传 --type 时:
|
|
59
|
+
{"data": {"mode": "sandbox", "type": "...", "packageName": "...", "version": "...",
|
|
60
|
+
"lane": "...", "syncedTop": [...], "targetDir": "...",
|
|
61
|
+
"steering": {"status": "synced|skipped|failed", ...}}}
|
|
62
|
+
steering 失败或无 stack 不影响整体成功——业务线 skill 已同步,不该因增量部分阻断
|
|
63
|
+
|
|
48
64
|
示例
|
|
49
65
|
$ miaoda skills sync # 拉 @latest
|
|
50
66
|
$ miaoda skills sync --local # 本地 dev / agent 用
|
|
51
67
|
$ miaoda skills sync --version 0.2.0 # 显式指定版本
|
|
52
68
|
$ miaoda skills sync --dir ./my-app
|
|
69
|
+
# 沙箱链路(由 feida-ai 下发):业务线 skill + steering 一次同步到位
|
|
70
|
+
$ miaoda skills sync --type miaoda-modern --tgz-key 'skills_versions/all_demo-lane.tgz'
|
|
53
71
|
`);
|
|
54
72
|
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
55
73
|
await (0, index_1.handleSkillsSync)({
|
|
56
74
|
dir: rawOpts.dir,
|
|
57
75
|
version: rawOpts.version,
|
|
58
76
|
local: rawOpts.local,
|
|
77
|
+
type: rawOpts.type,
|
|
78
|
+
tgzKey: rawOpts.tgzKey,
|
|
59
79
|
});
|
|
60
80
|
}));
|
|
61
81
|
}
|
|
@@ -46,6 +46,29 @@ const api = __importStar(require("../../../api/index"));
|
|
|
46
46
|
const error_1 = require("../../../utils/error");
|
|
47
47
|
const output_1 = require("../../../utils/output");
|
|
48
48
|
const logger_1 = require("../../../utils/logger");
|
|
49
|
+
/** 妙搭应用 ID 形态:`app_` + 字母数字下划线。非此形态按创意应用分享 token 处理。 */
|
|
50
|
+
const MIAODA_APP_ID_PATTERN = /^app_\w{1,64}$/;
|
|
51
|
+
/** 分享 token 宽松形态:16-128 位字母数字(CCM token 实际 20+ 位)。只拦明显不是 token 的输入(误传 flag / 短串),不追平服务端精确口径。 */
|
|
52
|
+
const META_TOKEN_PATTERN = /^[A-Za-z0-9_-]{16,128}$/;
|
|
53
|
+
/**
|
|
54
|
+
* 解析位置参数为源应用定位:app_xxx → appId;满足 token 宽松形态的串 → metaToken 透传
|
|
55
|
+
* (精确校验交给服务端反查,避免和服务端口径漂移)。不支持直接传分享链接——调用方
|
|
56
|
+
* (上游 agent)自行从链接提取 /page/<token> 段后传 token。
|
|
57
|
+
*/
|
|
58
|
+
function parseSourceRef(raw) {
|
|
59
|
+
if (MIAODA_APP_ID_PATTERN.test(raw)) {
|
|
60
|
+
return { appId: raw };
|
|
61
|
+
}
|
|
62
|
+
if (!META_TOKEN_PATTERN.test(raw)) {
|
|
63
|
+
throw new error_1.AppError('ARGS_INVALID', `无法识别的源应用定位:${raw}`, {
|
|
64
|
+
next_actions: [
|
|
65
|
+
'接受两种形态:应用 ID(app_xxx)或创意应用分享 token(16 位以上字母数字)',
|
|
66
|
+
'拿到的是分享链接(https://<tenant>.feishu.cn/page/<token>)时,请提取其中 /page/ 后的 <token> 段传入',
|
|
67
|
+
],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return { metaToken: raw };
|
|
71
|
+
}
|
|
49
72
|
/** 导出目录默认值:创意升级链路的参考物约定位置。 */
|
|
50
73
|
exports.DEFAULT_EXPORT_OUT = 'source_package/creative';
|
|
51
74
|
/** 升级 manifest 文件名:前置钩子幂等标记 / skill workspace-contains 锚点 / skill 第 0 步信息源。 */
|
|
@@ -97,7 +120,7 @@ function countFiles(dir) {
|
|
|
97
120
|
return count;
|
|
98
121
|
}
|
|
99
122
|
/**
|
|
100
|
-
* miaoda app export <app-id> [--out <dir>] [--checkpoint <n>]
|
|
123
|
+
* miaoda app export <app-id-or-token> [--out <dir>] [--checkpoint <n>]
|
|
101
124
|
*
|
|
102
125
|
* 创意应用升级链路:把源应用(创意稿)的源码从服务端导出到本工程,作为只读参考物。
|
|
103
126
|
* 链路:studio_server InnerGetCodeArchive(inner 通道,force 身份鉴权:调用者须为源应用
|
|
@@ -105,15 +128,18 @@ function countFiles(dir) {
|
|
|
105
128
|
* skill workspace-contains 锚点 + skill 第 0 步信息源)。
|
|
106
129
|
*
|
|
107
130
|
* URL 形状:path :appID 是当前沙箱应用(网关强校验其与沙箱 AK 一致),源应用走 query
|
|
108
|
-
* sourceAppID
|
|
131
|
+
* (sourceAppID 或 sourceMetaToken 二选一)—— 跨应用(升级常态:新应用沙箱里导出源
|
|
132
|
+
* 创意应用)必须如此。位置参数接受应用 ID / 分享 token 两种形态。
|
|
109
133
|
*
|
|
110
|
-
* 幂等:--out 已存在 manifest
|
|
134
|
+
* 幂等:--out 已存在 manifest 且源定位(sourceAppId / sourceMetaToken)一致时跳过导出
|
|
135
|
+
* 直接返回(重试/多轮不重复拉取)。
|
|
111
136
|
*/
|
|
112
137
|
async function handleAppExport(opts) {
|
|
113
|
-
const
|
|
114
|
-
if (!
|
|
115
|
-
throw new error_1.AppError('ARGS_INVALID', 'app-id 不能为空');
|
|
138
|
+
const rawSource = (opts.appId || '').trim();
|
|
139
|
+
if (!rawSource) {
|
|
140
|
+
throw new error_1.AppError('ARGS_INVALID', 'app-id-or-token 不能为空');
|
|
116
141
|
}
|
|
142
|
+
const source = parseSourceRef(rawSource);
|
|
117
143
|
// 当前沙箱应用:网关 AK 校验要求 path :appID 与沙箱绑定应用一致
|
|
118
144
|
const currentAppId = (opts.currentAppId || '').trim();
|
|
119
145
|
if (!currentAppId) {
|
|
@@ -124,13 +150,24 @@ async function handleAppExport(opts) {
|
|
|
124
150
|
const outRel = normalizeOutDir(opts.out ?? exports.DEFAULT_EXPORT_OUT);
|
|
125
151
|
const outDir = node_path_1.default.resolve(process.cwd(), outRel);
|
|
126
152
|
const manifestPath = node_path_1.default.join(outDir, exports.UPGRADE_MANIFEST_FILENAME);
|
|
127
|
-
// 幂等:同源应用已导出过则直接复用(前置钩子重试 /
|
|
153
|
+
// 幂等:同源应用已导出过则直接复用(前置钩子重试 / 多轮对话不重复拉取)。
|
|
154
|
+
// token 入参时按 manifest 记录的 sourceMetaToken 比对(导出前拿不到反查后的 appId)。
|
|
128
155
|
if (node_fs_1.default.existsSync(manifestPath)) {
|
|
129
156
|
try {
|
|
130
157
|
const prev = JSON.parse(node_fs_1.default.readFileSync(manifestPath, 'utf-8'));
|
|
131
|
-
|
|
158
|
+
const matched = source.appId
|
|
159
|
+
? prev.sourceAppId === source.appId
|
|
160
|
+
: prev.sourceMetaToken === source.metaToken;
|
|
161
|
+
if (matched) {
|
|
132
162
|
(0, logger_1.log)('export', `Already exported (manifest matched), skip: ${outRel}`);
|
|
133
|
-
(0, output_1.emit)({
|
|
163
|
+
(0, output_1.emit)({
|
|
164
|
+
data: {
|
|
165
|
+
appId: prev.sourceAppId ?? '',
|
|
166
|
+
out: outRel,
|
|
167
|
+
skipped: true,
|
|
168
|
+
reason: 'already_exported',
|
|
169
|
+
},
|
|
170
|
+
});
|
|
134
171
|
return;
|
|
135
172
|
}
|
|
136
173
|
}
|
|
@@ -138,10 +175,11 @@ async function handleAppExport(opts) {
|
|
|
138
175
|
// manifest 损坏视为未导出,走覆盖导出
|
|
139
176
|
}
|
|
140
177
|
}
|
|
141
|
-
(0, logger_1.log)('export', `Fetching code archive of ${appId}...`);
|
|
178
|
+
(0, logger_1.log)('export', `Fetching code archive of ${source.appId ?? 'meta token source'}...`);
|
|
142
179
|
const resp = await api.app.getCodeArchive({
|
|
143
180
|
appID: currentAppId,
|
|
144
|
-
sourceAppID: appId,
|
|
181
|
+
sourceAppID: source.appId,
|
|
182
|
+
sourceMetaToken: source.metaToken,
|
|
145
183
|
checkpointID: opts.checkpoint,
|
|
146
184
|
});
|
|
147
185
|
const fileBase64 = resp.file ?? '';
|
|
@@ -157,9 +195,11 @@ async function handleAppExport(opts) {
|
|
|
157
195
|
if (archive.length > MAX_ARCHIVE_BYTES) {
|
|
158
196
|
throw new error_1.AppError('EXPORT_ARCHIVE_TOO_LARGE', `归档超过大小上限(${String(archive.length)} > ${String(MAX_ARCHIVE_BYTES)} bytes)`);
|
|
159
197
|
}
|
|
198
|
+
// 实际导出的源应用 ID:服务端回传优先(token 入参时为反查结果),appId 入参时兜底自身
|
|
199
|
+
const resolvedSourceAppId = resp.sourceAppID ?? source.appId ?? '';
|
|
160
200
|
// 归档先落临时文件再解压;解压成功前不动已存在的 outDir 内容之外的文件
|
|
161
201
|
const tmpDir = node_fs_1.default.mkdtempSync(node_path_1.default.join(node_os_1.default.tmpdir(), 'miaoda-export-'));
|
|
162
|
-
const archivePath = node_path_1.default.join(tmpDir, resp.name ?? `${
|
|
202
|
+
const archivePath = node_path_1.default.join(tmpDir, resp.name ?? `${resolvedSourceAppId || 'source'}.zip`);
|
|
163
203
|
try {
|
|
164
204
|
node_fs_1.default.writeFileSync(archivePath, archive);
|
|
165
205
|
node_fs_1.default.mkdirSync(outDir, { recursive: true });
|
|
@@ -177,7 +217,9 @@ async function handleAppExport(opts) {
|
|
|
177
217
|
node_fs_1.default.rmSync(node_path_1.default.join(outDir, '.gitignore'), { force: true });
|
|
178
218
|
const fileCount = countFiles(outDir);
|
|
179
219
|
const manifest = {
|
|
180
|
-
sourceAppId:
|
|
220
|
+
sourceAppId: resolvedSourceAppId,
|
|
221
|
+
// token 入参时记录 token 供幂等比对;appId 入参时不写该字段
|
|
222
|
+
...(source.metaToken ? { sourceMetaToken: source.metaToken } : {}),
|
|
181
223
|
archiveName: resp.name ?? '',
|
|
182
224
|
checkpointId: opts.checkpoint ?? 0,
|
|
183
225
|
exportedAt: new Date().toISOString(),
|
|
@@ -186,6 +228,12 @@ async function handleAppExport(opts) {
|
|
|
186
228
|
node_fs_1.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
|
|
187
229
|
(0, logger_1.log)('export', 'Exported successfully');
|
|
188
230
|
(0, output_1.emit)({
|
|
189
|
-
data: {
|
|
231
|
+
data: {
|
|
232
|
+
appId: resolvedSourceAppId,
|
|
233
|
+
out: outRel,
|
|
234
|
+
fileCount,
|
|
235
|
+
archiveName: manifest.archiveName,
|
|
236
|
+
skipped: false,
|
|
237
|
+
},
|
|
190
238
|
});
|
|
191
239
|
}
|
|
@@ -13,7 +13,8 @@ const output_1 = require("../../../utils/output");
|
|
|
13
13
|
* miaoda registry add <name...> [--overwrite] [--dry-run] [--dir <path>] [--version <ver>]
|
|
14
14
|
*
|
|
15
15
|
* 解析 USAGE 块 Depends 传递闭包,把每个条目对应的 SDK 文件从 registry 包根的 <relPath>
|
|
16
|
-
*
|
|
16
|
+
* **保留相对路径**拷到落地目录(--dir,默认 cwd)。stack 一律从 <cwd>/.spark/meta.json 读,
|
|
17
|
+
* 不随 --dir 走。
|
|
17
18
|
* skip-by-disk:目标已存在默认跳过,--overwrite 才覆盖。不写任何安装记账文件。
|
|
18
19
|
*/
|
|
19
20
|
async function handleRegistryAdd(opts) {
|
|
@@ -22,7 +23,7 @@ async function handleRegistryAdd(opts) {
|
|
|
22
23
|
throw new error_1.AppError('ARGS_INVALID', 'registry add 至少需要一个条目名');
|
|
23
24
|
}
|
|
24
25
|
const targetDir = node_path_1.default.resolve(opts.dir ?? process.cwd());
|
|
25
|
-
const stack = (0, shared_1.resolveRegistryStack)(
|
|
26
|
+
const stack = (0, shared_1.resolveRegistryStack)();
|
|
26
27
|
const reg = (0, index_1.loadRegistry)({ stack, version: opts.version });
|
|
27
28
|
try {
|
|
28
29
|
const plan = (0, index_1.planAdd)({
|
|
@@ -1,17 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.handleRegistryList = handleRegistryList;
|
|
7
|
-
const node_path_1 = __importDefault(require("node:path"));
|
|
8
4
|
const index_1 = require("../../../services/registry/index");
|
|
9
5
|
const shared_1 = require("./shared");
|
|
10
6
|
const output_1 = require("../../../utils/output");
|
|
11
7
|
/**
|
|
12
|
-
* miaoda registry list [--
|
|
8
|
+
* miaoda registry list [--version <ver>]
|
|
13
9
|
*
|
|
14
|
-
* 从
|
|
10
|
+
* 从 <cwd>/.spark/meta.json 读 stack → runtime → 包名,拉对应 registry 包,从包根递归扫含 USAGE 块的 SDK 文件。
|
|
15
11
|
*
|
|
16
12
|
* - 默认(pretty):输出各条目 USAGE 块原文,带文件名分隔,给 Agent 直接读注释。
|
|
17
13
|
* - --json:输出 [{name, file, usage(原文字符串), dependsOn[]}],usage 保持原文不解析成字段。
|
|
@@ -19,8 +15,7 @@ const output_1 = require("../../../utils/output");
|
|
|
19
15
|
*/
|
|
20
16
|
async function handleRegistryList(opts) {
|
|
21
17
|
await Promise.resolve();
|
|
22
|
-
const
|
|
23
|
-
const stack = (0, shared_1.resolveRegistryStack)(targetDir);
|
|
18
|
+
const stack = (0, shared_1.resolveRegistryStack)();
|
|
24
19
|
const reg = (0, index_1.loadRegistry)({ stack, version: opts.version });
|
|
25
20
|
try {
|
|
26
21
|
if ((0, output_1.isJsonMode)()) {
|
|
@@ -5,16 +5,20 @@ const spark_meta_1 = require("../../../utils/spark-meta");
|
|
|
5
5
|
const index_1 = require("../../../services/registry/index");
|
|
6
6
|
const error_1 = require("../../../utils/error");
|
|
7
7
|
/**
|
|
8
|
-
* 从 <
|
|
8
|
+
* 从 <cwd>/.spark/meta.json 读 stack,校验该 stack 有组件 registry。
|
|
9
|
+
* meta 只与当前工作目录相关(CLI 必须在 user app 根目录下运行),不随 --dir 走——
|
|
10
|
+
* --dir 只影响 add 的 copy-in 落地目录。
|
|
9
11
|
* 缺 stack → 提示先 init;stack 无 registry(无对应 runtime / runtime 无 registry 包)→ 提示支持哪些 stack。
|
|
10
12
|
*
|
|
11
13
|
* 这里只做「stack 是否在支持列表」的早校验给用户友好提示;包名的两跳解析
|
|
12
14
|
* (stack → runtime → 包)由 loadRegistry/resolveRegistryPackage 负责。
|
|
13
15
|
*/
|
|
14
|
-
function resolveRegistryStack(
|
|
15
|
-
const meta = (0, spark_meta_1.readSparkMeta)(
|
|
16
|
+
function resolveRegistryStack() {
|
|
17
|
+
const meta = (0, spark_meta_1.readSparkMeta)(process.cwd());
|
|
16
18
|
if (meta.stack === undefined || meta.stack === '') {
|
|
17
|
-
throw new error_1.AppError('REGISTRY_META_INCOMPLETE', '.spark/meta.json missing stack — run `miaoda app init` first'
|
|
19
|
+
throw new error_1.AppError('REGISTRY_META_INCOMPLETE', '.spark/meta.json missing stack — run `miaoda app init` first', {
|
|
20
|
+
next_actions: ['meta 一律读当前工作目录(不随 --dir),请 cd 到 user app 根目录后重试'],
|
|
21
|
+
});
|
|
18
22
|
}
|
|
19
23
|
if (!index_1.REGISTRY_SUPPORTED_STACKS.includes(meta.stack)) {
|
|
20
24
|
throw new error_1.AppError('REGISTRY_STACK_UNSUPPORTED', `stack "${meta.stack}" 没有组件 registry`, {
|