@lark-apaas/miaoda-cli 0.1.3 → 0.1.4-alpha.abaa17f
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/dist/api/app/api.js +3 -3
- package/dist/api/app/schemas.js +43 -43
- package/dist/api/db/api.js +398 -55
- package/dist/api/db/client.js +155 -28
- package/dist/api/db/index.js +12 -1
- package/dist/api/db/parsers.js +20 -20
- package/dist/api/db/sql-keywords.js +87 -87
- package/dist/api/deploy/api.js +5 -5
- package/dist/api/deploy/index.js +12 -1
- package/dist/api/deploy/modern-types.js +23 -0
- package/dist/api/deploy/modern.js +70 -0
- package/dist/api/deploy/plugin-instances-types.js +6 -0
- package/dist/api/deploy/plugin-instances.js +30 -0
- package/dist/api/deploy/schemas.js +32 -32
- package/dist/api/file/api.js +89 -87
- package/dist/api/file/client.js +62 -22
- package/dist/api/file/detect.js +3 -3
- package/dist/api/file/index.js +2 -1
- package/dist/api/file/parsers.js +18 -7
- package/dist/api/observability/api.js +6 -6
- package/dist/api/observability/schemas.js +14 -14
- package/dist/api/plugin/api.js +31 -31
- package/dist/cli/commands/app/index.js +94 -13
- package/dist/cli/commands/db/index.js +602 -54
- package/dist/cli/commands/deploy/index.js +28 -28
- package/dist/cli/commands/deploy/modern.js +48 -0
- package/dist/cli/commands/file/index.js +85 -58
- package/dist/cli/commands/index.js +31 -5
- package/dist/cli/commands/observability/index.js +69 -69
- package/dist/cli/commands/plugin/index.js +27 -27
- package/dist/cli/commands/shared.js +10 -10
- package/dist/cli/handlers/app/index.js +6 -1
- package/dist/cli/handlers/app/init.js +86 -0
- package/dist/cli/handlers/app/update.js +2 -2
- package/dist/cli/handlers/db/_destructive.js +67 -0
- package/dist/cli/handlers/db/_env.js +26 -0
- package/dist/cli/handlers/db/_operator.js +35 -0
- package/dist/cli/handlers/db/audit.js +383 -0
- package/dist/cli/handlers/db/changelog.js +160 -0
- package/dist/cli/handlers/db/data.js +32 -31
- package/dist/cli/handlers/db/index.js +17 -1
- package/dist/cli/handlers/db/migration.js +234 -0
- package/dist/cli/handlers/db/quota.js +68 -0
- package/dist/cli/handlers/db/recovery.js +413 -0
- package/dist/cli/handlers/db/schema.js +33 -33
- package/dist/cli/handlers/db/sql.js +69 -69
- package/dist/cli/handlers/deploy/deploy.js +4 -4
- package/dist/cli/handlers/deploy/error-log.js +1 -1
- package/dist/cli/handlers/deploy/get.js +3 -3
- package/dist/cli/handlers/deploy/modern.js +32 -0
- package/dist/cli/handlers/deploy/polling.js +11 -11
- package/dist/cli/handlers/file/cp.js +30 -30
- package/dist/cli/handlers/file/index.js +3 -1
- package/dist/cli/handlers/file/ls.js +5 -5
- package/dist/cli/handlers/file/quota.js +66 -0
- package/dist/cli/handlers/file/rm.js +32 -30
- package/dist/cli/handlers/file/sign.js +3 -3
- package/dist/cli/handlers/file/stat.js +10 -9
- package/dist/cli/handlers/observability/analytics.js +47 -47
- package/dist/cli/handlers/observability/helpers.js +2 -2
- package/dist/cli/handlers/observability/log.js +9 -9
- package/dist/cli/handlers/observability/metric.js +26 -26
- package/dist/cli/handlers/observability/trace.js +5 -5
- package/dist/cli/handlers/plugin/plugin-local.js +53 -53
- package/dist/cli/handlers/plugin/plugin.js +15 -15
- package/dist/cli/help.js +16 -16
- package/dist/main.js +12 -12
- package/dist/services/app/init/index.js +14 -0
- package/dist/services/app/init/install.js +101 -0
- package/dist/services/app/init/steering.js +74 -0
- package/dist/services/app/init/template.js +85 -0
- package/dist/services/deploy/modern/atoms/build.js +54 -0
- package/dist/services/deploy/modern/atoms/context.js +30 -0
- package/dist/services/deploy/modern/atoms/index.js +17 -0
- package/dist/services/deploy/modern/atoms/local-release.js +27 -0
- package/dist/services/deploy/modern/atoms/pre-release.js +13 -0
- package/dist/services/deploy/modern/atoms/save-plugin-instances.js +72 -0
- package/dist/services/deploy/modern/atoms/upload.js +192 -0
- package/dist/services/deploy/modern/check.js +58 -0
- package/dist/services/deploy/modern/constants.js +13 -0
- package/dist/services/deploy/modern/index.js +16 -0
- package/dist/services/deploy/modern/pipelines/index.js +5 -0
- package/dist/services/deploy/modern/pipelines/local.js +75 -0
- package/dist/services/deploy/modern/protocol.js +43 -0
- package/dist/services/deploy/modern/run-types.js +4 -0
- package/dist/services/deploy/modern/run.js +13 -0
- package/dist/services/deploy/modern/template-key-map.js +29 -0
- package/dist/utils/args.js +1 -1
- package/dist/utils/colors.js +2 -2
- package/dist/utils/config.js +2 -2
- package/dist/utils/devops-error.js +9 -9
- package/dist/utils/error.js +2 -2
- package/dist/utils/git.js +4 -4
- package/dist/utils/http.js +27 -21
- package/dist/utils/index.js +3 -1
- package/dist/utils/npm-pack.js +55 -0
- package/dist/utils/output.js +67 -45
- package/dist/utils/poll.js +35 -0
- package/dist/utils/render.js +27 -27
- package/dist/utils/spark-meta.js +48 -0
- package/dist/utils/spinner.js +46 -0
- package/dist/utils/time.js +47 -42
- package/package.json +1 -1
|
@@ -16,12 +16,12 @@ const COMMON_TIME_HELP = `
|
|
|
16
16
|
`;
|
|
17
17
|
function registerObservabilityCommands(program) {
|
|
18
18
|
const obCmd = program
|
|
19
|
-
.command(
|
|
20
|
-
.description(
|
|
19
|
+
.command('observability')
|
|
20
|
+
.description('可观测性:查询线上日志、链路、监控指标、运营指标');
|
|
21
21
|
obCmd.action(() => {
|
|
22
22
|
obCmd.outputHelp();
|
|
23
23
|
});
|
|
24
|
-
obCmd.addHelpText(
|
|
24
|
+
obCmd.addHelpText('after', `
|
|
25
25
|
作用范围
|
|
26
26
|
仅供 Agent 在妙搭沙箱内调用,查询当前应用的线上观测数据。所有命令为只读。
|
|
27
27
|
应用上下文:--app-id <id> 或环境变量 MIAODA_APP_ID。
|
|
@@ -37,26 +37,26 @@ function registerObservabilityCommands(program) {
|
|
|
37
37
|
// ── log ──
|
|
38
38
|
function registerLog(parent) {
|
|
39
39
|
const cmd = parent
|
|
40
|
-
.command(
|
|
41
|
-
.description(
|
|
40
|
+
.command('log')
|
|
41
|
+
.description('查询线上运行日志(按时间倒序,分页)')
|
|
42
42
|
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
43
|
-
.addOption(new commander_1.Option(
|
|
44
|
-
.choices([
|
|
45
|
-
.argParser((0, shared_1.caseInsensitiveChoice)([
|
|
46
|
-
.option(
|
|
47
|
-
.option(
|
|
48
|
-
.option(
|
|
49
|
-
.option(
|
|
50
|
-
.option(
|
|
51
|
-
.option(
|
|
52
|
-
.option(
|
|
53
|
-
.option(
|
|
54
|
-
.option(
|
|
55
|
-
.option(
|
|
56
|
-
.option(
|
|
57
|
-
.option(
|
|
58
|
-
.option(
|
|
59
|
-
.addHelpText(
|
|
43
|
+
.addOption(new commander_1.Option('--level <level>', '日志级别(大小写不敏感)')
|
|
44
|
+
.choices(['DEBUG', 'INFO', 'WARN', 'ERROR'])
|
|
45
|
+
.argParser((0, shared_1.caseInsensitiveChoice)(['DEBUG', 'INFO', 'WARN', 'ERROR'])))
|
|
46
|
+
.option('--since <time>', '开始时间')
|
|
47
|
+
.option('--until <time>', '截止时间')
|
|
48
|
+
.option('--log-id <id>', '按请求 logid 过滤(attributes.ob_data_id)')
|
|
49
|
+
.option('--trace-id <id>', '按 trace ID 过滤')
|
|
50
|
+
.option('--grep <pattern>', '按关键字模糊搜索 body')
|
|
51
|
+
.option('--module <name>', '按模块名过滤')
|
|
52
|
+
.option('--user-id <user-id>', '按触发用户 ID 过滤')
|
|
53
|
+
.option('--page <path>', '按页面路径过滤')
|
|
54
|
+
.option('--api <path>', '按接口路径过滤')
|
|
55
|
+
.option('--min-duration <ms>', '最小耗时过滤(毫秒)')
|
|
56
|
+
.option('--max-duration <ms>', '最大耗时过滤(毫秒)')
|
|
57
|
+
.option('--limit <n>', '返回条数上限(1~100)', parseLimit, 50)
|
|
58
|
+
.option('--cursor <token>', '下一页游标,从上一次返回的 next_cursor 取值')
|
|
59
|
+
.addHelpText('after', `${COMMON_TIME_HELP}
|
|
60
60
|
JSON 输出
|
|
61
61
|
{"data": [...], "next_cursor": "...", "has_more": true|false}
|
|
62
62
|
|
|
@@ -67,8 +67,8 @@ JSON 输出
|
|
|
67
67
|
$ miaoda observability log --api /api/orders --min-duration 200 --json
|
|
68
68
|
`);
|
|
69
69
|
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
70
|
-
(0, shared_1.validateTimeOptions)(rawOpts,
|
|
71
|
-
(0, shared_1.rejectCliOverride)(cmd,
|
|
70
|
+
(0, shared_1.validateTimeOptions)(rawOpts, 'since', 'until');
|
|
71
|
+
(0, shared_1.rejectCliOverride)(cmd, 'appId');
|
|
72
72
|
await (0, index_1.handleObservabilityLog)({
|
|
73
73
|
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
74
74
|
level: rawOpts.level,
|
|
@@ -90,22 +90,22 @@ JSON 输出
|
|
|
90
90
|
}
|
|
91
91
|
// ── trace ──
|
|
92
92
|
function registerTrace(parent) {
|
|
93
|
-
const traceCmd = parent.command(
|
|
93
|
+
const traceCmd = parent.command('trace').description('链路追踪:列表 / 详情');
|
|
94
94
|
traceCmd.action(() => {
|
|
95
95
|
traceCmd.outputHelp();
|
|
96
96
|
});
|
|
97
97
|
const listCmd = traceCmd
|
|
98
|
-
.command(
|
|
99
|
-
.description(
|
|
98
|
+
.command('list')
|
|
99
|
+
.description('查询线上链路列表(按链路时间倒序,分页)')
|
|
100
100
|
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
101
|
-
.option(
|
|
102
|
-
.option(
|
|
103
|
-
.option(
|
|
104
|
-
.option(
|
|
105
|
-
.option(
|
|
106
|
-
.option(
|
|
107
|
-
.option(
|
|
108
|
-
.addHelpText(
|
|
101
|
+
.option('--since <time>', '开始时间')
|
|
102
|
+
.option('--until <time>', '截止时间')
|
|
103
|
+
.option('--trace-id <id>', '按 trace ID 过滤')
|
|
104
|
+
.option('--root-span <span-name>', '按入口节点关键词过滤')
|
|
105
|
+
.option('--user-id <user-id>', '按触发用户 ID 过滤')
|
|
106
|
+
.option('--limit <n>', '返回条数上限(1~100)', parseLimit, 50)
|
|
107
|
+
.option('--cursor <token>', '下一页游标')
|
|
108
|
+
.addHelpText('after', `${COMMON_TIME_HELP}
|
|
109
109
|
JSON 输出
|
|
110
110
|
{"data": [...], "next_cursor": "...", "has_more": true|false}
|
|
111
111
|
|
|
@@ -114,8 +114,8 @@ JSON 输出
|
|
|
114
114
|
$ miaoda observability trace list --root-span api-gateway --limit 20 --json
|
|
115
115
|
`);
|
|
116
116
|
listCmd.action((0, shared_1.withHelp)(listCmd, async (rawOpts) => {
|
|
117
|
-
(0, shared_1.validateTimeOptions)(rawOpts,
|
|
118
|
-
(0, shared_1.rejectCliOverride)(listCmd,
|
|
117
|
+
(0, shared_1.validateTimeOptions)(rawOpts, 'since', 'until');
|
|
118
|
+
(0, shared_1.rejectCliOverride)(listCmd, 'appId');
|
|
119
119
|
await (0, index_1.handleObservabilityTraceList)({
|
|
120
120
|
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
121
121
|
since: rawOpts.since,
|
|
@@ -128,12 +128,12 @@ JSON 输出
|
|
|
128
128
|
});
|
|
129
129
|
}));
|
|
130
130
|
const getCmd = traceCmd
|
|
131
|
-
.command(
|
|
132
|
-
.description(
|
|
133
|
-
.argument(
|
|
131
|
+
.command('get')
|
|
132
|
+
.description('查询指定链路的完整详情')
|
|
133
|
+
.argument('<trace-id>', '链路 ID')
|
|
134
134
|
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
135
|
-
.option(
|
|
136
|
-
.addHelpText(
|
|
135
|
+
.option('--with-log-severity-count', '返回每个 Span 各日志级别的数量', false)
|
|
136
|
+
.addHelpText('after', `
|
|
137
137
|
JSON 输出
|
|
138
138
|
{"data": {"spans": [...], "isBreak": false}}
|
|
139
139
|
|
|
@@ -146,7 +146,7 @@ JSON 输出
|
|
|
146
146
|
hint: 检查 trace-id 是否正确,或链路已超出保留时长
|
|
147
147
|
`);
|
|
148
148
|
getCmd.action((0, shared_1.withHelp)(getCmd, async (traceId, rawOpts) => {
|
|
149
|
-
(0, shared_1.rejectCliOverride)(getCmd,
|
|
149
|
+
(0, shared_1.rejectCliOverride)(getCmd, 'appId');
|
|
150
150
|
await (0, index_1.handleObservabilityTraceGet)({
|
|
151
151
|
traceId,
|
|
152
152
|
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
@@ -157,20 +157,20 @@ JSON 输出
|
|
|
157
157
|
// ── metric ──
|
|
158
158
|
function registerMetric(parent) {
|
|
159
159
|
const cmd = parent
|
|
160
|
-
.command(
|
|
161
|
-
.description(
|
|
162
|
-
.argument(
|
|
160
|
+
.command('metric')
|
|
161
|
+
.description('查询监控指标:requests / latency / cpu / memory')
|
|
162
|
+
.argument('<metric-name>', '指标名:requests | latency | cpu | memory')
|
|
163
163
|
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
164
|
-
.option(
|
|
165
|
-
.option(
|
|
166
|
-
.option(
|
|
167
|
-
.option(
|
|
168
|
-
.option(
|
|
169
|
-
.addOption(new commander_1.Option(
|
|
170
|
-
.choices([
|
|
171
|
-
.argParser((0, shared_1.caseInsensitiveChoice)([
|
|
172
|
-
.default(
|
|
173
|
-
.addHelpText(
|
|
164
|
+
.option('--page <path>', '按页面路径过滤(仅对 requests/latency 生效)')
|
|
165
|
+
.option('--api <path>', '按接口路径过滤(仅对 requests/latency 生效)')
|
|
166
|
+
.option('--series <name>', '过滤图表中的某条线:latency 取 p50/p99;requests 取 total/error;缺省返回所有相关线')
|
|
167
|
+
.option('--since <time>', '开始时间')
|
|
168
|
+
.option('--until <time>', '截止时间')
|
|
169
|
+
.addOption(new commander_1.Option('--down-sample <duration>', '降采样粒度(1m=1分钟 / 1h=1小时 / 1d=1天,大小写不敏感)')
|
|
170
|
+
.choices(['1m', '1h', '1d'])
|
|
171
|
+
.argParser((0, shared_1.caseInsensitiveChoice)(['1m', '1h', '1d']))
|
|
172
|
+
.default('1m'))
|
|
173
|
+
.addHelpText('after', `${COMMON_TIME_HELP}
|
|
174
174
|
JSON 输出
|
|
175
175
|
{"data": [{"metricName": "...", "dimensions": {...}, "dataPoints": [...]}], "next_cursor": null, "has_more": false}
|
|
176
176
|
|
|
@@ -181,8 +181,8 @@ JSON 输出
|
|
|
181
181
|
$ miaoda observability metric cpu --since 1d --down-sample 1d --json
|
|
182
182
|
`);
|
|
183
183
|
cmd.action((0, shared_1.withHelp)(cmd, async (metricName, rawOpts) => {
|
|
184
|
-
(0, shared_1.validateTimeOptions)(rawOpts,
|
|
185
|
-
(0, shared_1.rejectCliOverride)(cmd,
|
|
184
|
+
(0, shared_1.validateTimeOptions)(rawOpts, 'since', 'until');
|
|
185
|
+
(0, shared_1.rejectCliOverride)(cmd, 'appId');
|
|
186
186
|
await (0, index_1.handleObservabilityMetric)({
|
|
187
187
|
metricName,
|
|
188
188
|
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
@@ -198,16 +198,16 @@ JSON 输出
|
|
|
198
198
|
// ── analytics ──
|
|
199
199
|
function registerAnalytics(parent) {
|
|
200
200
|
const cmd = parent
|
|
201
|
-
.command(
|
|
202
|
-
.description(
|
|
203
|
-
.argument(
|
|
201
|
+
.command('analytics')
|
|
202
|
+
.description('查询运营指标:users(用户数)/ page-view(页面访问)')
|
|
203
|
+
.argument('<analytics-name>', '指标名:users | page-view')
|
|
204
204
|
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
205
|
-
.option(
|
|
206
|
-
.option(
|
|
207
|
-
.option(
|
|
208
|
-
.option(
|
|
209
|
-
.option(
|
|
210
|
-
.addHelpText(
|
|
205
|
+
.option('--page <path>', '按页面路径过滤(仅对 page-view 生效)')
|
|
206
|
+
.option('--series <name>', '过滤图表中的某条线:users 取 active/new/total(兼容 active-users/new-users/total-users);page-view 取 all/desktop/mobile(兼容 all-view/desktop-view/mobile-view)')
|
|
207
|
+
.option('--since <time>', '开始时间')
|
|
208
|
+
.option('--until <time>', '截止时间')
|
|
209
|
+
.option('--granularity <duration>', '时间粒度:day | week | month', 'day')
|
|
210
|
+
.addHelpText('after', `${COMMON_TIME_HELP}
|
|
211
211
|
JSON 输出
|
|
212
212
|
{"data": [{"metricType": "...", "points": [{"timestampNs": "...", "value": ..., "dimensions": {...}}]}], "next_cursor": null, "has_more": false}
|
|
213
213
|
|
|
@@ -217,8 +217,8 @@ JSON 输出
|
|
|
217
217
|
$ miaoda observability analytics page-view --granularity week --json
|
|
218
218
|
`);
|
|
219
219
|
cmd.action((0, shared_1.withHelp)(cmd, async (analyticsName, rawOpts) => {
|
|
220
|
-
(0, shared_1.validateTimeOptions)(rawOpts,
|
|
221
|
-
(0, shared_1.rejectCliOverride)(cmd,
|
|
220
|
+
(0, shared_1.validateTimeOptions)(rawOpts, 'since', 'until');
|
|
221
|
+
(0, shared_1.rejectCliOverride)(cmd, 'appId');
|
|
222
222
|
await (0, index_1.handleObservabilityAnalytics)({
|
|
223
223
|
analyticsName,
|
|
224
224
|
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
@@ -4,13 +4,13 @@ exports.registerPluginCommands = registerPluginCommands;
|
|
|
4
4
|
const index_1 = require("../../../cli/handlers/plugin/index");
|
|
5
5
|
function registerPluginCommands(program) {
|
|
6
6
|
const pluginCmd = program
|
|
7
|
-
.command(
|
|
8
|
-
.description(
|
|
9
|
-
.usage(
|
|
7
|
+
.command('plugin')
|
|
8
|
+
.description('插件管理:安装/更新/移除插件包,查询 capability 实例')
|
|
9
|
+
.usage('<command> [flags]');
|
|
10
10
|
pluginCmd.action(() => {
|
|
11
11
|
pluginCmd.outputHelp();
|
|
12
12
|
});
|
|
13
|
-
pluginCmd.addHelpText(
|
|
13
|
+
pluginCmd.addHelpText('after', `
|
|
14
14
|
概念
|
|
15
15
|
- 插件包 (plugin package):.tgz 包,释放到 ./node_modules/<name>
|
|
16
16
|
- actionPlugins:./package.json 里的字段,记录 { "@scope/name": "version" },作为
|
|
@@ -26,10 +26,10 @@ function registerPluginCommands(program) {
|
|
|
26
26
|
- remove / list / list-packages:纯本地 FS,不走网络
|
|
27
27
|
`);
|
|
28
28
|
pluginCmd
|
|
29
|
-
.command(
|
|
30
|
-
.description(
|
|
31
|
-
.argument(
|
|
32
|
-
.addHelpText(
|
|
29
|
+
.command('install')
|
|
30
|
+
.description('拉取插件包并安装到当前应用项目')
|
|
31
|
+
.argument('<names...>', '插件名,格式 @scope/name 或 @scope/name@version;未带版本默认 latest')
|
|
32
|
+
.addHelpText('after', `
|
|
33
33
|
行为
|
|
34
34
|
- 拉取 .tgz 到本地缓存,解压到 ./node_modules/<name>
|
|
35
35
|
- 在 ./package.json 的 actionPlugins 字段记录 <name>: <version>
|
|
@@ -62,10 +62,10 @@ JSON 输出
|
|
|
62
62
|
await (0, index_1.handlePluginInstall)({ names });
|
|
63
63
|
});
|
|
64
64
|
pluginCmd
|
|
65
|
-
.command(
|
|
66
|
-
.description(
|
|
67
|
-
.argument(
|
|
68
|
-
.addHelpText(
|
|
65
|
+
.command('update')
|
|
66
|
+
.description('把已安装插件升级到 latest 版本')
|
|
67
|
+
.argument('<names...>', '插件名,格式 @scope/name;带 @version 也只取 name 部分')
|
|
68
|
+
.addHelpText('after', `
|
|
69
69
|
行为
|
|
70
70
|
- 仅对已在 actionPlugins 里的插件生效;未安装的插件返回 notInstalled
|
|
71
71
|
- 拉取 latest 版本 .tgz,覆盖安装到 ./node_modules/<name>
|
|
@@ -97,10 +97,10 @@ JSON 输出
|
|
|
97
97
|
await (0, index_1.handlePluginUpdate)({ names });
|
|
98
98
|
});
|
|
99
99
|
pluginCmd
|
|
100
|
-
.command(
|
|
101
|
-
.description(
|
|
102
|
-
.argument(
|
|
103
|
-
.addHelpText(
|
|
100
|
+
.command('remove')
|
|
101
|
+
.description('从当前项目移除一个已安装的插件')
|
|
102
|
+
.argument('<name>', '插件名,格式 @scope/name')
|
|
103
|
+
.addHelpText('after', `
|
|
104
104
|
行为
|
|
105
105
|
- 删除 ./node_modules/<name> 目录
|
|
106
106
|
- 从 ./package.json 的 actionPlugins 字段移除对应条目
|
|
@@ -123,9 +123,9 @@ JSON 输出
|
|
|
123
123
|
(0, index_1.handlePluginRemove)({ name });
|
|
124
124
|
});
|
|
125
125
|
pluginCmd
|
|
126
|
-
.command(
|
|
127
|
-
.description(
|
|
128
|
-
.addHelpText(
|
|
126
|
+
.command('init')
|
|
127
|
+
.description('按 package.json 的 actionPlugins 批量安装所有插件')
|
|
128
|
+
.addHelpText('after', `
|
|
129
129
|
行为
|
|
130
130
|
- 读取 ./package.json 的 actionPlugins,对每个条目按固定版本安装
|
|
131
131
|
- 已经是目标版本的跳过(幂等),用于 clone 项目后首次同步 / CI
|
|
@@ -156,11 +156,11 @@ JSON 输出
|
|
|
156
156
|
await (0, index_1.handlePluginInit)();
|
|
157
157
|
});
|
|
158
158
|
pluginCmd
|
|
159
|
-
.command(
|
|
160
|
-
.description(
|
|
161
|
-
.option(
|
|
162
|
-
.option(
|
|
163
|
-
.addHelpText(
|
|
159
|
+
.command('list')
|
|
160
|
+
.description('列出当前项目的 capability 实例(./server/capabilities/*.json)')
|
|
161
|
+
.option('--summary', '不 hydrate(不充血),直接返回 capability 文件的原始 JSON')
|
|
162
|
+
.option('--id <id>', '只查询指定 id 的单个 capability')
|
|
163
|
+
.addHelpText('after', `
|
|
164
164
|
行为
|
|
165
165
|
- 扫描 ./server/capabilities/*.json(排除 capabilities.json)
|
|
166
166
|
- 默认对每条 capability 做 hydrate:读取 ./node_modules/<pluginKey>/manifest.json,
|
|
@@ -190,9 +190,9 @@ JSON 输出
|
|
|
190
190
|
await (0, index_1.handlePluginList)(opts);
|
|
191
191
|
});
|
|
192
192
|
pluginCmd
|
|
193
|
-
.command(
|
|
194
|
-
.description(
|
|
195
|
-
.addHelpText(
|
|
193
|
+
.command('list-packages')
|
|
194
|
+
.description('列出 package.json actionPlugins 里已声明的插件包')
|
|
195
|
+
.addHelpText('after', `
|
|
196
196
|
行为
|
|
197
197
|
- 读取 ./package.json 的 actionPlugins 字段
|
|
198
198
|
- 只看声明,不校验 ./node_modules 下是否实际存在
|
|
@@ -16,10 +16,10 @@ const args_1 = require("../../utils/args");
|
|
|
16
16
|
Object.defineProperty(exports, "failArgs", { enumerable: true, get: function () { return args_1.failArgs; } });
|
|
17
17
|
/** --app-id option,需要应用上下文的命令自行 .addOption(appIdOption()) */
|
|
18
18
|
function appIdOption() {
|
|
19
|
-
return new commander_1.Option(
|
|
19
|
+
return new commander_1.Option('--app-id <id>', '指定目标应用').env('MIAODA_APP_ID');
|
|
20
20
|
}
|
|
21
21
|
function branchOption() {
|
|
22
|
-
return new commander_1.Option(
|
|
22
|
+
return new commander_1.Option('--branch <branch>', '分支(优先级:--branch > 当前仓库 HEAD 分支;非 git 目录回退应用默认分支)').env('LOCAL_MOCK_MIAODA_DEPLOY_BRANCH');
|
|
23
23
|
}
|
|
24
24
|
/**
|
|
25
25
|
* soft-required: Commander 类型上 optional,runtime 校验必填。
|
|
@@ -36,8 +36,8 @@ function softRequiredOption(name, desc) {
|
|
|
36
36
|
function resolveAppId(opts) {
|
|
37
37
|
const id = opts.appId ?? process.env.MIAODA_APP_ID ?? process.env.app_id;
|
|
38
38
|
if (!id) {
|
|
39
|
-
throw new error_1.AppError(
|
|
40
|
-
next_actions: [
|
|
39
|
+
throw new error_1.AppError('APP_ID_MISSING', '未指定应用', {
|
|
40
|
+
next_actions: ['设置 export MIAODA_APP_ID=<id>'],
|
|
41
41
|
});
|
|
42
42
|
}
|
|
43
43
|
return id;
|
|
@@ -53,7 +53,7 @@ function withHelp(cmd, handler) {
|
|
|
53
53
|
await handler(...args);
|
|
54
54
|
}
|
|
55
55
|
catch (err) {
|
|
56
|
-
if (err instanceof error_1.AppError && err.code ===
|
|
56
|
+
if (err instanceof error_1.AppError && err.code === 'ARGS_INVALID') {
|
|
57
57
|
process.stderr.write(`Error: ${err.message}\n`);
|
|
58
58
|
cmd.outputHelp();
|
|
59
59
|
process.exitCode = 2;
|
|
@@ -81,7 +81,7 @@ function caseInsensitiveChoice(canonical) {
|
|
|
81
81
|
return (raw) => {
|
|
82
82
|
const hit = map.get(raw.toLowerCase());
|
|
83
83
|
if (hit === undefined) {
|
|
84
|
-
throw new commander_1.InvalidArgumentError(`Allowed choices are ${canonical.join(
|
|
84
|
+
throw new commander_1.InvalidArgumentError(`Allowed choices are ${canonical.join(', ')} (case-insensitive).`);
|
|
85
85
|
}
|
|
86
86
|
return hit;
|
|
87
87
|
};
|
|
@@ -98,8 +98,8 @@ function validateTimeOptions(opts, ...names) {
|
|
|
98
98
|
const value = opts[name];
|
|
99
99
|
if (value === undefined)
|
|
100
100
|
continue;
|
|
101
|
-
if (typeof value !==
|
|
102
|
-
(0, args_1.failArgs)(`--${name.replace(/([A-Z])/g,
|
|
101
|
+
if (typeof value !== 'string') {
|
|
102
|
+
(0, args_1.failArgs)(`--${name.replace(/([A-Z])/g, '-$1').toLowerCase()} 必须是时间字符串`);
|
|
103
103
|
}
|
|
104
104
|
(0, time_1.parseTimeToMs)(value);
|
|
105
105
|
}
|
|
@@ -118,8 +118,8 @@ function validateTimeOptions(opts, ...names) {
|
|
|
118
118
|
*/
|
|
119
119
|
function rejectCliOverride(cmd, ...optNames) {
|
|
120
120
|
for (const name of optNames) {
|
|
121
|
-
if (cmd.getOptionValueSource(name) ===
|
|
122
|
-
const flag =
|
|
121
|
+
if (cmd.getOptionValueSource(name) === 'cli') {
|
|
122
|
+
const flag = '--' + name.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
123
123
|
(0, args_1.failArgs)(`${flag} 由沙箱/本地配置注入,不允许显式传递`);
|
|
124
124
|
}
|
|
125
125
|
}
|
|
@@ -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
|
+
}
|
|
@@ -41,7 +41,7 @@ const index_1 = require("../../../api/app/index");
|
|
|
41
41
|
/** miaoda app update [--app-id <id>] [--name <n>] [--description <d>] */
|
|
42
42
|
async function handleAppUpdate(opts) {
|
|
43
43
|
if (opts.name === undefined && opts.description === undefined) {
|
|
44
|
-
(0, args_1.failArgs)(
|
|
44
|
+
(0, args_1.failArgs)('至少需要 --name 或 --description 中的一个');
|
|
45
45
|
}
|
|
46
46
|
const appID = opts.appId;
|
|
47
47
|
await api.app.updateAppMeta({
|
|
@@ -53,7 +53,7 @@ async function handleAppUpdate(opts) {
|
|
|
53
53
|
const resp = await api.app.getAppInfo(appID);
|
|
54
54
|
const meta = resp.appInfo?.appMeta ?? {};
|
|
55
55
|
if (!(0, output_1.isJsonMode)()) {
|
|
56
|
-
process.stdout.write(
|
|
56
|
+
process.stdout.write('✓ App updated successfully\n');
|
|
57
57
|
}
|
|
58
58
|
(0, output_1.emit)({ data: meta }, index_1.appMetaSchema);
|
|
59
59
|
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// db 域内不可逆 / destructive 操作的统一确认门。
|
|
3
|
+
//
|
|
4
|
+
// 设计要点:
|
|
5
|
+
// 1. --yes:放行(CI / 脚本显式确认场景)
|
|
6
|
+
// 2. 非 TTY 且无 --yes:抛 DESTRUCTIVE_REQUIRES_CONFIRM,不要默认放行。
|
|
7
|
+
// 非 TTY 通常是 CI / agent / 管道场景,无人能交互回答 y/N,必须靠 --yes 显式确认。
|
|
8
|
+
// 3. TTY 且无 --yes:交互式 y/N(prompt 写 stderr,避免污染 --json 模式 stdout)
|
|
9
|
+
//
|
|
10
|
+
// 反例(已修复,见这次 commit):把 !isJsonMode() 拿来做确认门 —— --json 只改输出
|
|
11
|
+
// 格式,跟"用户已确认"不是同一语义。CI / agent 几乎总会带 --json 解析输出,等价于
|
|
12
|
+
// 把不可逆操作的确认变成了空门,跟 file rm 的模型也不一致。
|
|
13
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
14
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
15
|
+
};
|
|
16
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
+
exports.confirmDestructive = confirmDestructive;
|
|
18
|
+
exports.assertDestructiveAllowedInTty = assertDestructiveAllowedInTty;
|
|
19
|
+
exports.askYesNo = askYesNo;
|
|
20
|
+
const node_readline_1 = __importDefault(require("node:readline"));
|
|
21
|
+
const render_1 = require("../../../utils/render");
|
|
22
|
+
const error_1 = require("../../../utils/error");
|
|
23
|
+
const DESTRUCTIVE_REQUIRES_CONFIRM_MSG = 'This operation is destructive. Rerun with --yes to confirm.';
|
|
24
|
+
/**
|
|
25
|
+
* 完整的"yes/tty/交互"决策门。直接用于无需 fetch 预览的简单确认场景(如 migration init)。
|
|
26
|
+
*
|
|
27
|
+
* 返回值:
|
|
28
|
+
* - true → 已确认,可执行
|
|
29
|
+
* - false → TTY 下用户输了 n,调用方应渲染 Aborted 并退出
|
|
30
|
+
*
|
|
31
|
+
* 异常:
|
|
32
|
+
* - DESTRUCTIVE_REQUIRES_CONFIRM:非 TTY 且无 --yes
|
|
33
|
+
*/
|
|
34
|
+
async function confirmDestructive(prompt, yes) {
|
|
35
|
+
if (yes === true)
|
|
36
|
+
return true;
|
|
37
|
+
if (!(0, render_1.isStdoutTty)()) {
|
|
38
|
+
throw new error_1.AppError('DESTRUCTIVE_REQUIRES_CONFIRM', DESTRUCTIVE_REQUIRES_CONFIRM_MSG);
|
|
39
|
+
}
|
|
40
|
+
return askYesNo(prompt);
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 给"需要在 confirm 之前先 fetch 预览"的场景(migration apply / recovery apply 可选优化)用:
|
|
44
|
+
* 在非 TTY 无 --yes 时提前抛错,避免无意义触发后端 dry-run RPC。
|
|
45
|
+
*
|
|
46
|
+
* 调用后 TTY 路径仍需自行调 askYesNo / confirmDestructive 完成交互。
|
|
47
|
+
*/
|
|
48
|
+
function assertDestructiveAllowedInTty(yes) {
|
|
49
|
+
if (yes === true)
|
|
50
|
+
return;
|
|
51
|
+
if (!(0, render_1.isStdoutTty)()) {
|
|
52
|
+
throw new error_1.AppError('DESTRUCTIVE_REQUIRES_CONFIRM', DESTRUCTIVE_REQUIRES_CONFIRM_MSG);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* 交互式 y/N。输入读 stdin,prompt 写 stderr —— stderr 避免污染 --json 模式 stdout 的 envelope。
|
|
57
|
+
* 仅 'y'(忽略大小写)算确认;其余一律视为否决。
|
|
58
|
+
*/
|
|
59
|
+
async function askYesNo(prompt) {
|
|
60
|
+
const rl = node_readline_1.default.createInterface({ input: process.stdin, output: process.stderr });
|
|
61
|
+
return new Promise((resolve) => {
|
|
62
|
+
rl.question(prompt, (answer) => {
|
|
63
|
+
rl.close();
|
|
64
|
+
resolve(answer.trim().toLowerCase() === 'y');
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// db 域内 --env 参数的 CLI vocab 校验。
|
|
3
|
+
//
|
|
4
|
+
// 用户视角的 vocab:
|
|
5
|
+
// - dev → 多环境沙箱分支
|
|
6
|
+
// - online → 生产分支(向后端发请求时映射成 main,client.ts/api.ts 已处理)
|
|
7
|
+
// - main → online 的别名,兼容历史脚本
|
|
8
|
+
//
|
|
9
|
+
// 配合 client.ts::buildUnknownEnvError,把 vocab 错(这里)跟 backend state 错
|
|
10
|
+
// (client.ts 处理)统一成同一类 UNKNOWN_ENV_VALUE,避免用户在两种失败之间困惑。
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.validateEnv = validateEnv;
|
|
13
|
+
const client_1 = require("../../../api/db/client");
|
|
14
|
+
const VOCAB = ['dev', 'main', 'online'];
|
|
15
|
+
const VOCAB_SET = new Set(VOCAB);
|
|
16
|
+
/**
|
|
17
|
+
* CLI vocab 校验。空值(未传 --env)放行,由后端按 workspace 默认 branch 处理。
|
|
18
|
+
* 不在 vocab 内 → 抛 UNKNOWN_ENV_VALUE,preAction hook 兜底,避免无意义触发 RPC。
|
|
19
|
+
*/
|
|
20
|
+
function validateEnv(env) {
|
|
21
|
+
if (env === undefined || env === '')
|
|
22
|
+
return;
|
|
23
|
+
if (VOCAB_SET.has(env))
|
|
24
|
+
return;
|
|
25
|
+
throw (0, client_1.buildUnknownEnvError)(env);
|
|
26
|
+
}
|