@lark-apaas/miaoda-cli 0.1.2-alpha.57c97dc → 0.1.2-alpha.66a9e46
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/deploy/schemas.js +3 -1
- package/dist/api/observability/schemas.js +22 -1
- package/dist/cli/commands/app/index.js +3 -3
- package/dist/cli/commands/db/index.js +6 -5
- package/dist/cli/commands/deploy/index.js +22 -7
- package/dist/cli/commands/file/index.js +6 -5
- package/dist/cli/commands/index.js +10 -12
- package/dist/cli/commands/observability/index.js +22 -11
- package/dist/cli/commands/shared.js +24 -6
- package/dist/cli/handlers/app/get.js +2 -3
- package/dist/cli/handlers/app/update.js +4 -4
- package/dist/cli/handlers/db/data.js +2 -3
- 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 +4 -3
- package/dist/cli/handlers/deploy/error-log.js +4 -5
- package/dist/cli/handlers/deploy/format.js +39 -0
- package/dist/cli/handlers/deploy/get.js +5 -4
- package/dist/cli/handlers/deploy/helpers.js +4 -4
- package/dist/cli/handlers/deploy/history.js +3 -3
- package/dist/cli/handlers/deploy/polling.js +26 -3
- package/dist/cli/handlers/file/cp.js +1 -2
- package/dist/cli/handlers/file/ls.js +1 -2
- 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 +30 -8
- package/dist/cli/handlers/observability/helpers.js +2 -2
- package/dist/cli/handlers/observability/log.js +5 -5
- package/dist/cli/handlers/observability/metric.js +4 -4
- package/dist/cli/handlers/observability/trace.js +4 -4
- package/dist/main.js +6 -2
- package/dist/utils/args.js +8 -0
- package/dist/utils/time.js +82 -11
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,22 +14,22 @@ pnpm add -g @lark-apaas/miaoda-cli
|
|
|
14
14
|
# 设置默认应用(需要应用上下文的命令可用 --app-id 覆盖)
|
|
15
15
|
export MIAODA_APP_ID=app_demo_xxx
|
|
16
16
|
|
|
17
|
-
#
|
|
18
|
-
miaoda
|
|
19
|
-
miaoda
|
|
17
|
+
# 文件操作
|
|
18
|
+
miaoda file ls
|
|
19
|
+
miaoda file upload ./local/path
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
JSON 结构化输出(Agent 推荐):
|
|
23
23
|
|
|
24
24
|
```bash
|
|
25
25
|
# 输出全部字段
|
|
26
|
-
miaoda
|
|
26
|
+
miaoda file ls --json
|
|
27
27
|
|
|
28
28
|
# 可选字段级选择
|
|
29
|
-
miaoda
|
|
29
|
+
miaoda file ls --json id,name
|
|
30
30
|
|
|
31
31
|
# 或通过 --output 指定格式
|
|
32
|
-
miaoda
|
|
32
|
+
miaoda file ls --output json
|
|
33
33
|
```
|
|
34
34
|
|
|
35
35
|
## 命令树
|
|
@@ -38,7 +38,8 @@ miaoda plugin list --output json
|
|
|
38
38
|
|
|
39
39
|
| 域 | 用途 |
|
|
40
40
|
|---|---|
|
|
41
|
-
| `miaoda
|
|
41
|
+
| `miaoda file ...` | 文件操作:上传、下载、元数据、签名下载、批量删除 |
|
|
42
|
+
| `miaoda db ...` | 数据操作:SQL 执行、表结构查询、数据导入导出 |
|
|
42
43
|
|
|
43
44
|
完整命令通过 `miaoda --help` 或 `miaoda <domain> --help` 查看。
|
|
44
45
|
|
|
@@ -15,6 +15,8 @@ const NODE_STATUS_TEXT = {
|
|
|
15
15
|
[types_1.NodeStatus.HOLD_ON]: "hold_on",
|
|
16
16
|
};
|
|
17
17
|
function nodeStatusText(v) {
|
|
18
|
+
if (typeof v === "string")
|
|
19
|
+
return v;
|
|
18
20
|
if (typeof v !== "number")
|
|
19
21
|
return undefined;
|
|
20
22
|
return NODE_STATUS_TEXT[v];
|
|
@@ -97,7 +99,7 @@ exports.errorJobSchema = {
|
|
|
97
99
|
columns: [
|
|
98
100
|
{ key: "jobID", label: "job-id" },
|
|
99
101
|
{ key: "componentName", label: "component" },
|
|
100
|
-
{ key: "errorMsg", label: "error"
|
|
102
|
+
{ key: "errorMsg", label: "error" },
|
|
101
103
|
],
|
|
102
104
|
strict: true,
|
|
103
105
|
};
|
|
@@ -6,7 +6,28 @@ const output_1 = require("../../utils/output");
|
|
|
6
6
|
exports.logItemSchema = {
|
|
7
7
|
columns: [
|
|
8
8
|
{ key: "timestampNs", label: "time", format: output_1.fmt.ns("yyyy-MM-dd HH:mm:ss.SSS") },
|
|
9
|
-
{
|
|
9
|
+
{
|
|
10
|
+
key: "module",
|
|
11
|
+
derive: (row) => {
|
|
12
|
+
return row.attributes?.module;
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
key: "user-id",
|
|
17
|
+
derive: (row) => {
|
|
18
|
+
return row.attributes?.user_id;
|
|
19
|
+
},
|
|
20
|
+
},
|
|
21
|
+
{ key: "severityText", label: "severity-text" },
|
|
22
|
+
{
|
|
23
|
+
key: "duration",
|
|
24
|
+
format: output_1.fmt.durationMs(),
|
|
25
|
+
derive: (row) => {
|
|
26
|
+
return row.attributes?.duration_ms;
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
{ key: "traceID", label: "trace-id" },
|
|
30
|
+
{ key: "id", label: "log-id" },
|
|
10
31
|
{ key: "body" },
|
|
11
32
|
],
|
|
12
33
|
strict: true,
|
|
@@ -32,7 +32,7 @@ JSON 输出
|
|
|
32
32
|
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
33
33
|
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
34
34
|
await (0, index_1.handleAppGet)({
|
|
35
|
-
appId: rawOpts.appId,
|
|
35
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
36
36
|
});
|
|
37
37
|
}));
|
|
38
38
|
}
|
|
@@ -45,7 +45,7 @@ function registerAppUpdate(parent) {
|
|
|
45
45
|
.option("--description <desc>", "新应用描述")
|
|
46
46
|
.addHelpText("after", `
|
|
47
47
|
JSON 输出
|
|
48
|
-
与 'app get' 一致:{"data": {...新 meta...}
|
|
48
|
+
与 'app get' 一致:{"data": {...新 meta...}}
|
|
49
49
|
|
|
50
50
|
示例
|
|
51
51
|
$ miaoda app update --name my-store
|
|
@@ -54,7 +54,7 @@ JSON 输出
|
|
|
54
54
|
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
55
55
|
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
56
56
|
await (0, index_1.handleAppUpdate)({
|
|
57
|
-
appId: rawOpts.appId,
|
|
57
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
58
58
|
name: rawOpts.name,
|
|
59
59
|
description: rawOpts.description,
|
|
60
60
|
});
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.registerDbCommands = registerDbCommands;
|
|
4
4
|
const index_1 = require("../../../cli/handlers/db/index");
|
|
5
|
+
const shared_1 = require("../../../cli/commands/shared");
|
|
5
6
|
function registerDbCommands(program) {
|
|
6
7
|
const dbCmd = program
|
|
7
8
|
.command("db")
|
|
@@ -22,7 +23,7 @@ function registerDbCommands(program) {
|
|
|
22
23
|
.usage("<query> [flags]")
|
|
23
24
|
.argument("[query]", "要执行的 SQL 语句;省略时从标准输入读取")
|
|
24
25
|
.action(async function (query) {
|
|
25
|
-
await (0, index_1.handleDbSql)(query, this.optsWithGlobals());
|
|
26
|
+
await (0, index_1.handleDbSql)(query, { ...this.optsWithGlobals(), appId: (0, shared_1.resolveAppId)({}) });
|
|
26
27
|
})
|
|
27
28
|
.addHelpText("after", `
|
|
28
29
|
Notes:
|
|
@@ -73,7 +74,7 @@ Examples:
|
|
|
73
74
|
"查看单表完整结构请用 `schema get`;查看 DDL 变更历史请用 `changelog`(P1)。")
|
|
74
75
|
.usage("[flags]")
|
|
75
76
|
.action(async function () {
|
|
76
|
-
await (0, index_1.handleDbSchemaList)(this.optsWithGlobals());
|
|
77
|
+
await (0, index_1.handleDbSchemaList)({ ...this.optsWithGlobals(), appId: (0, shared_1.resolveAppId)({}) });
|
|
77
78
|
})
|
|
78
79
|
.addHelpText("after", `
|
|
79
80
|
Examples:
|
|
@@ -103,7 +104,7 @@ Examples:
|
|
|
103
104
|
.argument("<table>", "表名(无需带 schema 前缀)")
|
|
104
105
|
.option("--ddl", "强制输出 CREATE TABLE 建表语句(pretty 默认就是 DDL,--json 时配合此 flag 返 SQL 文本)")
|
|
105
106
|
.action(async function (table) {
|
|
106
|
-
await (0, index_1.handleDbSchemaGet)(table, this.optsWithGlobals());
|
|
107
|
+
await (0, index_1.handleDbSchemaGet)(table, { ...this.optsWithGlobals(), appId: (0, shared_1.resolveAppId)({}) });
|
|
107
108
|
})
|
|
108
109
|
.addHelpText("after", `
|
|
109
110
|
Examples:
|
|
@@ -144,7 +145,7 @@ Examples:
|
|
|
144
145
|
.option("--table <name>", "目标表名;未指定时按文件名(不含扩展名)推断(如 users.csv → users)")
|
|
145
146
|
.option("--format <fmt>", "文件格式 csv / json;未指定时按文件扩展名推断")
|
|
146
147
|
.action(async function (file) {
|
|
147
|
-
await (0, index_1.handleDbDataImport)(file, this.optsWithGlobals());
|
|
148
|
+
await (0, index_1.handleDbDataImport)(file, { ...this.optsWithGlobals(), appId: (0, shared_1.resolveAppId)({}) });
|
|
148
149
|
})
|
|
149
150
|
.addHelpText("after", `
|
|
150
151
|
Notes:
|
|
@@ -181,7 +182,7 @@ Examples:
|
|
|
181
182
|
.option("--limit <n>", "最多导出行数(不超过 5000)")
|
|
182
183
|
.option("--force", "输出文件已存在时覆盖(默认报错)")
|
|
183
184
|
.action(async function (table) {
|
|
184
|
-
await (0, index_1.handleDbDataExport)(table, this.optsWithGlobals());
|
|
185
|
+
await (0, index_1.handleDbDataExport)(table, { ...this.optsWithGlobals(), appId: (0, shared_1.resolveAppId)({}) });
|
|
185
186
|
})
|
|
186
187
|
.addHelpText("after", `
|
|
187
188
|
Notes:
|
|
@@ -6,9 +6,13 @@ const shared_1 = require("../../../cli/commands/shared");
|
|
|
6
6
|
const index_1 = require("../../../cli/handlers/deploy/index");
|
|
7
7
|
const COMMON_TIME_HELP = `
|
|
8
8
|
时间格式:
|
|
9
|
-
- 相对时间:
|
|
10
|
-
- 日期:2026-04-01
|
|
11
|
-
-
|
|
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' 拆成两个参数。
|
|
12
16
|
`;
|
|
13
17
|
function registerDeployCommands(program) {
|
|
14
18
|
// PRD:`miaoda deploy` 自身即为触发发布;get / history / error-log 是子命令
|
|
@@ -20,6 +24,7 @@ function registerDeployCommands(program) {
|
|
|
20
24
|
.option("--wait", "阻塞直到流水线终态", false)
|
|
21
25
|
.option("--timeout <sec>", "--wait 最长等待秒数(默认 300)", parseTimeout, 300)
|
|
22
26
|
.addHelpText("after", `
|
|
27
|
+
不要用异步模式或后台模式调用 deploy(例如 &、nohup、后台 task、detached run),否则调用可能提前结束,Agent 会误判发布已完成。
|
|
23
28
|
部署前置检查(Agent 必须执行)
|
|
24
29
|
miaoda deploy 跑的是当前分支的远端 HEAD——本地未 commit / 未 push 的代码不会进发布产物。
|
|
25
30
|
调用前先 \`git status\` + \`git rev-list --count @{u}..HEAD\` 确认;有未提交 / 未推送时:
|
|
@@ -29,6 +34,13 @@ function registerDeployCommands(program) {
|
|
|
29
34
|
JSON 输出
|
|
30
35
|
不带 --wait:{"data": {"pipelineTaskID": "..."}}(pipelineTaskID 即 deploy-id)
|
|
31
36
|
带 --wait:data 额外包含 detail(pipeline 终态)
|
|
37
|
+
detail/status 字段为可读字符串;对应 *_code 字段保留 BAM 原始数字枚举。
|
|
38
|
+
|
|
39
|
+
状态确认(Agent 必须执行)
|
|
40
|
+
优先使用 --wait,但无论是否传 --wait,只要 deploy 调用后没有明确的 success / failed / canceled 终态,
|
|
41
|
+
都必须每 2s 轮询一次当前发布状态:拿到 pipelineTaskID 就执行 \`miaoda deploy get <pipelineTaskID> --json\`;
|
|
42
|
+
如果没有拿到 deploy-id 或输出为空,先执行 \`miaoda deploy history --limit 1 --json\` 找到对应发布记录并取得 ID,
|
|
43
|
+
再用这个 ID 每 2s 执行一次 \`miaoda deploy get <ID> --json\`,直到 status 进入终态。
|
|
32
44
|
|
|
33
45
|
示例
|
|
34
46
|
$ miaoda deploy
|
|
@@ -39,7 +51,7 @@ JSON 输出
|
|
|
39
51
|
deployCmd.action((0, shared_1.withHelp)(deployCmd, async (rawOpts) => {
|
|
40
52
|
(0, shared_1.rejectCliOverride)(deployCmd, "appId", "branch");
|
|
41
53
|
await (0, index_1.handleDeploy)({
|
|
42
|
-
appId: rawOpts.appId,
|
|
54
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
43
55
|
branch: rawOpts.branch,
|
|
44
56
|
wait: rawOpts.wait,
|
|
45
57
|
timeout: rawOpts.timeout,
|
|
@@ -59,6 +71,7 @@ function registerDeployGet(parent) {
|
|
|
59
71
|
JSON 输出
|
|
60
72
|
{"data": {"ID": ..., "status": ..., "creator": "...", "createdAt": ..., "updatedAt": ..., ...}, "next_cursor": null, "has_more": false}
|
|
61
73
|
ID 即 deploy-id(== pipelineTaskID)
|
|
74
|
+
data.status 为可读字符串;data.status_code 为 BAM 原始数字枚举。
|
|
62
75
|
|
|
63
76
|
示例
|
|
64
77
|
$ miaoda deploy get 12345
|
|
@@ -67,7 +80,7 @@ JSON 输出
|
|
|
67
80
|
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
68
81
|
await (0, index_1.handleDeployGet)({
|
|
69
82
|
deployId,
|
|
70
|
-
appId: rawOpts.appId,
|
|
83
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
71
84
|
});
|
|
72
85
|
}));
|
|
73
86
|
}
|
|
@@ -86,15 +99,17 @@ function registerDeployHistory(parent) {
|
|
|
86
99
|
.addHelpText("after", `${COMMON_TIME_HELP}
|
|
87
100
|
JSON 输出
|
|
88
101
|
{"data": [...], "next_cursor": "...", "has_more": true|false}
|
|
102
|
+
data[].status 为可读字符串;data[].status_code 为 BAM 原始数字枚举。
|
|
89
103
|
|
|
90
104
|
示例
|
|
91
105
|
$ miaoda deploy history --status failed
|
|
92
106
|
$ miaoda deploy history --since 7d
|
|
93
107
|
`);
|
|
94
108
|
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
109
|
+
(0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
|
|
95
110
|
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
96
111
|
await (0, index_1.handleDeployHistory)({
|
|
97
|
-
appId: rawOpts.appId,
|
|
112
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
98
113
|
status: rawOpts.status,
|
|
99
114
|
since: rawOpts.since,
|
|
100
115
|
until: rawOpts.until,
|
|
@@ -120,7 +135,7 @@ JSON 输出
|
|
|
120
135
|
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
121
136
|
await (0, index_1.handleDeployErrorLog)({
|
|
122
137
|
deployId,
|
|
123
|
-
appId: rawOpts.appId,
|
|
138
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
124
139
|
});
|
|
125
140
|
}));
|
|
126
141
|
}
|
|
@@ -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:
|
|
@@ -1,17 +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/
|
|
7
|
-
const index_4 = require("../../cli/commands/
|
|
8
|
-
const index_5 = require("../../cli/commands/
|
|
9
|
-
const index_6 = require("../../cli/commands/deploy/index");
|
|
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");
|
|
10
9
|
function registerCommands(program) {
|
|
11
|
-
(0, index_1.
|
|
12
|
-
(0, index_2.
|
|
13
|
-
(0, index_3.
|
|
14
|
-
(0, index_4.
|
|
15
|
-
(0, index_5.
|
|
16
|
-
(0, index_6.registerDeployCommands)(program);
|
|
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);
|
|
17
15
|
}
|
|
@@ -6,9 +6,13 @@ const shared_1 = require("../../../cli/commands/shared");
|
|
|
6
6
|
const index_1 = require("../../../cli/handlers/observability/index");
|
|
7
7
|
const COMMON_TIME_HELP = `
|
|
8
8
|
时间格式:
|
|
9
|
-
- 相对时间:
|
|
10
|
-
- 日期:2026-04-01
|
|
11
|
-
-
|
|
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' 拆成两个参数。
|
|
12
16
|
`;
|
|
13
17
|
function registerObservabilityCommands(program) {
|
|
14
18
|
const obCmd = program
|
|
@@ -41,6 +45,7 @@ function registerLog(parent) {
|
|
|
41
45
|
.argParser((0, shared_1.caseInsensitiveChoice)(["DEBUG", "INFO", "WARN", "ERROR"])))
|
|
42
46
|
.option("--since <time>", "开始时间")
|
|
43
47
|
.option("--until <time>", "截止时间")
|
|
48
|
+
.option("--log-id <id>", "按请求 logid 过滤(attributes.ob_data_id)")
|
|
44
49
|
.option("--trace-id <id>", "按 trace ID 过滤")
|
|
45
50
|
.option("--grep <pattern>", "按关键字模糊搜索 body")
|
|
46
51
|
.option("--module <name>", "按模块名过滤")
|
|
@@ -57,16 +62,19 @@ JSON 输出
|
|
|
57
62
|
|
|
58
63
|
示例
|
|
59
64
|
$ miaoda observability log --since 1h --level error --json
|
|
65
|
+
$ miaoda observability log --log-id log_abc123 --json
|
|
60
66
|
$ miaoda observability log --trace-id 140ebb5ac9b... --json
|
|
61
67
|
$ miaoda observability log --api /api/orders --min-duration 200 --json
|
|
62
68
|
`);
|
|
63
69
|
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
70
|
+
(0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
|
|
64
71
|
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
65
72
|
await (0, index_1.handleObservabilityLog)({
|
|
66
|
-
appId: rawOpts.appId,
|
|
73
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
67
74
|
level: rawOpts.level,
|
|
68
75
|
since: rawOpts.since,
|
|
69
76
|
until: rawOpts.until,
|
|
77
|
+
logId: rawOpts.logId,
|
|
70
78
|
traceId: rawOpts.traceId,
|
|
71
79
|
grep: rawOpts.grep,
|
|
72
80
|
module: rawOpts.module,
|
|
@@ -106,9 +114,10 @@ JSON 输出
|
|
|
106
114
|
$ miaoda observability trace list --root-span api-gateway --limit 20 --json
|
|
107
115
|
`);
|
|
108
116
|
listCmd.action((0, shared_1.withHelp)(listCmd, async (rawOpts) => {
|
|
117
|
+
(0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
|
|
109
118
|
(0, shared_1.rejectCliOverride)(listCmd, "appId");
|
|
110
119
|
await (0, index_1.handleObservabilityTraceList)({
|
|
111
|
-
appId: rawOpts.appId,
|
|
120
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
112
121
|
since: rawOpts.since,
|
|
113
122
|
until: rawOpts.until,
|
|
114
123
|
traceId: rawOpts.traceId,
|
|
@@ -140,7 +149,7 @@ JSON 输出
|
|
|
140
149
|
(0, shared_1.rejectCliOverride)(getCmd, "appId");
|
|
141
150
|
await (0, index_1.handleObservabilityTraceGet)({
|
|
142
151
|
traceId,
|
|
143
|
-
appId: rawOpts.appId,
|
|
152
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
144
153
|
withLogSeverityCount: rawOpts.withLogSeverityCount,
|
|
145
154
|
});
|
|
146
155
|
}));
|
|
@@ -160,7 +169,7 @@ function registerMetric(parent) {
|
|
|
160
169
|
.addOption(new commander_1.Option("--down-sample <duration>", "降采样粒度(1m=1分钟 / 1h=1小时 / 1d=1天,大小写不敏感)")
|
|
161
170
|
.choices(["1m", "1h", "1d"])
|
|
162
171
|
.argParser((0, shared_1.caseInsensitiveChoice)(["1m", "1h", "1d"]))
|
|
163
|
-
.default("
|
|
172
|
+
.default("1m"))
|
|
164
173
|
.addHelpText("after", `${COMMON_TIME_HELP}
|
|
165
174
|
JSON 输出
|
|
166
175
|
{"data": [{"metricName": "...", "dimensions": {...}, "dataPoints": [...]}], "next_cursor": null, "has_more": false}
|
|
@@ -172,10 +181,11 @@ JSON 输出
|
|
|
172
181
|
$ miaoda observability metric cpu --since 1d --down-sample 1d --json
|
|
173
182
|
`);
|
|
174
183
|
cmd.action((0, shared_1.withHelp)(cmd, async (metricName, rawOpts) => {
|
|
184
|
+
(0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
|
|
175
185
|
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
176
186
|
await (0, index_1.handleObservabilityMetric)({
|
|
177
187
|
metricName,
|
|
178
|
-
appId: rawOpts.appId,
|
|
188
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
179
189
|
page: rawOpts.page,
|
|
180
190
|
api: rawOpts.api,
|
|
181
191
|
series: rawOpts.series,
|
|
@@ -193,7 +203,7 @@ function registerAnalytics(parent) {
|
|
|
193
203
|
.argument("<analytics-name>", "指标名:users | page-view")
|
|
194
204
|
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
195
205
|
.option("--page <path>", "按页面路径过滤(仅对 page-view 生效)")
|
|
196
|
-
.option("--series <name>", "过滤图表中的某条线:users 取 active-users/new-users/total-users
|
|
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)")
|
|
197
207
|
.option("--since <time>", "开始时间")
|
|
198
208
|
.option("--until <time>", "截止时间")
|
|
199
209
|
.option("--granularity <duration>", "时间粒度:day | week | month", "day")
|
|
@@ -203,14 +213,15 @@ JSON 输出
|
|
|
203
213
|
|
|
204
214
|
示例
|
|
205
215
|
$ miaoda observability analytics users --json # 同时返回 active/new/total 三条线
|
|
206
|
-
$ miaoda observability analytics users --series new
|
|
216
|
+
$ miaoda observability analytics users --series new --since 7d --json
|
|
207
217
|
$ miaoda observability analytics page-view --granularity week --json
|
|
208
218
|
`);
|
|
209
219
|
cmd.action((0, shared_1.withHelp)(cmd, async (analyticsName, rawOpts) => {
|
|
220
|
+
(0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
|
|
210
221
|
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
211
222
|
await (0, index_1.handleObservabilityAnalytics)({
|
|
212
223
|
analyticsName,
|
|
213
|
-
appId: rawOpts.appId,
|
|
224
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
214
225
|
env: rawOpts.env,
|
|
215
226
|
page: rawOpts.page,
|
|
216
227
|
series: rawOpts.series,
|
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.failArgs = void 0;
|
|
3
4
|
exports.appIdOption = appIdOption;
|
|
4
5
|
exports.branchOption = branchOption;
|
|
5
6
|
exports.softRequiredOption = softRequiredOption;
|
|
6
7
|
exports.resolveAppId = resolveAppId;
|
|
7
8
|
exports.withHelp = withHelp;
|
|
8
|
-
exports.failArgs = failArgs;
|
|
9
9
|
exports.caseInsensitiveChoice = caseInsensitiveChoice;
|
|
10
|
+
exports.validateTimeOptions = validateTimeOptions;
|
|
10
11
|
exports.rejectCliOverride = rejectCliOverride;
|
|
11
12
|
const commander_1 = require("commander");
|
|
12
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; } });
|
|
13
17
|
/** --app-id option,需要应用上下文的命令自行 .addOption(appIdOption()) */
|
|
14
18
|
function appIdOption() {
|
|
15
19
|
return new commander_1.Option("--app-id <id>", "指定目标应用").env("MIAODA_APP_ID");
|
|
@@ -59,10 +63,6 @@ function withHelp(cmd, handler) {
|
|
|
59
63
|
}
|
|
60
64
|
};
|
|
61
65
|
}
|
|
62
|
-
/** 参数校验失败时抛出,配合 withHelp 自动打 help */
|
|
63
|
-
function failArgs(message) {
|
|
64
|
-
throw new error_1.AppError("ARGS_INVALID", message);
|
|
65
|
-
}
|
|
66
66
|
/**
|
|
67
67
|
* 大小写不敏感的 choice argParser:把用户输入按 lowerCase 匹配回 canonical 数组里
|
|
68
68
|
* 的同名值(保留 canonical 大小写),未命中抛 Commander 的 InvalidArgumentError
|
|
@@ -86,6 +86,24 @@ function caseInsensitiveChoice(canonical) {
|
|
|
86
86
|
return hit;
|
|
87
87
|
};
|
|
88
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
|
+
}
|
|
89
107
|
/**
|
|
90
108
|
* 拒绝 CLI 显式覆盖:用于 hideHelp() + .env() 的"沙箱注入参数"(如 --app-id / --branch)。
|
|
91
109
|
*
|
|
@@ -102,7 +120,7 @@ function rejectCliOverride(cmd, ...optNames) {
|
|
|
102
120
|
for (const name of optNames) {
|
|
103
121
|
if (cmd.getOptionValueSource(name) === "cli") {
|
|
104
122
|
const flag = "--" + name.replace(/([A-Z])/g, "-$1").toLowerCase();
|
|
105
|
-
failArgs(`${flag} 由沙箱/本地配置注入,不允许显式传递`);
|
|
123
|
+
(0, args_1.failArgs)(`${flag} 由沙箱/本地配置注入,不允许显式传递`);
|
|
106
124
|
}
|
|
107
125
|
}
|
|
108
126
|
}
|
|
@@ -36,13 +36,12 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.handleAppGet = handleAppGet;
|
|
37
37
|
const api = __importStar(require("../../../api/index"));
|
|
38
38
|
const output_1 = require("../../../utils/output");
|
|
39
|
-
const shared_1 = require("../../../cli/commands/shared");
|
|
40
39
|
const index_1 = require("../../../api/app/index");
|
|
41
40
|
/** miaoda app get [--app-id <id>] */
|
|
42
41
|
async function handleAppGet(opts) {
|
|
43
|
-
const appID =
|
|
42
|
+
const appID = opts.appId;
|
|
44
43
|
const resp = await api.app.getAppInfo(appID);
|
|
45
44
|
// BAM 在 status_code=0 时 data 字段含 appInfo;不存在则 fallback 空对象
|
|
46
45
|
const meta = resp.appInfo?.appMeta ?? {};
|
|
47
|
-
(0, output_1.emit)({ data: meta
|
|
46
|
+
(0, output_1.emit)({ data: meta }, index_1.appMetaSchema);
|
|
48
47
|
}
|
|
@@ -36,14 +36,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.handleAppUpdate = handleAppUpdate;
|
|
37
37
|
const api = __importStar(require("../../../api/index"));
|
|
38
38
|
const output_1 = require("../../../utils/output");
|
|
39
|
-
const
|
|
39
|
+
const args_1 = require("../../../utils/args");
|
|
40
40
|
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,
|
|
44
|
+
(0, args_1.failArgs)("至少需要 --name 或 --description 中的一个");
|
|
45
45
|
}
|
|
46
|
-
const appID =
|
|
46
|
+
const appID = opts.appId;
|
|
47
47
|
await api.app.updateAppMeta({
|
|
48
48
|
appID,
|
|
49
49
|
name: opts.name,
|
|
@@ -55,5 +55,5 @@ async function handleAppUpdate(opts) {
|
|
|
55
55
|
if (!(0, output_1.isJsonMode)()) {
|
|
56
56
|
process.stdout.write("✓ App updated successfully\n");
|
|
57
57
|
}
|
|
58
|
-
(0, output_1.emit)({ data: meta
|
|
58
|
+
(0, output_1.emit)({ data: meta }, index_1.appMetaSchema);
|
|
59
59
|
}
|
|
@@ -40,13 +40,12 @@ const path = __importStar(require("node:path"));
|
|
|
40
40
|
const api = __importStar(require("../../../api/index"));
|
|
41
41
|
const error_1 = require("../../../utils/error");
|
|
42
42
|
const output_1 = require("../../../utils/output");
|
|
43
|
-
const shared_1 = require("../../../cli/commands/shared");
|
|
44
43
|
const render_1 = require("../../../utils/render");
|
|
45
44
|
// P0 规格(对齐技术方案关键决策 2)
|
|
46
45
|
const MAX_SIZE_BYTES = 1 * 1024 * 1024; // 1 MB
|
|
47
46
|
const MAX_ROWS = 5000;
|
|
48
47
|
async function handleDbDataImport(file, opts) {
|
|
49
|
-
const appId =
|
|
48
|
+
const appId = opts.appId;
|
|
50
49
|
const ext = path.extname(file).toLowerCase();
|
|
51
50
|
const format = resolveFormat(opts.format, ext, "import");
|
|
52
51
|
let body;
|
|
@@ -95,7 +94,7 @@ async function handleDbDataImport(file, opts) {
|
|
|
95
94
|
: `OK Imported ${file} -> table '${result.tableName}' (${String(result.recordCount)} rows)`);
|
|
96
95
|
}
|
|
97
96
|
async function handleDbDataExport(table, opts) {
|
|
98
|
-
const appId =
|
|
97
|
+
const appId = opts.appId;
|
|
99
98
|
const format = resolveFormat(opts.format, undefined, "export", "csv");
|
|
100
99
|
const outputPath = opts.file ?? `${table}.${format}`;
|
|
101
100
|
const limit = opts.limit ? Number(opts.limit) : MAX_ROWS;
|
|
@@ -39,12 +39,11 @@ const api = __importStar(require("../../../api/index"));
|
|
|
39
39
|
const error_1 = require("../../../utils/error");
|
|
40
40
|
const output_1 = require("../../../utils/output");
|
|
41
41
|
const render_1 = require("../../../utils/render");
|
|
42
|
-
const shared_1 = require("../../../cli/commands/shared");
|
|
43
42
|
const colors_1 = require("../../../utils/colors");
|
|
44
43
|
const index_1 = require("../../../api/db/index");
|
|
45
44
|
// ── schema list ──
|
|
46
45
|
async function handleDbSchemaList(opts) {
|
|
47
|
-
const appId =
|
|
46
|
+
const appId = opts.appId;
|
|
48
47
|
const resp = await api.db.getSchema({
|
|
49
48
|
appId,
|
|
50
49
|
format: "schema",
|
|
@@ -80,7 +79,7 @@ async function handleDbSchemaList(opts) {
|
|
|
80
79
|
(0, output_1.emit)(tty ? (0, render_1.renderAlignedTable)(headers, rows) : (0, render_1.renderTsv)(headers, rows));
|
|
81
80
|
}
|
|
82
81
|
async function handleDbSchemaGet(table, opts) {
|
|
83
|
-
const appId =
|
|
82
|
+
const appId = opts.appId;
|
|
84
83
|
const tty = (0, render_1.isStdoutTty)();
|
|
85
84
|
const forceDdl = Boolean(opts.ddl);
|
|
86
85
|
const wantsStructured = (0, output_1.isJsonMode)() || (tty && !forceDdl);
|
|
@@ -42,7 +42,6 @@ const error_1 = require("../../../utils/error");
|
|
|
42
42
|
const output_1 = require("../../../utils/output");
|
|
43
43
|
const config_1 = require("../../../utils/config");
|
|
44
44
|
const logger_1 = require("../../../utils/logger");
|
|
45
|
-
const shared_1 = require("../../../cli/commands/shared");
|
|
46
45
|
const render_1 = require("../../../utils/render");
|
|
47
46
|
const colors_1 = require("../../../utils/colors");
|
|
48
47
|
const fuzzy_match_1 = require("../../../utils/fuzzy-match");
|
|
@@ -62,7 +61,7 @@ const node_path_1 = __importDefault(require("node:path"));
|
|
|
62
61
|
* 末尾汇总,--json 输出 data 数组。
|
|
63
62
|
*/
|
|
64
63
|
async function handleDbSql(query, opts) {
|
|
65
|
-
const appId =
|
|
64
|
+
const appId = opts.appId;
|
|
66
65
|
const sql = await readSql(query);
|
|
67
66
|
if (!sql.trim()) {
|
|
68
67
|
throw new error_1.AppError("ARGS_INVALID", "Empty SQL (no inline query and stdin is empty)");
|