@lark-apaas/miaoda-cli 0.1.2 → 0.1.3-alpha.4bf312e
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 +8 -7
- package/dist/api/app/api.js +25 -0
- package/dist/api/app/index.js +15 -0
- package/dist/api/app/schemas.js +79 -0
- package/dist/api/app/types.js +58 -0
- package/dist/api/db/api.js +317 -6
- package/dist/api/db/client.js +36 -0
- package/dist/api/db/index.js +11 -1
- package/dist/api/deploy/api.js +60 -0
- package/dist/api/deploy/index.js +16 -0
- package/dist/api/deploy/schemas.js +105 -0
- package/dist/api/deploy/types.js +22 -0
- package/dist/api/file/api.js +15 -0
- package/dist/api/file/index.js +2 -1
- package/dist/api/index.js +7 -1
- package/dist/api/observability/api.js +52 -0
- package/dist/api/observability/index.js +16 -0
- package/dist/api/observability/schemas.js +60 -0
- package/dist/api/observability/types.js +27 -0
- package/dist/cli/commands/app/index.js +62 -0
- package/dist/cli/commands/db/index.js +440 -5
- package/dist/cli/commands/deploy/index.js +155 -0
- package/dist/cli/commands/file/index.js +13 -5
- package/dist/cli/commands/index.js +10 -6
- package/dist/cli/commands/observability/index.js +240 -0
- package/dist/cli/commands/shared.js +83 -7
- package/dist/cli/handlers/app/get.js +47 -0
- package/dist/cli/handlers/app/index.js +7 -0
- package/dist/cli/handlers/app/update.js +59 -0
- package/dist/cli/handlers/db/audit.js +285 -0
- package/dist/cli/handlers/db/changelog.js +117 -0
- package/dist/cli/handlers/db/data.js +3 -4
- package/dist/cli/handlers/db/index.js +17 -1
- package/dist/cli/handlers/db/migration.js +235 -0
- package/dist/cli/handlers/db/quota.js +68 -0
- package/dist/cli/handlers/db/recovery.js +328 -0
- package/dist/cli/handlers/db/schema.js +2 -3
- package/dist/cli/handlers/db/sql.js +1 -2
- package/dist/cli/handlers/deploy/deploy.js +84 -0
- package/dist/cli/handlers/deploy/error-log.js +60 -0
- package/dist/cli/handlers/deploy/format.js +39 -0
- package/dist/cli/handlers/deploy/get.js +71 -0
- package/dist/cli/handlers/deploy/helpers.js +41 -0
- package/dist/cli/handlers/deploy/history.js +70 -0
- package/dist/cli/handlers/deploy/index.js +14 -0
- package/dist/cli/handlers/deploy/polling.js +162 -0
- package/dist/cli/handlers/file/cp.js +1 -2
- package/dist/cli/handlers/file/index.js +3 -1
- package/dist/cli/handlers/file/ls.js +1 -2
- package/dist/cli/handlers/file/quota.js +66 -0
- package/dist/cli/handlers/file/rm.js +1 -2
- package/dist/cli/handlers/file/sign.js +1 -2
- package/dist/cli/handlers/file/stat.js +1 -2
- package/dist/cli/handlers/observability/analytics.js +212 -0
- package/dist/cli/handlers/observability/helpers.js +66 -0
- package/dist/cli/handlers/observability/index.js +12 -0
- package/dist/cli/handlers/observability/log.js +94 -0
- package/dist/cli/handlers/observability/metric.js +208 -0
- package/dist/cli/handlers/observability/trace.js +102 -0
- package/dist/main.js +6 -2
- package/dist/utils/args.js +8 -0
- package/dist/utils/devops-error.js +28 -0
- package/dist/utils/git.js +29 -0
- package/dist/utils/http.js +118 -0
- package/dist/utils/index.js +15 -1
- package/dist/utils/output.js +360 -7
- package/dist/utils/poll.js +27 -0
- package/dist/utils/time.js +203 -0
- package/package.json +7 -5
|
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.registerFileCommands = registerFileCommands;
|
|
4
4
|
const index_1 = require("../../../cli/handlers/file/index");
|
|
5
5
|
const error_1 = require("../../../utils/error");
|
|
6
|
+
const shared_1 = require("../../../cli/commands/shared");
|
|
6
7
|
/**
|
|
7
8
|
* commander option 校验器:把 --limit <n> 解析成正整数(≥1)。
|
|
8
9
|
* 默认值(如 "50")会先经过这里被规范化成 number。
|
|
@@ -43,7 +44,7 @@ function registerFileCommands(program) {
|
|
|
43
44
|
.option("--cursor <token>", "分页游标,从上次响应的 next_cursor 取值")
|
|
44
45
|
.option("--all", "自动翻页返回全部结果")
|
|
45
46
|
.action(async (query, opts) => {
|
|
46
|
-
await (0, index_1.handleFileLs)({ ...opts, query });
|
|
47
|
+
await (0, index_1.handleFileLs)({ ...opts, appId: (0, shared_1.resolveAppId)({}), query });
|
|
47
48
|
})
|
|
48
49
|
.addHelpText("after", `
|
|
49
50
|
Examples:
|
|
@@ -68,7 +69,7 @@ Examples:
|
|
|
68
69
|
.usage("<file> [flags]")
|
|
69
70
|
.argument("<file>", "文件的路径或文件名(自动识别)")
|
|
70
71
|
.action(async (file, opts) => {
|
|
71
|
-
await (0, index_1.handleFileStat)(file, opts);
|
|
72
|
+
await (0, index_1.handleFileStat)(file, { ...opts, appId: (0, shared_1.resolveAppId)({}) });
|
|
72
73
|
})
|
|
73
74
|
.addHelpText("after", `
|
|
74
75
|
Notes:
|
|
@@ -101,7 +102,7 @@ Examples:
|
|
|
101
102
|
.argument("<dst>", "目标:本地路径或远程路径")
|
|
102
103
|
.option("--rename <name>", "上传后在远端使用的新文件名")
|
|
103
104
|
.action(async (src, dst, opts) => {
|
|
104
|
-
await (0, index_1.handleFileCp)(src, dst, opts);
|
|
105
|
+
await (0, index_1.handleFileCp)(src, dst, { ...opts, appId: (0, shared_1.resolveAppId)({}) });
|
|
105
106
|
})
|
|
106
107
|
.addHelpText("after", `
|
|
107
108
|
Notes:
|
|
@@ -147,7 +148,7 @@ Examples:
|
|
|
147
148
|
.option("-n, --name <name>", "按文件名删除(可重复指定)", (value, prev) => [...(prev ?? []), value])
|
|
148
149
|
.option("-y, --yes", "跳过交互确认;非交互场景必加")
|
|
149
150
|
.action(async (paths, opts) => {
|
|
150
|
-
await (0, index_1.handleFileRm)(paths, opts);
|
|
151
|
+
await (0, index_1.handleFileRm)(paths, { ...opts, appId: (0, shared_1.resolveAppId)({}) });
|
|
151
152
|
})
|
|
152
153
|
.addHelpText("after", `
|
|
153
154
|
Notes:
|
|
@@ -190,7 +191,7 @@ Examples:
|
|
|
190
191
|
.argument("<file>", "文件的路径或文件名")
|
|
191
192
|
.option("--expires <duration>", "链接有效期,支持 30m / 24h / 7d 等单位(默认 1d,最长 30d)")
|
|
192
193
|
.action(async (file, opts) => {
|
|
193
|
-
await (0, index_1.handleFileSign)(file, opts);
|
|
194
|
+
await (0, index_1.handleFileSign)(file, { ...opts, appId: (0, shared_1.resolveAppId)({}) });
|
|
194
195
|
})
|
|
195
196
|
.addHelpText("after", `
|
|
196
197
|
Notes:
|
|
@@ -209,4 +210,11 @@ Examples:
|
|
|
209
210
|
Error: Expires duration '60d' exceeds the maximum of 30d
|
|
210
211
|
hint: Maximum allowed value is 30d. Use \`--expires 30d\` for the longest link.
|
|
211
212
|
`);
|
|
213
|
+
fileCmd
|
|
214
|
+
.command("quota")
|
|
215
|
+
.summary("查看文件存储用量与限额")
|
|
216
|
+
.usage("[flags]")
|
|
217
|
+
.action(async function () {
|
|
218
|
+
await (0, index_1.handleFileQuota)(this.optsWithGlobals());
|
|
219
|
+
});
|
|
212
220
|
}
|
|
@@ -1,11 +1,15 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.registerCommands = registerCommands;
|
|
4
|
-
const index_1 = require("../../cli/commands/
|
|
5
|
-
const index_2 = require("../../cli/commands/
|
|
6
|
-
const index_3 = require("../../cli/commands/
|
|
4
|
+
const index_1 = require("../../cli/commands/file/index");
|
|
5
|
+
const index_2 = require("../../cli/commands/db/index");
|
|
6
|
+
const index_3 = require("../../cli/commands/observability/index");
|
|
7
|
+
const index_4 = require("../../cli/commands/app/index");
|
|
8
|
+
const index_5 = require("../../cli/commands/deploy/index");
|
|
7
9
|
function registerCommands(program) {
|
|
8
|
-
(0, index_1.
|
|
9
|
-
(0, index_2.
|
|
10
|
-
(0, index_3.
|
|
10
|
+
(0, index_1.registerFileCommands)(program);
|
|
11
|
+
(0, index_2.registerDbCommands)(program);
|
|
12
|
+
(0, index_3.registerObservabilityCommands)(program);
|
|
13
|
+
(0, index_4.registerAppCommands)(program);
|
|
14
|
+
(0, index_5.registerDeployCommands)(program);
|
|
11
15
|
}
|
|
@@ -0,0 +1,240 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerObservabilityCommands = registerObservabilityCommands;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const shared_1 = require("../../../cli/commands/shared");
|
|
6
|
+
const index_1 = require("../../../cli/handlers/observability/index");
|
|
7
|
+
const COMMON_TIME_HELP = `
|
|
8
|
+
时间格式:
|
|
9
|
+
- 相对时间:30m(30 分钟前)、1h、2d、1w
|
|
10
|
+
- 日期:2026-04-01(本地时区当日 00:00:00)
|
|
11
|
+
- 本地日期+时间:2026-04-01T10:00:00(按本地时区,T 分隔)
|
|
12
|
+
- 带时区 ISO:2026-04-01T10:00:00Z(UTC)或 2026-04-01T10:00:00+08:00(指定偏移)
|
|
13
|
+
备注:
|
|
14
|
+
1) 不带时区的形式与 pretty 输出闭环(同机器复制粘贴稳定);跨机器请带显式时区。
|
|
15
|
+
2) 必须用 T 分隔日期与时间,禁止空格——shell 会把不带引号的 'YYYY-MM-DD HH:mm:ss' 拆成两个参数。
|
|
16
|
+
`;
|
|
17
|
+
function registerObservabilityCommands(program) {
|
|
18
|
+
const obCmd = program
|
|
19
|
+
.command("observability")
|
|
20
|
+
.description("可观测性:查询线上日志、链路、监控指标、运营指标");
|
|
21
|
+
obCmd.action(() => {
|
|
22
|
+
obCmd.outputHelp();
|
|
23
|
+
});
|
|
24
|
+
obCmd.addHelpText("after", `
|
|
25
|
+
作用范围
|
|
26
|
+
仅供 Agent 在妙搭沙箱内调用,查询当前应用的线上观测数据。所有命令为只读。
|
|
27
|
+
应用上下文:--app-id <id> 或环境变量 MIAODA_APP_ID。
|
|
28
|
+
|
|
29
|
+
应用环境
|
|
30
|
+
目前只支持线上环境。
|
|
31
|
+
`);
|
|
32
|
+
registerLog(obCmd);
|
|
33
|
+
registerTrace(obCmd);
|
|
34
|
+
registerMetric(obCmd);
|
|
35
|
+
registerAnalytics(obCmd);
|
|
36
|
+
}
|
|
37
|
+
// ── log ──
|
|
38
|
+
function registerLog(parent) {
|
|
39
|
+
const cmd = parent
|
|
40
|
+
.command("log")
|
|
41
|
+
.description("查询线上运行日志(按时间倒序,分页)")
|
|
42
|
+
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
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
|
+
JSON 输出
|
|
61
|
+
{"data": [...], "next_cursor": "...", "has_more": true|false}
|
|
62
|
+
|
|
63
|
+
示例
|
|
64
|
+
$ miaoda observability log --since 1h --level error --json
|
|
65
|
+
$ miaoda observability log --log-id log_abc123 --json
|
|
66
|
+
$ miaoda observability log --trace-id 140ebb5ac9b... --json
|
|
67
|
+
$ miaoda observability log --api /api/orders --min-duration 200 --json
|
|
68
|
+
`);
|
|
69
|
+
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
70
|
+
(0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
|
|
71
|
+
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
72
|
+
await (0, index_1.handleObservabilityLog)({
|
|
73
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
74
|
+
level: rawOpts.level,
|
|
75
|
+
since: rawOpts.since,
|
|
76
|
+
until: rawOpts.until,
|
|
77
|
+
logId: rawOpts.logId,
|
|
78
|
+
traceId: rawOpts.traceId,
|
|
79
|
+
grep: rawOpts.grep,
|
|
80
|
+
module: rawOpts.module,
|
|
81
|
+
userId: rawOpts.userId,
|
|
82
|
+
page: rawOpts.page,
|
|
83
|
+
api: rawOpts.api,
|
|
84
|
+
minDuration: rawOpts.minDuration,
|
|
85
|
+
maxDuration: rawOpts.maxDuration,
|
|
86
|
+
limit: rawOpts.limit,
|
|
87
|
+
cursor: rawOpts.cursor,
|
|
88
|
+
});
|
|
89
|
+
}));
|
|
90
|
+
}
|
|
91
|
+
// ── trace ──
|
|
92
|
+
function registerTrace(parent) {
|
|
93
|
+
const traceCmd = parent.command("trace").description("链路追踪:列表 / 详情");
|
|
94
|
+
traceCmd.action(() => {
|
|
95
|
+
traceCmd.outputHelp();
|
|
96
|
+
});
|
|
97
|
+
const listCmd = traceCmd
|
|
98
|
+
.command("list")
|
|
99
|
+
.description("查询线上链路列表(按链路时间倒序,分页)")
|
|
100
|
+
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
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
|
+
JSON 输出
|
|
110
|
+
{"data": [...], "next_cursor": "...", "has_more": true|false}
|
|
111
|
+
|
|
112
|
+
示例
|
|
113
|
+
$ miaoda observability trace list --since 1h --json
|
|
114
|
+
$ miaoda observability trace list --root-span api-gateway --limit 20 --json
|
|
115
|
+
`);
|
|
116
|
+
listCmd.action((0, shared_1.withHelp)(listCmd, async (rawOpts) => {
|
|
117
|
+
(0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
|
|
118
|
+
(0, shared_1.rejectCliOverride)(listCmd, "appId");
|
|
119
|
+
await (0, index_1.handleObservabilityTraceList)({
|
|
120
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
121
|
+
since: rawOpts.since,
|
|
122
|
+
until: rawOpts.until,
|
|
123
|
+
traceId: rawOpts.traceId,
|
|
124
|
+
rootSpan: rawOpts.rootSpan,
|
|
125
|
+
userId: rawOpts.userId,
|
|
126
|
+
limit: rawOpts.limit,
|
|
127
|
+
cursor: rawOpts.cursor,
|
|
128
|
+
});
|
|
129
|
+
}));
|
|
130
|
+
const getCmd = traceCmd
|
|
131
|
+
.command("get")
|
|
132
|
+
.description("查询指定链路的完整详情")
|
|
133
|
+
.argument("<trace-id>", "链路 ID")
|
|
134
|
+
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
135
|
+
.option("--with-log-severity-count", "返回每个 Span 各日志级别的数量", false)
|
|
136
|
+
.addHelpText("after", `
|
|
137
|
+
JSON 输出
|
|
138
|
+
{"data": {"spans": [...], "isBreak": false}}
|
|
139
|
+
|
|
140
|
+
示例
|
|
141
|
+
$ miaoda observability trace get 140ebb5ac9b55ed50d6ed1090cad4d --json
|
|
142
|
+
|
|
143
|
+
# 报错:链路不存在
|
|
144
|
+
$ miaoda observability trace get not-exist
|
|
145
|
+
Error: Failed to get trace: 500 ...
|
|
146
|
+
hint: 检查 trace-id 是否正确,或链路已超出保留时长
|
|
147
|
+
`);
|
|
148
|
+
getCmd.action((0, shared_1.withHelp)(getCmd, async (traceId, rawOpts) => {
|
|
149
|
+
(0, shared_1.rejectCliOverride)(getCmd, "appId");
|
|
150
|
+
await (0, index_1.handleObservabilityTraceGet)({
|
|
151
|
+
traceId,
|
|
152
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
153
|
+
withLogSeverityCount: rawOpts.withLogSeverityCount,
|
|
154
|
+
});
|
|
155
|
+
}));
|
|
156
|
+
}
|
|
157
|
+
// ── metric ──
|
|
158
|
+
function registerMetric(parent) {
|
|
159
|
+
const cmd = parent
|
|
160
|
+
.command("metric")
|
|
161
|
+
.description("查询监控指标:requests / latency / cpu / memory")
|
|
162
|
+
.argument("<metric-name>", "指标名:requests | latency | cpu | memory")
|
|
163
|
+
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
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
|
+
JSON 输出
|
|
175
|
+
{"data": [{"metricName": "...", "dimensions": {...}, "dataPoints": [...]}], "next_cursor": null, "has_more": false}
|
|
176
|
+
|
|
177
|
+
示例
|
|
178
|
+
$ miaoda observability metric latency --since 1h --json
|
|
179
|
+
$ miaoda observability metric latency --since 1h --series p99 --json
|
|
180
|
+
$ miaoda observability metric requests --api /api/users --json
|
|
181
|
+
$ miaoda observability metric cpu --since 1d --down-sample 1d --json
|
|
182
|
+
`);
|
|
183
|
+
cmd.action((0, shared_1.withHelp)(cmd, async (metricName, rawOpts) => {
|
|
184
|
+
(0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
|
|
185
|
+
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
186
|
+
await (0, index_1.handleObservabilityMetric)({
|
|
187
|
+
metricName,
|
|
188
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
189
|
+
page: rawOpts.page,
|
|
190
|
+
api: rawOpts.api,
|
|
191
|
+
series: rawOpts.series,
|
|
192
|
+
since: rawOpts.since,
|
|
193
|
+
until: rawOpts.until,
|
|
194
|
+
downSample: rawOpts.downSample,
|
|
195
|
+
});
|
|
196
|
+
}));
|
|
197
|
+
}
|
|
198
|
+
// ── analytics ──
|
|
199
|
+
function registerAnalytics(parent) {
|
|
200
|
+
const cmd = parent
|
|
201
|
+
.command("analytics")
|
|
202
|
+
.description("查询运营指标:users(用户数)/ page-view(页面访问)")
|
|
203
|
+
.argument("<analytics-name>", "指标名:users | page-view")
|
|
204
|
+
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
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
|
+
JSON 输出
|
|
212
|
+
{"data": [{"metricType": "...", "points": [{"timestampNs": "...", "value": ..., "dimensions": {...}}]}], "next_cursor": null, "has_more": false}
|
|
213
|
+
|
|
214
|
+
示例
|
|
215
|
+
$ miaoda observability analytics users --json # 同时返回 active/new/total 三条线
|
|
216
|
+
$ miaoda observability analytics users --series new --since 7d --json
|
|
217
|
+
$ miaoda observability analytics page-view --granularity week --json
|
|
218
|
+
`);
|
|
219
|
+
cmd.action((0, shared_1.withHelp)(cmd, async (analyticsName, rawOpts) => {
|
|
220
|
+
(0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
|
|
221
|
+
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
222
|
+
await (0, index_1.handleObservabilityAnalytics)({
|
|
223
|
+
analyticsName,
|
|
224
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
225
|
+
env: rawOpts.env,
|
|
226
|
+
page: rawOpts.page,
|
|
227
|
+
series: rawOpts.series,
|
|
228
|
+
since: rawOpts.since,
|
|
229
|
+
until: rawOpts.until,
|
|
230
|
+
granularity: rawOpts.granularity,
|
|
231
|
+
});
|
|
232
|
+
}));
|
|
233
|
+
}
|
|
234
|
+
function parseLimit(value) {
|
|
235
|
+
const n = Number(value);
|
|
236
|
+
if (!Number.isFinite(n)) {
|
|
237
|
+
throw new Error(`--limit 必须是数字,收到 '${value}'`);
|
|
238
|
+
}
|
|
239
|
+
return n;
|
|
240
|
+
}
|
|
@@ -1,11 +1,26 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.failArgs = void 0;
|
|
4
|
+
exports.appIdOption = appIdOption;
|
|
5
|
+
exports.branchOption = branchOption;
|
|
3
6
|
exports.softRequiredOption = softRequiredOption;
|
|
4
7
|
exports.resolveAppId = resolveAppId;
|
|
5
8
|
exports.withHelp = withHelp;
|
|
6
|
-
exports.
|
|
9
|
+
exports.caseInsensitiveChoice = caseInsensitiveChoice;
|
|
10
|
+
exports.validateTimeOptions = validateTimeOptions;
|
|
11
|
+
exports.rejectCliOverride = rejectCliOverride;
|
|
7
12
|
const commander_1 = require("commander");
|
|
8
13
|
const error_1 = require("../../utils/error");
|
|
14
|
+
const time_1 = require("../../utils/time");
|
|
15
|
+
const args_1 = require("../../utils/args");
|
|
16
|
+
Object.defineProperty(exports, "failArgs", { enumerable: true, get: function () { return args_1.failArgs; } });
|
|
17
|
+
/** --app-id option,需要应用上下文的命令自行 .addOption(appIdOption()) */
|
|
18
|
+
function appIdOption() {
|
|
19
|
+
return new commander_1.Option("--app-id <id>", "指定目标应用").env("MIAODA_APP_ID");
|
|
20
|
+
}
|
|
21
|
+
function branchOption() {
|
|
22
|
+
return new commander_1.Option("--branch <branch>", "分支(优先级:--branch > 当前仓库 HEAD 分支;非 git 目录回退应用默认分支)").env("LOCAL_MOCK_MIAODA_DEPLOY_BRANCH");
|
|
23
|
+
}
|
|
9
24
|
/**
|
|
10
25
|
* soft-required: Commander 类型上 optional,runtime 校验必填。
|
|
11
26
|
*/
|
|
@@ -27,11 +42,15 @@ function resolveAppId(opts) {
|
|
|
27
42
|
}
|
|
28
43
|
return id;
|
|
29
44
|
}
|
|
30
|
-
/**
|
|
45
|
+
/**
|
|
46
|
+
* 包裹 handler,缺 soft-required 参数 / 自定义校验失败时打 help 并退出。
|
|
47
|
+
*
|
|
48
|
+
* 兼容带位置参数的 action 签名(如 `(arg1: string, opts) => ...`)。
|
|
49
|
+
*/
|
|
31
50
|
function withHelp(cmd, handler) {
|
|
32
|
-
return async (
|
|
51
|
+
return async (...args) => {
|
|
33
52
|
try {
|
|
34
|
-
await handler(
|
|
53
|
+
await handler(...args);
|
|
35
54
|
}
|
|
36
55
|
catch (err) {
|
|
37
56
|
if (err instanceof error_1.AppError && err.code === "ARGS_INVALID") {
|
|
@@ -44,7 +63,64 @@ function withHelp(cmd, handler) {
|
|
|
44
63
|
}
|
|
45
64
|
};
|
|
46
65
|
}
|
|
47
|
-
/**
|
|
48
|
-
|
|
49
|
-
|
|
66
|
+
/**
|
|
67
|
+
* 大小写不敏感的 choice argParser:把用户输入按 lowerCase 匹配回 canonical 数组里
|
|
68
|
+
* 的同名值(保留 canonical 大小写),未命中抛 Commander 的 InvalidArgumentError
|
|
69
|
+
* (exit code 1,自带友好错误)。
|
|
70
|
+
*
|
|
71
|
+
* 必须先 .choices(canonical)、再 .argParser()——Commander 内部 .choices() 会
|
|
72
|
+
* 重置 parseArg,反过来调会让 argParser 失效。.choices() 仅留给 help 渲染
|
|
73
|
+
* "choices: A, B, C",实际白名单校验由 argParser 接管。
|
|
74
|
+
*
|
|
75
|
+
* new Option("--level <level>", "日志级别(不区分大小写)")
|
|
76
|
+
* .choices(["DEBUG", "INFO", "WARN", "ERROR"])
|
|
77
|
+
* .argParser(caseInsensitiveChoice(["DEBUG", "INFO", "WARN", "ERROR"]));
|
|
78
|
+
*/
|
|
79
|
+
function caseInsensitiveChoice(canonical) {
|
|
80
|
+
const map = new Map(canonical.map((v) => [v.toLowerCase(), v]));
|
|
81
|
+
return (raw) => {
|
|
82
|
+
const hit = map.get(raw.toLowerCase());
|
|
83
|
+
if (hit === undefined) {
|
|
84
|
+
throw new commander_1.InvalidArgumentError(`Allowed choices are ${canonical.join(", ")} (case-insensitive).`);
|
|
85
|
+
}
|
|
86
|
+
return hit;
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* --since / --until 等时间参数的 action 阶段校验。
|
|
91
|
+
*
|
|
92
|
+
* Commander 的 argParser 会在 action 前直接按 parser error 退出(exit 1),
|
|
93
|
+
* 绕过 withHelp 的 ARGS_INVALID → exit 2 路径;所以时间参数在 action 内先
|
|
94
|
+
* 校验一遍,再把原始字符串交给 handler 解析成接口需要的单位。
|
|
95
|
+
*/
|
|
96
|
+
function validateTimeOptions(opts, ...names) {
|
|
97
|
+
for (const name of names) {
|
|
98
|
+
const value = opts[name];
|
|
99
|
+
if (value === undefined)
|
|
100
|
+
continue;
|
|
101
|
+
if (typeof value !== "string") {
|
|
102
|
+
(0, args_1.failArgs)(`--${name.replace(/([A-Z])/g, "-$1").toLowerCase()} 必须是时间字符串`);
|
|
103
|
+
}
|
|
104
|
+
(0, time_1.parseTimeToMs)(value);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* 拒绝 CLI 显式覆盖:用于 hideHelp() + .env() 的"沙箱注入参数"(如 --app-id / --branch)。
|
|
109
|
+
*
|
|
110
|
+
* Commander 的 getOptionValueSource() 区分来源(cli / env / default / config / undefined);
|
|
111
|
+
* 用户在 CLI 上显式传时 source === "cli",此时抛 ARGS_INVALID(exit 2 + help)。
|
|
112
|
+
*
|
|
113
|
+
* 用法:在 action 头部一行调用——
|
|
114
|
+
* cmd.action(withHelp(cmd, async (rawOpts) => {
|
|
115
|
+
* rejectCliOverride(cmd, "appId", "branch");
|
|
116
|
+
* ...
|
|
117
|
+
* }));
|
|
118
|
+
*/
|
|
119
|
+
function rejectCliOverride(cmd, ...optNames) {
|
|
120
|
+
for (const name of optNames) {
|
|
121
|
+
if (cmd.getOptionValueSource(name) === "cli") {
|
|
122
|
+
const flag = "--" + name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
123
|
+
(0, args_1.failArgs)(`${flag} 由沙箱/本地配置注入,不允许显式传递`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
50
126
|
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.handleAppGet = handleAppGet;
|
|
37
|
+
const api = __importStar(require("../../../api/index"));
|
|
38
|
+
const output_1 = require("../../../utils/output");
|
|
39
|
+
const index_1 = require("../../../api/app/index");
|
|
40
|
+
/** miaoda app get [--app-id <id>] */
|
|
41
|
+
async function handleAppGet(opts) {
|
|
42
|
+
const appID = opts.appId;
|
|
43
|
+
const resp = await api.app.getAppInfo(appID);
|
|
44
|
+
// BAM 在 status_code=0 时 data 字段含 appInfo;不存在则 fallback 空对象
|
|
45
|
+
const meta = resp.appInfo?.appMeta ?? {};
|
|
46
|
+
(0, output_1.emit)({ data: meta }, index_1.appMetaSchema);
|
|
47
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleAppUpdate = exports.handleAppGet = void 0;
|
|
4
|
+
var get_1 = require("./get");
|
|
5
|
+
Object.defineProperty(exports, "handleAppGet", { enumerable: true, get: function () { return get_1.handleAppGet; } });
|
|
6
|
+
var update_1 = require("./update");
|
|
7
|
+
Object.defineProperty(exports, "handleAppUpdate", { enumerable: true, get: function () { return update_1.handleAppUpdate; } });
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.handleAppUpdate = handleAppUpdate;
|
|
37
|
+
const api = __importStar(require("../../../api/index"));
|
|
38
|
+
const output_1 = require("../../../utils/output");
|
|
39
|
+
const args_1 = require("../../../utils/args");
|
|
40
|
+
const index_1 = require("../../../api/app/index");
|
|
41
|
+
/** miaoda app update [--app-id <id>] [--name <n>] [--description <d>] */
|
|
42
|
+
async function handleAppUpdate(opts) {
|
|
43
|
+
if (opts.name === undefined && opts.description === undefined) {
|
|
44
|
+
(0, args_1.failArgs)("至少需要 --name 或 --description 中的一个");
|
|
45
|
+
}
|
|
46
|
+
const appID = opts.appId;
|
|
47
|
+
await api.app.updateAppMeta({
|
|
48
|
+
appID,
|
|
49
|
+
name: opts.name,
|
|
50
|
+
description: opts.description,
|
|
51
|
+
});
|
|
52
|
+
// 服务端不回写新 meta,本地再 fetch 一次拿最新值给用户
|
|
53
|
+
const resp = await api.app.getAppInfo(appID);
|
|
54
|
+
const meta = resp.appInfo?.appMeta ?? {};
|
|
55
|
+
if (!(0, output_1.isJsonMode)()) {
|
|
56
|
+
process.stdout.write("✓ App updated successfully\n");
|
|
57
|
+
}
|
|
58
|
+
(0, output_1.emit)({ data: meta }, index_1.appMetaSchema);
|
|
59
|
+
}
|