@lark-apaas/miaoda-cli 0.1.3 → 0.1.4
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/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 +12 -12
- package/dist/cli/commands/db/index.js +602 -54
- package/dist/cli/commands/deploy/index.js +28 -28
- package/dist/cli/commands/file/index.js +85 -58
- 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/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/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/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 +19 -19
- package/dist/utils/index.js +3 -1
- package/dist/utils/output.js +67 -45
- package/dist/utils/poll.js +35 -0
- package/dist/utils/render.js +27 -27
- package/dist/utils/spinner.js +46 -0
- package/dist/utils/time.js +47 -42
- package/package.json +1 -1
|
@@ -17,13 +17,13 @@ const COMMON_TIME_HELP = `
|
|
|
17
17
|
function registerDeployCommands(program) {
|
|
18
18
|
// PRD:`miaoda deploy` 自身即为触发发布;get / history / error-log 是子命令
|
|
19
19
|
const deployCmd = program
|
|
20
|
-
.command(
|
|
21
|
-
.description(
|
|
20
|
+
.command('deploy')
|
|
21
|
+
.description('触发发布;支持 --wait 阻塞到终态。子命令:get / history / error-log')
|
|
22
22
|
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
23
23
|
.addOption((0, shared_1.branchOption)().hideHelp())
|
|
24
|
-
.option(
|
|
25
|
-
.option(
|
|
26
|
-
.addHelpText(
|
|
24
|
+
.option('--wait', '阻塞直到流水线终态', false)
|
|
25
|
+
.option('--timeout <sec>', '--wait 最长等待秒数(默认 300)', parseTimeout, 300)
|
|
26
|
+
.addHelpText('after', `
|
|
27
27
|
不要用异步模式或后台模式调用 deploy(例如 &、nohup、后台 task、detached run,或任何会让命令提前返回的包装方式),否则调用可能提前结束,Agent 会误判发布已完成。
|
|
28
28
|
部署前置检查(Agent 必须执行)
|
|
29
29
|
miaoda deploy 跑的是当前分支的远端 HEAD——本地未 commit / 未 push 的代码不会进发布产物。
|
|
@@ -49,7 +49,7 @@ JSON 输出
|
|
|
49
49
|
$ miaoda deploy history --status failed
|
|
50
50
|
`);
|
|
51
51
|
deployCmd.action((0, shared_1.withHelp)(deployCmd, async (rawOpts) => {
|
|
52
|
-
(0, shared_1.rejectCliOverride)(deployCmd,
|
|
52
|
+
(0, shared_1.rejectCliOverride)(deployCmd, 'appId', 'branch');
|
|
53
53
|
await (0, index_1.handleDeploy)({
|
|
54
54
|
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
55
55
|
branch: rawOpts.branch,
|
|
@@ -63,11 +63,11 @@ JSON 输出
|
|
|
63
63
|
}
|
|
64
64
|
function registerDeployGet(parent) {
|
|
65
65
|
const cmd = parent
|
|
66
|
-
.command(
|
|
67
|
-
.description(
|
|
68
|
-
.argument(
|
|
66
|
+
.command('get')
|
|
67
|
+
.description('查看指定发布单的详情')
|
|
68
|
+
.argument('<deploy-id>', '发布单 ID')
|
|
69
69
|
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
70
|
-
.addHelpText(
|
|
70
|
+
.addHelpText('after', `
|
|
71
71
|
JSON 输出
|
|
72
72
|
{"data": {"ID": ..., "status": ..., "creator": "...", "createdAt": ..., "updatedAt": ..., ...}, "next_cursor": null, "has_more": false}
|
|
73
73
|
ID 即 deploy-id(== pipelineTaskID)
|
|
@@ -77,7 +77,7 @@ JSON 输出
|
|
|
77
77
|
$ miaoda deploy get 12345
|
|
78
78
|
`);
|
|
79
79
|
cmd.action((0, shared_1.withHelp)(cmd, async (deployId, rawOpts) => {
|
|
80
|
-
(0, shared_1.rejectCliOverride)(cmd,
|
|
80
|
+
(0, shared_1.rejectCliOverride)(cmd, 'appId');
|
|
81
81
|
await (0, index_1.handleDeployGet)({
|
|
82
82
|
deployId,
|
|
83
83
|
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
@@ -86,17 +86,17 @@ JSON 输出
|
|
|
86
86
|
}
|
|
87
87
|
function registerDeployHistory(parent) {
|
|
88
88
|
const cmd = parent
|
|
89
|
-
.command(
|
|
90
|
-
.description(
|
|
89
|
+
.command('history')
|
|
90
|
+
.description('查询发布历史(按时间倒序,分页)')
|
|
91
91
|
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
92
|
-
.addOption(new commander_1.Option(
|
|
93
|
-
.choices([
|
|
94
|
-
.argParser((0, shared_1.caseInsensitiveChoice)([
|
|
95
|
-
.option(
|
|
96
|
-
.option(
|
|
97
|
-
.option(
|
|
98
|
-
.option(
|
|
99
|
-
.addHelpText(
|
|
92
|
+
.addOption(new commander_1.Option('--status <status>', '状态过滤(pipeline 节点状态,大小写不敏感)')
|
|
93
|
+
.choices(['todo', 'running', 'success', 'failed', 'canceled', 'hold_on'])
|
|
94
|
+
.argParser((0, shared_1.caseInsensitiveChoice)(['todo', 'running', 'success', 'failed', 'canceled', 'hold_on'])))
|
|
95
|
+
.option('--since <time>', '起始时间')
|
|
96
|
+
.option('--until <time>', '截止时间')
|
|
97
|
+
.option('--limit <n>', '返回条数上限(1~100)', parseLimit, 50)
|
|
98
|
+
.option('--cursor <token>', '下一页游标')
|
|
99
|
+
.addHelpText('after', `${COMMON_TIME_HELP}
|
|
100
100
|
JSON 输出
|
|
101
101
|
{"data": [...], "next_cursor": "...", "has_more": true|false}
|
|
102
102
|
data[].status 为可读字符串;data[].status_code 为 BAM 原始数字枚举。
|
|
@@ -106,8 +106,8 @@ JSON 输出
|
|
|
106
106
|
$ miaoda deploy history --since 7d
|
|
107
107
|
`);
|
|
108
108
|
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
109
|
-
(0, shared_1.validateTimeOptions)(rawOpts,
|
|
110
|
-
(0, shared_1.rejectCliOverride)(cmd,
|
|
109
|
+
(0, shared_1.validateTimeOptions)(rawOpts, 'since', 'until');
|
|
110
|
+
(0, shared_1.rejectCliOverride)(cmd, 'appId');
|
|
111
111
|
await (0, index_1.handleDeployHistory)({
|
|
112
112
|
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
113
113
|
status: rawOpts.status,
|
|
@@ -120,11 +120,11 @@ JSON 输出
|
|
|
120
120
|
}
|
|
121
121
|
function registerDeployErrorLog(parent) {
|
|
122
122
|
const cmd = parent
|
|
123
|
-
.command(
|
|
124
|
-
.description(
|
|
125
|
-
.argument(
|
|
123
|
+
.command('error-log')
|
|
124
|
+
.description('查询指定发布的错误日志')
|
|
125
|
+
.argument('<deploy-id>', '发布单 ID')
|
|
126
126
|
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
127
|
-
.addHelpText(
|
|
127
|
+
.addHelpText('after', `
|
|
128
128
|
JSON 输出
|
|
129
129
|
{"data": [{"jobID": ..., "componentName": ..., "errorMsg": ...}], "next_cursor": null, "has_more": false}
|
|
130
130
|
|
|
@@ -132,7 +132,7 @@ JSON 输出
|
|
|
132
132
|
$ miaoda deploy error-log 12345 --json
|
|
133
133
|
`);
|
|
134
134
|
cmd.action((0, shared_1.withHelp)(cmd, async (deployId, rawOpts) => {
|
|
135
|
-
(0, shared_1.rejectCliOverride)(cmd,
|
|
135
|
+
(0, shared_1.rejectCliOverride)(cmd, 'appId');
|
|
136
136
|
await (0, index_1.handleDeployErrorLog)({
|
|
137
137
|
deployId,
|
|
138
138
|
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
@@ -13,40 +13,40 @@ const shared_1 = require("../../../cli/commands/shared");
|
|
|
13
13
|
function parsePositiveInt(raw) {
|
|
14
14
|
const n = Number(raw);
|
|
15
15
|
if (!Number.isInteger(n) || n < 1) {
|
|
16
|
-
throw new error_1.AppError(
|
|
16
|
+
throw new error_1.AppError('ARGS_INVALID', `--limit must be a positive integer (got '${raw}')`);
|
|
17
17
|
}
|
|
18
18
|
return n;
|
|
19
19
|
}
|
|
20
20
|
function registerFileCommands(program) {
|
|
21
21
|
const fileCmd = program
|
|
22
|
-
.command(
|
|
23
|
-
.description(
|
|
24
|
-
|
|
25
|
-
.usage(
|
|
22
|
+
.command('file')
|
|
23
|
+
.description('应用文件存储(TOS)的命令行操作集合。操作对象是 UGC 资源(用户上传的文件、应用运行时\n' +
|
|
24
|
+
'生成的报表 / 导出文件等),不涉及代码仓库里的本地文件。')
|
|
25
|
+
.usage('<command> [flags]');
|
|
26
26
|
fileCmd.action(() => {
|
|
27
27
|
fileCmd.outputHelp();
|
|
28
28
|
});
|
|
29
29
|
fileCmd
|
|
30
|
-
.command(
|
|
31
|
-
.summary(
|
|
32
|
-
.description(
|
|
33
|
-
|
|
34
|
-
.usage(
|
|
35
|
-
.argument(
|
|
36
|
-
.option(
|
|
37
|
-
.option(
|
|
38
|
-
.option(
|
|
39
|
-
.option(
|
|
40
|
-
.option(
|
|
41
|
-
.option(
|
|
42
|
-
.option(
|
|
43
|
-
.option(
|
|
44
|
-
.option(
|
|
45
|
-
.option(
|
|
30
|
+
.command('ls')
|
|
31
|
+
.summary('列出 / 筛选文件')
|
|
32
|
+
.description('列出当前应用存储里的文件,支持按文件名、大小、上传时间筛选,以及游标分页。\n' +
|
|
33
|
+
'默认 pretty 输出省略 download_url(列宽限制),需获取请用 --json。')
|
|
34
|
+
.usage('[query] [flags]')
|
|
35
|
+
.argument('[query]', '筛选值:以 / 开头视为路径精确匹配,否则按文件名精确匹配')
|
|
36
|
+
.option('--path <path>', '按路径精确匹配')
|
|
37
|
+
.option('--name <name>', '按文件名精确匹配')
|
|
38
|
+
.option('--type <mime>', '按 MIME 类型筛选(如 image/png)')
|
|
39
|
+
.option('--size-gt <size>', '文件大小下限(支持 B / KB / MB / GB)')
|
|
40
|
+
.option('--size-lt <size>', '文件大小上限(支持 B / KB / MB / GB)')
|
|
41
|
+
.option('--uploaded-since <time>', '上传时间下限(晚于该时间)')
|
|
42
|
+
.option('--uploaded-until <time>', '上传时间上限(早于该时间)')
|
|
43
|
+
.option('--limit <n>', '单次返回上限(正整数,默认 50)', parsePositiveInt, 50)
|
|
44
|
+
.option('--cursor <token>', '分页游标,从上次响应的 next_cursor 取值')
|
|
45
|
+
.option('--all', '自动翻页返回全部结果')
|
|
46
46
|
.action(async (query, opts) => {
|
|
47
47
|
await (0, index_1.handleFileLs)({ ...opts, appId: (0, shared_1.resolveAppId)({}), query });
|
|
48
48
|
})
|
|
49
|
-
.addHelpText(
|
|
49
|
+
.addHelpText('after', `
|
|
50
50
|
Examples:
|
|
51
51
|
$ miaoda file ls
|
|
52
52
|
file_name path size type uploaded_at
|
|
@@ -62,16 +62,16 @@ Examples:
|
|
|
62
62
|
...
|
|
63
63
|
`);
|
|
64
64
|
fileCmd
|
|
65
|
-
.command(
|
|
66
|
-
.summary(
|
|
67
|
-
.description(
|
|
68
|
-
|
|
69
|
-
.usage(
|
|
70
|
-
.argument(
|
|
65
|
+
.command('stat')
|
|
66
|
+
.summary('查看单文件元数据')
|
|
67
|
+
.description('查看单文件完整元数据,含 download_url(应用内消费)。\n' +
|
|
68
|
+
'需要公网可访问的临时链接请用 `file sign` 生成 signed_url。')
|
|
69
|
+
.usage('<file> [flags]')
|
|
70
|
+
.argument('<file>', '文件的路径或文件名(自动识别)')
|
|
71
71
|
.action(async (file, opts) => {
|
|
72
72
|
await (0, index_1.handleFileStat)(file, { ...opts, appId: (0, shared_1.resolveAppId)({}) });
|
|
73
73
|
})
|
|
74
|
-
.addHelpText(
|
|
74
|
+
.addHelpText('after', `
|
|
75
75
|
Notes:
|
|
76
76
|
- <file> 可传 path(推荐,全局唯一)或 file_name。
|
|
77
77
|
- file_name 重名时报 AMBIGUOUS_FILE_NAME,需先 \`ls --name\` 拿到 path 再调用。
|
|
@@ -92,19 +92,19 @@ Examples:
|
|
|
92
92
|
hint: Use path instead. Run \`miaoda file ls --name logo.png\` to see candidates.
|
|
93
93
|
`);
|
|
94
94
|
fileCmd
|
|
95
|
-
.command(
|
|
96
|
-
.summary(
|
|
97
|
-
.description(
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
.usage(
|
|
101
|
-
.argument(
|
|
102
|
-
.argument(
|
|
103
|
-
.option(
|
|
95
|
+
.command('cp')
|
|
96
|
+
.summary('上传或下载文件(方向由路径前缀自动判断)')
|
|
97
|
+
.description('上传或下载文件,方向由 src/dst 路径前缀自动判断:\n' +
|
|
98
|
+
' / 开头 → 远程 TOS 路径\n' +
|
|
99
|
+
' ./、~/、裸文件名 → 本地路径')
|
|
100
|
+
.usage('<src> <dst> [flags]')
|
|
101
|
+
.argument('<src>', '源:本地文件路径或远程文件路径 / 文件名')
|
|
102
|
+
.argument('<dst>', '目标:本地路径或远程路径')
|
|
103
|
+
.option('--rename <name>', '上传后在远端使用的新文件名')
|
|
104
104
|
.action(async (src, dst, opts) => {
|
|
105
105
|
await (0, index_1.handleFileCp)(src, dst, { ...opts, appId: (0, shared_1.resolveAppId)({}) });
|
|
106
106
|
})
|
|
107
|
-
.addHelpText(
|
|
107
|
+
.addHelpText('after', `
|
|
108
108
|
Notes:
|
|
109
109
|
- 单文件上限 100 MB,超过请拆分或用 web console 上传。
|
|
110
110
|
- 同目录下允许同 file_name 并存(每次上传生成新的 path),不会因重名失败。
|
|
@@ -139,18 +139,18 @@ Examples:
|
|
|
139
139
|
hint: Split the file, or use the web console for large uploads.
|
|
140
140
|
`);
|
|
141
141
|
fileCmd
|
|
142
|
-
.command(
|
|
143
|
-
.summary(
|
|
144
|
-
.description(
|
|
145
|
-
|
|
146
|
-
.usage(
|
|
147
|
-
.argument(
|
|
148
|
-
.option(
|
|
149
|
-
.option(
|
|
142
|
+
.command('rm')
|
|
143
|
+
.summary('删除一个或多个文件')
|
|
144
|
+
.description('删除一个或多个文件。<file> 可传 path(推荐)或 file_name,可混用。\n' +
|
|
145
|
+
'操作不可撤销(不进回收站),TTY 下默认会要求二次确认。')
|
|
146
|
+
.usage('[paths...] [flags]')
|
|
147
|
+
.argument('[paths...]', '要删除的文件,每项可填路径或文件名(自动识别)')
|
|
148
|
+
.option('-n, --name <name>', '按文件名删除(可重复指定)', (value, prev) => [...(prev ?? []), value])
|
|
149
|
+
.option('-y, --yes', '跳过交互确认;非交互场景必加')
|
|
150
150
|
.action(async (paths, opts) => {
|
|
151
151
|
await (0, index_1.handleFileRm)(paths, { ...opts, appId: (0, shared_1.resolveAppId)({}) });
|
|
152
152
|
})
|
|
153
|
-
.addHelpText(
|
|
153
|
+
.addHelpText('after', `
|
|
154
154
|
Notes:
|
|
155
155
|
- 删除不可撤销,没有回收站。
|
|
156
156
|
- 批量场景下失败项不影响其他项;全部成功退出码 0,任意一项失败退出码 1。
|
|
@@ -159,7 +159,7 @@ Notes:
|
|
|
159
159
|
Examples:
|
|
160
160
|
# 单文件删除(TTY 下需确认)
|
|
161
161
|
$ miaoda file rm /images/brand/1858537546760216.png
|
|
162
|
-
?
|
|
162
|
+
? Are you sure you want to permanently delete '/images/brand/1858537546760216.png'? (y/N) y
|
|
163
163
|
✓ Deleted /images/brand/1858537546760216.png
|
|
164
164
|
|
|
165
165
|
# 批量删除(混用 path 与 file_name)
|
|
@@ -182,18 +182,18 @@ Examples:
|
|
|
182
182
|
hint: Use path instead. Run \`miaoda file ls --name logo.png\` to see candidates.
|
|
183
183
|
`);
|
|
184
184
|
fileCmd
|
|
185
|
-
.command(
|
|
186
|
-
.summary(
|
|
187
|
-
.description(
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
.usage(
|
|
191
|
-
.argument(
|
|
192
|
-
.option(
|
|
185
|
+
.command('sign')
|
|
186
|
+
.summary('生成公网可访问的临时下载链接')
|
|
187
|
+
.description('生成 signed_url——公网可直接访问的临时下载链接,带过期时间。\n' +
|
|
188
|
+
'仅在需要浏览器打开 / 公网分享时使用;应用代码内引用文件请用 download_url\n' +
|
|
189
|
+
'(来自 cp 或 stat),无需 sign。')
|
|
190
|
+
.usage('<file> [flags]')
|
|
191
|
+
.argument('<file>', '文件的路径或文件名')
|
|
192
|
+
.option('--expires <duration>', '链接有效期,支持 30m / 24h / 7d 等单位(默认 1d,最长 30d)')
|
|
193
193
|
.action(async (file, opts) => {
|
|
194
194
|
await (0, index_1.handleFileSign)(file, { ...opts, appId: (0, shared_1.resolveAppId)({}) });
|
|
195
195
|
})
|
|
196
|
-
.addHelpText(
|
|
196
|
+
.addHelpText('after', `
|
|
197
197
|
Notes:
|
|
198
198
|
- <file> 可传 path(推荐)或 file_name;重名报 AMBIGUOUS_FILE_NAME。
|
|
199
199
|
- signed_url 会过期,不要持久化到数据库。永久引用请用 download_url。
|
|
@@ -209,5 +209,32 @@ Examples:
|
|
|
209
209
|
$ miaoda file sign /images/brand/1858537546760216.png --expires 60d
|
|
210
210
|
Error: Expires duration '60d' exceeds the maximum of 30d
|
|
211
211
|
hint: Maximum allowed value is 30d. Use \`--expires 30d\` for the longest link.
|
|
212
|
+
`);
|
|
213
|
+
fileCmd
|
|
214
|
+
.command('quota')
|
|
215
|
+
.summary('查看文件存储的用量与配额')
|
|
216
|
+
.description('查看文件存储的用量与配额。')
|
|
217
|
+
.usage('[flags]')
|
|
218
|
+
.action(async function () {
|
|
219
|
+
await (0, index_1.handleFileQuota)(this.optsWithGlobals());
|
|
220
|
+
})
|
|
221
|
+
.addHelpText('after', `
|
|
222
|
+
Examples:
|
|
223
|
+
$ miaoda file quota
|
|
224
|
+
Storage: 150 MB / 1 GB (15%)
|
|
225
|
+
Files: 42
|
|
226
|
+
|
|
227
|
+
# --json
|
|
228
|
+
$ miaoda file quota --json
|
|
229
|
+
{
|
|
230
|
+
"data": {
|
|
231
|
+
"storage_used_bytes": 157286400,
|
|
232
|
+
"storage_quota_bytes": 1073741824,
|
|
233
|
+
"usage_percent": 15,
|
|
234
|
+
"files": 42
|
|
235
|
+
},
|
|
236
|
+
"next_cursor": null,
|
|
237
|
+
"has_more": false
|
|
238
|
+
}
|
|
212
239
|
`);
|
|
213
240
|
}
|
|
@@ -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 }),
|