@lark-apaas/miaoda-cli 0.1.2 → 0.1.3
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/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/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 +6 -5
- package/dist/cli/commands/deploy/index.js +155 -0
- package/dist/cli/commands/file/index.js +6 -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/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 +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/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 +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 +13 -1
- package/dist/utils/output.js +338 -7
- package/dist/utils/time.js +203 -0
- package/package.json +7 -5
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerAppCommands = registerAppCommands;
|
|
4
|
+
const shared_1 = require("../../../cli/commands/shared");
|
|
5
|
+
const index_1 = require("../../../cli/handlers/app/index");
|
|
6
|
+
function registerAppCommands(program) {
|
|
7
|
+
const appCmd = program.command("app").description("应用元数据管理:查看 / 修改");
|
|
8
|
+
appCmd.action(() => {
|
|
9
|
+
appCmd.outputHelp();
|
|
10
|
+
});
|
|
11
|
+
appCmd.addHelpText("after", `
|
|
12
|
+
作用范围
|
|
13
|
+
仅供 Agent 在妙搭沙箱内调用。
|
|
14
|
+
应用上下文:沙箱内的默认应用。
|
|
15
|
+
`);
|
|
16
|
+
registerAppGet(appCmd);
|
|
17
|
+
registerAppUpdate(appCmd);
|
|
18
|
+
}
|
|
19
|
+
function registerAppGet(parent) {
|
|
20
|
+
const cmd = parent
|
|
21
|
+
.command("get")
|
|
22
|
+
.description("查看应用元数据(status / type / mode / branch / 时间戳 等)")
|
|
23
|
+
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
24
|
+
.addHelpText("after", `
|
|
25
|
+
JSON 输出
|
|
26
|
+
{"data": {"appID": "...", "name": "...", ...}}
|
|
27
|
+
|
|
28
|
+
示例
|
|
29
|
+
$ miaoda app get
|
|
30
|
+
$ miaoda app get --json
|
|
31
|
+
`);
|
|
32
|
+
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
33
|
+
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
34
|
+
await (0, index_1.handleAppGet)({
|
|
35
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
36
|
+
});
|
|
37
|
+
}));
|
|
38
|
+
}
|
|
39
|
+
function registerAppUpdate(parent) {
|
|
40
|
+
const cmd = parent
|
|
41
|
+
.command("update")
|
|
42
|
+
.description("修改应用名称 / 描述(至少传 --name 或 --description)")
|
|
43
|
+
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
44
|
+
.option("--name <name>", "新应用名称")
|
|
45
|
+
.option("--description <desc>", "新应用描述")
|
|
46
|
+
.addHelpText("after", `
|
|
47
|
+
JSON 输出
|
|
48
|
+
与 'app get' 一致:{"data": {...新 meta...}}
|
|
49
|
+
|
|
50
|
+
示例
|
|
51
|
+
$ miaoda app update --name my-store
|
|
52
|
+
$ miaoda app update --description "在线商城应用"
|
|
53
|
+
`);
|
|
54
|
+
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
55
|
+
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
56
|
+
await (0, index_1.handleAppUpdate)({
|
|
57
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
58
|
+
name: rawOpts.name,
|
|
59
|
+
description: rawOpts.description,
|
|
60
|
+
});
|
|
61
|
+
}));
|
|
62
|
+
}
|
|
@@ -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:
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.registerDeployCommands = registerDeployCommands;
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const shared_1 = require("../../../cli/commands/shared");
|
|
6
|
+
const index_1 = require("../../../cli/handlers/deploy/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 registerDeployCommands(program) {
|
|
18
|
+
// PRD:`miaoda deploy` 自身即为触发发布;get / history / error-log 是子命令
|
|
19
|
+
const deployCmd = program
|
|
20
|
+
.command("deploy")
|
|
21
|
+
.description("触发发布;支持 --wait 阻塞到终态。子命令:get / history / error-log")
|
|
22
|
+
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
23
|
+
.addOption((0, shared_1.branchOption)().hideHelp())
|
|
24
|
+
.option("--wait", "阻塞直到流水线终态", false)
|
|
25
|
+
.option("--timeout <sec>", "--wait 最长等待秒数(默认 300)", parseTimeout, 300)
|
|
26
|
+
.addHelpText("after", `
|
|
27
|
+
不要用异步模式或后台模式调用 deploy(例如 &、nohup、后台 task、detached run,或任何会让命令提前返回的包装方式),否则调用可能提前结束,Agent 会误判发布已完成。
|
|
28
|
+
部署前置检查(Agent 必须执行)
|
|
29
|
+
miaoda deploy 跑的是当前分支的远端 HEAD——本地未 commit / 未 push 的代码不会进发布产物。
|
|
30
|
+
调用前先 \`git status\` + \`git rev-list --count @{u}..HEAD\` 确认;有未提交 / 未推送时:
|
|
31
|
+
- 是本次任务的产物 → 先 commit + push 再 deploy
|
|
32
|
+
- 不能确定意图 → 用 AskUser 跟用户确认(不要默默继续)
|
|
33
|
+
|
|
34
|
+
JSON 输出
|
|
35
|
+
不带 --wait:{"data": {"pipelineTaskID": "..."}}(pipelineTaskID 即 deploy-id)
|
|
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 进入终态。
|
|
44
|
+
|
|
45
|
+
示例
|
|
46
|
+
$ miaoda deploy
|
|
47
|
+
$ miaoda deploy --wait --timeout 600
|
|
48
|
+
$ miaoda deploy get 12345
|
|
49
|
+
$ miaoda deploy history --status failed
|
|
50
|
+
`);
|
|
51
|
+
deployCmd.action((0, shared_1.withHelp)(deployCmd, async (rawOpts) => {
|
|
52
|
+
(0, shared_1.rejectCliOverride)(deployCmd, "appId", "branch");
|
|
53
|
+
await (0, index_1.handleDeploy)({
|
|
54
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
55
|
+
branch: rawOpts.branch,
|
|
56
|
+
wait: rawOpts.wait,
|
|
57
|
+
timeout: rawOpts.timeout,
|
|
58
|
+
});
|
|
59
|
+
}));
|
|
60
|
+
registerDeployGet(deployCmd);
|
|
61
|
+
registerDeployHistory(deployCmd);
|
|
62
|
+
registerDeployErrorLog(deployCmd);
|
|
63
|
+
}
|
|
64
|
+
function registerDeployGet(parent) {
|
|
65
|
+
const cmd = parent
|
|
66
|
+
.command("get")
|
|
67
|
+
.description("查看指定发布单的详情")
|
|
68
|
+
.argument("<deploy-id>", "发布单 ID")
|
|
69
|
+
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
70
|
+
.addHelpText("after", `
|
|
71
|
+
JSON 输出
|
|
72
|
+
{"data": {"ID": ..., "status": ..., "creator": "...", "createdAt": ..., "updatedAt": ..., ...}, "next_cursor": null, "has_more": false}
|
|
73
|
+
ID 即 deploy-id(== pipelineTaskID)
|
|
74
|
+
data.status 为可读字符串;data.status_code 为 BAM 原始数字枚举。
|
|
75
|
+
|
|
76
|
+
示例
|
|
77
|
+
$ miaoda deploy get 12345
|
|
78
|
+
`);
|
|
79
|
+
cmd.action((0, shared_1.withHelp)(cmd, async (deployId, rawOpts) => {
|
|
80
|
+
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
81
|
+
await (0, index_1.handleDeployGet)({
|
|
82
|
+
deployId,
|
|
83
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
84
|
+
});
|
|
85
|
+
}));
|
|
86
|
+
}
|
|
87
|
+
function registerDeployHistory(parent) {
|
|
88
|
+
const cmd = parent
|
|
89
|
+
.command("history")
|
|
90
|
+
.description("查询发布历史(按时间倒序,分页)")
|
|
91
|
+
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
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
|
+
JSON 输出
|
|
101
|
+
{"data": [...], "next_cursor": "...", "has_more": true|false}
|
|
102
|
+
data[].status 为可读字符串;data[].status_code 为 BAM 原始数字枚举。
|
|
103
|
+
|
|
104
|
+
示例
|
|
105
|
+
$ miaoda deploy history --status failed
|
|
106
|
+
$ miaoda deploy history --since 7d
|
|
107
|
+
`);
|
|
108
|
+
cmd.action((0, shared_1.withHelp)(cmd, async (rawOpts) => {
|
|
109
|
+
(0, shared_1.validateTimeOptions)(rawOpts, "since", "until");
|
|
110
|
+
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
111
|
+
await (0, index_1.handleDeployHistory)({
|
|
112
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
113
|
+
status: rawOpts.status,
|
|
114
|
+
since: rawOpts.since,
|
|
115
|
+
until: rawOpts.until,
|
|
116
|
+
limit: rawOpts.limit,
|
|
117
|
+
cursor: rawOpts.cursor,
|
|
118
|
+
});
|
|
119
|
+
}));
|
|
120
|
+
}
|
|
121
|
+
function registerDeployErrorLog(parent) {
|
|
122
|
+
const cmd = parent
|
|
123
|
+
.command("error-log")
|
|
124
|
+
.description("查询指定发布的错误日志")
|
|
125
|
+
.argument("<deploy-id>", "发布单 ID")
|
|
126
|
+
.addOption((0, shared_1.appIdOption)().hideHelp())
|
|
127
|
+
.addHelpText("after", `
|
|
128
|
+
JSON 输出
|
|
129
|
+
{"data": [{"jobID": ..., "componentName": ..., "errorMsg": ...}], "next_cursor": null, "has_more": false}
|
|
130
|
+
|
|
131
|
+
示例
|
|
132
|
+
$ miaoda deploy error-log 12345 --json
|
|
133
|
+
`);
|
|
134
|
+
cmd.action((0, shared_1.withHelp)(cmd, async (deployId, rawOpts) => {
|
|
135
|
+
(0, shared_1.rejectCliOverride)(cmd, "appId");
|
|
136
|
+
await (0, index_1.handleDeployErrorLog)({
|
|
137
|
+
deployId,
|
|
138
|
+
appId: (0, shared_1.resolveAppId)({ appId: rawOpts.appId }),
|
|
139
|
+
});
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
function parseLimit(value) {
|
|
143
|
+
const n = Number(value);
|
|
144
|
+
if (!Number.isFinite(n)) {
|
|
145
|
+
throw new Error(`--limit 必须是数字,收到 '${value}'`);
|
|
146
|
+
}
|
|
147
|
+
return n;
|
|
148
|
+
}
|
|
149
|
+
function parseTimeout(value) {
|
|
150
|
+
const n = Number(value);
|
|
151
|
+
if (!Number.isFinite(n) || n <= 0) {
|
|
152
|
+
throw new Error(`--timeout 必须是正数,收到 '${value}'`);
|
|
153
|
+
}
|
|
154
|
+
return n;
|
|
155
|
+
}
|
|
@@ -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,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
|
+
}
|