@lark-apaas/miaoda-cli 0.1.1 → 0.1.2-alpha.b2b5ae5

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.
Files changed (64) hide show
  1. package/dist/api/app/api.js +25 -0
  2. package/dist/api/app/index.js +15 -0
  3. package/dist/api/app/schemas.js +79 -0
  4. package/dist/api/app/types.js +58 -0
  5. package/dist/api/db/api.js +83 -6
  6. package/dist/api/db/client.js +40 -29
  7. package/dist/api/db/parsers.js +33 -20
  8. package/dist/api/db/sql-keywords.js +123 -0
  9. package/dist/api/deploy/api.js +60 -0
  10. package/dist/api/deploy/index.js +16 -0
  11. package/dist/api/deploy/schemas.js +103 -0
  12. package/dist/api/deploy/types.js +22 -0
  13. package/dist/api/file/api.js +78 -24
  14. package/dist/api/file/client.js +1 -5
  15. package/dist/api/file/parsers.js +1 -5
  16. package/dist/api/index.js +7 -1
  17. package/dist/api/observability/api.js +52 -0
  18. package/dist/api/observability/index.js +16 -0
  19. package/dist/api/observability/schemas.js +39 -0
  20. package/dist/api/observability/types.js +27 -0
  21. package/dist/api/plugin/api.js +8 -3
  22. package/dist/cli/commands/app/index.js +62 -0
  23. package/dist/cli/commands/db/index.js +1 -0
  24. package/dist/cli/commands/deploy/index.js +139 -0
  25. package/dist/cli/commands/index.js +6 -0
  26. package/dist/cli/commands/observability/index.js +227 -0
  27. package/dist/cli/commands/plugin/index.js +18 -6
  28. package/dist/cli/commands/shared.js +38 -6
  29. package/dist/cli/handlers/app/get.js +48 -0
  30. package/dist/cli/handlers/app/index.js +7 -0
  31. package/dist/cli/handlers/app/update.js +59 -0
  32. package/dist/cli/handlers/db/data.js +22 -2
  33. package/dist/cli/handlers/db/schema.js +22 -8
  34. package/dist/cli/handlers/db/sql.js +304 -16
  35. package/dist/cli/handlers/deploy/deploy.js +83 -0
  36. package/dist/cli/handlers/deploy/error-log.js +61 -0
  37. package/dist/cli/handlers/deploy/get.js +70 -0
  38. package/dist/cli/handlers/deploy/helpers.js +41 -0
  39. package/dist/cli/handlers/deploy/history.js +70 -0
  40. package/dist/cli/handlers/deploy/index.js +14 -0
  41. package/dist/cli/handlers/deploy/polling.js +139 -0
  42. package/dist/cli/handlers/file/cp.js +39 -17
  43. package/dist/cli/handlers/file/ls.js +1 -3
  44. package/dist/cli/handlers/file/rm.js +4 -3
  45. package/dist/cli/handlers/observability/analytics.js +189 -0
  46. package/dist/cli/handlers/observability/helpers.js +66 -0
  47. package/dist/cli/handlers/observability/index.js +12 -0
  48. package/dist/cli/handlers/observability/log.js +94 -0
  49. package/dist/cli/handlers/observability/metric.js +208 -0
  50. package/dist/cli/handlers/observability/trace.js +102 -0
  51. package/dist/cli/handlers/plugin/plugin-local.js +23 -9
  52. package/dist/cli/handlers/plugin/plugin.js +21 -7
  53. package/dist/cli/help.js +5 -2
  54. package/dist/utils/colors.js +98 -0
  55. package/dist/utils/devops-error.js +28 -0
  56. package/dist/utils/error.js +11 -0
  57. package/dist/utils/fuzzy-match.js +91 -0
  58. package/dist/utils/git.js +29 -0
  59. package/dist/utils/http.js +32 -0
  60. package/dist/utils/index.js +13 -1
  61. package/dist/utils/output.js +397 -12
  62. package/dist/utils/render.js +61 -41
  63. package/dist/utils/time.js +132 -0
  64. package/package.json +16 -6
@@ -0,0 +1,66 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ceilMsToBucket = exports.floorMsToBucket = exports.msToSec = exports.msToNs = exports.parseToSec = exports.parseToNs = exports.parseToMs = exports.parseTimeToMs = void 0;
4
+ exports.eqFilter = eqFilter;
5
+ exports.rangeFilter = rangeFilter;
6
+ exports.fuzzyFilter = fuzzyFilter;
7
+ exports.buildFieldFilters = buildFieldFilters;
8
+ exports.validateLimit = validateLimit;
9
+ const shared_1 = require("../../../cli/commands/shared");
10
+ const index_1 = require("../../../api/observability/index");
11
+ // 时间解析助手已抽到 utils/time.ts;这里 re-export 维持 observability handler 的旧 import 路径。
12
+ var time_1 = require("../../../utils/time");
13
+ Object.defineProperty(exports, "parseTimeToMs", { enumerable: true, get: function () { return time_1.parseTimeToMs; } });
14
+ Object.defineProperty(exports, "parseToMs", { enumerable: true, get: function () { return time_1.parseToMs; } });
15
+ Object.defineProperty(exports, "parseToNs", { enumerable: true, get: function () { return time_1.parseToNs; } });
16
+ Object.defineProperty(exports, "parseToSec", { enumerable: true, get: function () { return time_1.parseToSec; } });
17
+ Object.defineProperty(exports, "msToNs", { enumerable: true, get: function () { return time_1.msToNs; } });
18
+ Object.defineProperty(exports, "msToSec", { enumerable: true, get: function () { return time_1.msToSec; } });
19
+ Object.defineProperty(exports, "floorMsToBucket", { enumerable: true, get: function () { return time_1.floorMsToBucket; } });
20
+ Object.defineProperty(exports, "ceilMsToBucket", { enumerable: true, get: function () { return time_1.ceilMsToBucket; } });
21
+ // ── filter 构造 ──
22
+ //
23
+ // BAM FieldFilter 是「类型分桶 + 算子」结构(str/i64/double),不是扁平字段;
24
+ // 字符串字段无 server-side contains,模糊匹配走 fuzzyFilter。
25
+ /** 字符串字段等值过滤 → { str: { eq } } */
26
+ function eqFilter(value, type = "str") {
27
+ const v = type !== "str" ? Number(value) : value;
28
+ return { [type]: { eq: v } };
29
+ }
30
+ /** i64 数值范围过滤 → { i64: { gte, lte } };输入接受 string(CLI flag)或 number */
31
+ function rangeFilter(opts) {
32
+ const i64 = {};
33
+ if (opts.gte !== undefined)
34
+ i64.gte = Number(opts.gte);
35
+ if (opts.lte !== undefined)
36
+ i64.lte = Number(opts.lte);
37
+ return { i64 };
38
+ }
39
+ /** 模糊搜索过滤器;默认 AND 运算符 */
40
+ function fuzzyFilter(keyword, operator = index_1.WordOperator.AND) {
41
+ return { include: { words: [keyword], operator } };
42
+ }
43
+ /**
44
+ * 把若干条件按 key 装到 fieldFilters。值为 undefined 的条目跳过;
45
+ * 全部跳过时返回 undefined(避免传空 map)。
46
+ */
47
+ function buildFieldFilters(entries) {
48
+ const out = {};
49
+ let added = false;
50
+ for (const { key, value } of entries) {
51
+ if (value === undefined)
52
+ continue;
53
+ out[key] = value;
54
+ added = true;
55
+ }
56
+ return added ? out : undefined;
57
+ }
58
+ // ── 校验 ──
59
+ /** limit 校验(API 限制 1~100) */
60
+ function validateLimit(limit, defaultLimit = 50) {
61
+ const n = Number.isFinite(limit) ? limit : defaultLimit;
62
+ if (n <= 0 || n > 100) {
63
+ (0, shared_1.failArgs)(`--limit 必须在 1~100 之间,收到 ${String(n)}`);
64
+ }
65
+ return n;
66
+ }
@@ -0,0 +1,12 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.handleObservabilityAnalytics = exports.handleObservabilityMetric = exports.handleObservabilityTraceGet = exports.handleObservabilityTraceList = exports.handleObservabilityLog = void 0;
4
+ var log_1 = require("./log");
5
+ Object.defineProperty(exports, "handleObservabilityLog", { enumerable: true, get: function () { return log_1.handleObservabilityLog; } });
6
+ var trace_1 = require("./trace");
7
+ Object.defineProperty(exports, "handleObservabilityTraceList", { enumerable: true, get: function () { return trace_1.handleObservabilityTraceList; } });
8
+ Object.defineProperty(exports, "handleObservabilityTraceGet", { enumerable: true, get: function () { return trace_1.handleObservabilityTraceGet; } });
9
+ var metric_1 = require("./metric");
10
+ Object.defineProperty(exports, "handleObservabilityMetric", { enumerable: true, get: function () { return metric_1.handleObservabilityMetric; } });
11
+ var analytics_1 = require("./analytics");
12
+ Object.defineProperty(exports, "handleObservabilityAnalytics", { enumerable: true, get: function () { return analytics_1.handleObservabilityAnalytics; } });
@@ -0,0 +1,94 @@
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.handleObservabilityLog = handleObservabilityLog;
37
+ const api = __importStar(require("../../../api/index"));
38
+ const output_1 = require("../../../utils/output");
39
+ const shared_1 = require("../../../cli/commands/shared");
40
+ const index_1 = require("../../../api/observability/index");
41
+ const helpers_1 = require("./helpers");
42
+ /** miaoda observability log */
43
+ async function handleObservabilityLog(opts) {
44
+ const appID = (0, shared_1.resolveAppId)({ appId: opts.appId });
45
+ const appEnv = "runtime";
46
+ const limit = (0, helpers_1.validateLimit)(opts.limit ?? 50);
47
+ // 过滤 key 对齐 BAM LogItem 字段名:
48
+ // - 顶层字段:severityText / traceID(注意大小写)
49
+ // - attributes 里的业务字段:module / user_id / page / api(snake_case,
50
+ // 依据 BAM IDL 的 LogItem.attributes 描述:"包括 tenant_id, module, user_id, page, api 等")
51
+ // - duration_ms 暂未在 BAM 描述里明确列出,留 TODO 等 e2e 验证
52
+ const fieldFilters = (0, helpers_1.buildFieldFilters)([
53
+ { key: "severity_text", value: opts.level ? (0, helpers_1.eqFilter)(opts.level) : undefined },
54
+ { key: "trace_id", value: opts.traceId ? (0, helpers_1.eqFilter)(opts.traceId) : undefined },
55
+ { key: "module", value: opts.module ? (0, helpers_1.eqFilter)(opts.module) : undefined },
56
+ { key: "user_id", value: opts.userId ? (0, helpers_1.eqFilter)(opts.userId, "i64") : undefined },
57
+ { key: "page", value: opts.page ? (0, helpers_1.eqFilter)(opts.page) : undefined },
58
+ { key: "api", value: opts.api ? (0, helpers_1.eqFilter)(opts.api) : undefined },
59
+ {
60
+ // TODO: 确认 BAM 是否真用 duration_ms 作为 attribute key
61
+ key: "duration_ms",
62
+ value: opts.minDuration !== undefined || opts.maxDuration !== undefined
63
+ ? (0, helpers_1.rangeFilter)({ gte: opts.minDuration, lte: opts.maxDuration })
64
+ : undefined,
65
+ },
66
+ ]);
67
+ const fuzzyFilter = opts.grep
68
+ ? {
69
+ include: {
70
+ words: [opts.grep],
71
+ operator: 1, // AND
72
+ },
73
+ }
74
+ : undefined;
75
+ const req = {
76
+ appID,
77
+ appEnv,
78
+ startTimestampNs: (0, helpers_1.parseToNs)(opts.since),
79
+ endTimestampNs: (0, helpers_1.parseToNs)(opts.until),
80
+ limit,
81
+ pageToken: opts.cursor,
82
+ fieldFilters,
83
+ fuzzyFilter,
84
+ };
85
+ const resp = await api.observability.searchLogs(req);
86
+ emitSearchLogsResponse(resp);
87
+ }
88
+ function emitSearchLogsResponse(resp) {
89
+ (0, output_1.emit)({
90
+ data: resp.logItems ?? [],
91
+ next_cursor: resp.hasMore ? resp.nextPageToken : null,
92
+ has_more: resp.hasMore,
93
+ }, index_1.logItemSchema);
94
+ }
@@ -0,0 +1,208 @@
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.handleObservabilityMetric = handleObservabilityMetric;
37
+ const api = __importStar(require("../../../api/index"));
38
+ const output_1 = require("../../../utils/output");
39
+ const shared_1 = require("../../../cli/commands/shared");
40
+ const helpers_1 = require("./helpers");
41
+ /**
42
+ * PRD <metric-name> + --series → 服务端 metric name → 表头列名(=对应 --series 取值)。
43
+ *
44
+ * - 缺省 --series 时返回该 cliName 下所有相关线。
45
+ * - latency / requests 的 error 线 v1.0.122 起由独立 metric 提供(不再 is_error filter)。
46
+ *
47
+ * 表头标签直接用 --series 名("total"/"error"/"p50"/"p99");单一指标则用 cliName。
48
+ */
49
+ const METRIC_LABELS = {
50
+ cpu: { cpu_usage: "cpu" },
51
+ memory: { mem_usage: "memory" },
52
+ requests: {
53
+ client_api_request_count: "total",
54
+ client_api_request_error_count: "error",
55
+ },
56
+ latency: {
57
+ client_api_request_latency_p50: "p50",
58
+ client_api_request_latency_p99: "p99",
59
+ },
60
+ };
61
+ /** miaoda observability metric <metric-name> */
62
+ async function handleObservabilityMetric(opts) {
63
+ if (!opts.metricName)
64
+ (0, shared_1.failArgs)("<metric-name> 必填");
65
+ const appID = (0, shared_1.resolveAppId)({ appId: opts.appId });
66
+ const { metricNames, labelByMetric, extraFilters } = resolveMetricSelection(opts.metricName, opts.series);
67
+ const filters = (0, helpers_1.buildFieldFilters)([
68
+ { key: "referer_path", value: opts.page ? (0, helpers_1.eqFilter)(opts.page) : undefined },
69
+ { key: "api", value: opts.api ? (0, helpers_1.eqFilter)(opts.api) : undefined },
70
+ ...extraFilters,
71
+ ]);
72
+ // since/until 直接以秒透传到 BAM;当前未做桶对齐(如需开启把 until
73
+ // 用 ceilMsToBucket(untilMs, downSample as GranularityBucket) 包一层即可)。
74
+ const downSample = opts.downSample ?? "1h";
75
+ const nowMs = Date.now();
76
+ const sinceMs = (0, helpers_1.parseToMs)(opts.since) ?? nowMs - 30 * 86_400_000;
77
+ const untilMs = (0, helpers_1.parseToMs)(opts.until) ?? nowMs;
78
+ const req = {
79
+ appID,
80
+ appEnv: "runtime",
81
+ metricNames,
82
+ startTimestamp: (0, helpers_1.msToSec)(sinceMs),
83
+ endTimestamp: (0, helpers_1.msToSec)(untilMs),
84
+ filters,
85
+ downSample,
86
+ needPackLackPoint: false,
87
+ };
88
+ const resp = await api.observability.queryMetricsData(req);
89
+ // requests 的 count / error_count:
90
+ // - BAM 在该时间点无数据时会传 null,或者 values 里干脆缺 key
91
+ // - CLI 按"请求过的 metricNames"逐一兜底为 0,让"没有请求"和"没有数据"
92
+ // 在表格 / JSON 里都显式呈现成 0,避免空格 / 缺字段被误解为缺失
93
+ // latency / cpu / memory 不做这个兜底——p50 没数据 ≠ 延迟为 0。
94
+ if (opts.metricName === "requests") {
95
+ fillRequestedMetricsWithZero(resp, metricNames);
96
+ }
97
+ emitMetricsResponse(resp, labelByMetric);
98
+ }
99
+ /**
100
+ * 按"客户端请求过的 metricNames"兜底缺失/为 null 的值为 0;不去动 BAM 多返回的
101
+ * 其他字段(防御性,避免误改不属于本次查询的数据)。
102
+ */
103
+ function fillRequestedMetricsWithZero(resp, requestedMetricNames) {
104
+ for (const point of resp.points ?? []) {
105
+ for (const name of requestedMetricNames) {
106
+ point.values[name] ??= 0;
107
+ }
108
+ }
109
+ }
110
+ // ── 渲染 ─────────
111
+ //
112
+ // BAM v1.0.123 起返回 points: [{timestamp, dimensions, values: {metricName: v}}]——
113
+ // 服务端已把多 metric 合并到同 timestamp 的 row,下游直接平铺即可。
114
+ // JSON 模式:原样透传 points(保留 BAM 原 metricName 作为 values 的 key)。
115
+ // Pretty 模式:把 values 里的 metricName 重命名成 CLI label(p50/p99/total 等),
116
+ // 再走 buildPivotSchema 渲染。
117
+ function emitMetricsResponse(resp, labelByMetric) {
118
+ const points = resp.points ?? [];
119
+ // metric 不分页,但仍补齐 has_more / next_cursor 以对齐 log/trace 的信封形状
120
+ if ((0, output_1.isJsonMode)()) {
121
+ (0, output_1.emit)({ data: points, next_cursor: null, has_more: false });
122
+ return;
123
+ }
124
+ const { rows, seriesLabels } = renameAndSort(points, labelByMetric);
125
+ if (rows.length === 0) {
126
+ (0, output_1.emit)({ data: [], next_cursor: null, has_more: false });
127
+ return;
128
+ }
129
+ const hasLatency = Object.keys(labelByMetric).some((n) => n.includes("latency"));
130
+ (0, output_1.emit)({ data: rows, next_cursor: null, has_more: false }, buildPivotSchema(seriesLabels, hasLatency));
131
+ }
132
+ function renameAndSort(points, labelByMetric) {
133
+ // 列顺序按 labelByMetric 声明顺序,避免 BAM 返回顺序波动影响表头
134
+ const seriesLabels = Object.values(labelByMetric);
135
+ // TODO: 当 dimensions 非空(groupBy 多 series)时,把 dimensions 也并入 row
136
+ const rows = points.map((point) => {
137
+ const row = { timestamp: point.timestamp };
138
+ for (const [metricName, value] of Object.entries(point.values)) {
139
+ const label = labelByMetric[metricName] ?? metricName;
140
+ row[label] = value;
141
+ }
142
+ return row;
143
+ });
144
+ // 按时间倒序,与 log/trace 的"最新在前"风格一致
145
+ rows.sort((a, b) => b.timestamp - a.timestamp);
146
+ return { rows, seriesLabels };
147
+ }
148
+ function buildPivotSchema(seriesLabels, hasLatency) {
149
+ const valueFormat = hasLatency ? output_1.fmt.durationMs() : undefined;
150
+ return {
151
+ columns: [
152
+ { key: "timestamp", label: "time", format: output_1.fmt.sec() },
153
+ ...seriesLabels.map((label) => ({
154
+ key: label,
155
+ label,
156
+ ...(valueFormat ? { format: valueFormat } : {}),
157
+ })),
158
+ ],
159
+ strict: true,
160
+ };
161
+ }
162
+ /**
163
+ * 解析 <metric-name> + --series 的组合:决定最终 metricNames、表头 label 映射、附加过滤。
164
+ *
165
+ * label 直接用 --series 取值("total"/"error"/"p50"/"p99");单一指标 cliName 也作为 label。
166
+ */
167
+ function resolveMetricSelection(cliName, series) {
168
+ const extras = [];
169
+ if (cliName === "latency") {
170
+ if (series === "p99") {
171
+ return single("client_api_request_latency_p99", "p99", extras);
172
+ }
173
+ if (series === "p50") {
174
+ return single("client_api_request_latency_p50", "p50", extras);
175
+ }
176
+ return all(METRIC_LABELS.latency, extras);
177
+ }
178
+ if (cliName === "requests") {
179
+ // v1.0.122 起 error 线由独立 metric 提供,不再走 is_error filter
180
+ if (series === "total") {
181
+ return single("client_api_request_count", "total", extras);
182
+ }
183
+ if (series === "error") {
184
+ return single("client_api_request_error_count", "error", extras);
185
+ }
186
+ return all(METRIC_LABELS.requests, extras);
187
+ }
188
+ if (cliName === "cpu")
189
+ return all(METRIC_LABELS.cpu, extras);
190
+ if (cliName === "memory")
191
+ return all(METRIC_LABELS.memory, extras);
192
+ // 兜底:CLI 名直接作为 metric name + label
193
+ return single(cliName, cliName, extras);
194
+ }
195
+ function single(metricName, label, extras) {
196
+ return {
197
+ metricNames: [metricName],
198
+ labelByMetric: { [metricName]: label },
199
+ extraFilters: extras,
200
+ };
201
+ }
202
+ function all(labelByMetric, extras) {
203
+ return {
204
+ metricNames: Object.keys(labelByMetric),
205
+ labelByMetric,
206
+ extraFilters: extras,
207
+ };
208
+ }
@@ -0,0 +1,102 @@
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.handleObservabilityTraceList = handleObservabilityTraceList;
37
+ exports.handleObservabilityTraceGet = handleObservabilityTraceGet;
38
+ const api = __importStar(require("../../../api/index"));
39
+ const output_1 = require("../../../utils/output");
40
+ const shared_1 = require("../../../cli/commands/shared");
41
+ const index_1 = require("../../../api/observability/index");
42
+ const helpers_1 = require("./helpers");
43
+ /** miaoda observability trace list */
44
+ async function handleObservabilityTraceList(opts) {
45
+ const appID = (0, shared_1.resolveAppId)({ appId: opts.appId });
46
+ const appEnv = "runtime";
47
+ const limit = (0, helpers_1.validateLimit)(opts.limit ?? 50);
48
+ // 过滤 key 对齐 BAM Span 字段名:
49
+ // - 顶层字段:traceID(注意大小写)
50
+ // - attributes 业务字段:user_id(snake_case)
51
+ // - 入口 span:BAM 没有 entrySpanName fieldFilter,统一走 fuzzyFilter(trace endpoint
52
+ // 默认作用于 span name),所以 --root-span 不进 fieldFilters
53
+ const fieldFilters = (0, helpers_1.buildFieldFilters)([
54
+ { key: "trace_id", value: opts.traceId ? (0, helpers_1.eqFilter)(opts.traceId) : undefined },
55
+ { key: "user_id", value: opts.userId ? (0, helpers_1.eqFilter)(opts.userId, "i64") : undefined },
56
+ ]);
57
+ const req = {
58
+ appID,
59
+ appEnv,
60
+ startTimestampNs: (0, helpers_1.parseToNs)(opts.since),
61
+ endTimestampNs: (0, helpers_1.parseToNs)(opts.until),
62
+ limit,
63
+ pageToken: opts.cursor,
64
+ fieldFilters,
65
+ fuzzyFilter: opts.rootSpan ? (0, helpers_1.fuzzyFilter)(opts.rootSpan) : undefined,
66
+ };
67
+ const resp = await api.observability.searchTraces(req);
68
+ emitSearchTracesResponse(resp);
69
+ }
70
+ function emitSearchTracesResponse(resp) {
71
+ (0, output_1.emit)({
72
+ data: resp.spans ?? [],
73
+ next_cursor: resp.hasMore ? resp.nextPageToken : null,
74
+ has_more: resp.hasMore,
75
+ }, index_1.spanSchema);
76
+ }
77
+ /** miaoda observability trace get <trace-id> */
78
+ async function handleObservabilityTraceGet(opts) {
79
+ if (!opts.traceId)
80
+ (0, shared_1.failArgs)("<trace-id> 必填");
81
+ const appID = (0, shared_1.resolveAppId)({ appId: opts.appId });
82
+ const appEnv = "runtime";
83
+ const resp = await api.observability.getTraces({
84
+ appID,
85
+ appEnv,
86
+ traceID: opts.traceId,
87
+ withLogSeverityCount: opts.withLogSeverityCount,
88
+ });
89
+ emitGetTraceResponse(resp);
90
+ }
91
+ function emitGetTraceResponse(resp) {
92
+ // JSON 模式:保持单条信封语义,便于下游一次解析
93
+ if ((0, output_1.isJsonMode)()) {
94
+ (0, output_1.emit)({
95
+ data: { spans: resp.spans ?? [], isBreak: resp.isBreak },
96
+ });
97
+ return;
98
+ }
99
+ // Pretty 模式:先打头部 key-value(断链状态等元信息),再按 spanSchema 渲染主表
100
+ (0, output_1.emit)({ data: { isBreak: resp.isBreak } });
101
+ (0, output_1.emit)({ data: resp.spans ?? [] }, index_1.spanSchema);
102
+ }
@@ -82,7 +82,9 @@ function parsePluginName(input) {
82
82
  function readPackageJson() {
83
83
  const pkgPath = getPackageJsonPath();
84
84
  if (!node_fs_1.default.existsSync(pkgPath)) {
85
- throw new error_1.AppError("PKG_JSON_NOT_FOUND", "package.json not found in current directory", { next_actions: ["在应用项目根目录运行"] });
85
+ throw new error_1.AppError("PKG_JSON_NOT_FOUND", "package.json not found in current directory", {
86
+ next_actions: ["在应用项目根目录运行"],
87
+ });
86
88
  }
87
89
  const content = node_fs_1.default.readFileSync(pkgPath, "utf-8");
88
90
  return JSON.parse(content);
@@ -189,9 +191,14 @@ function installMissingDeps(deps) {
189
191
  if (deps.length === 0)
190
192
  return;
191
193
  (0, logger_1.log)("plugin", `Installing missing dependencies: ${deps.join(", ")}`);
192
- const result = (0, node_child_process_1.spawnSync)("npm", ["install", ...deps, "--no-save", "--no-package-lock"], { cwd: getProjectRoot(), stdio: "inherit" });
194
+ const result = (0, node_child_process_1.spawnSync)("npm", ["install", ...deps, "--no-save", "--no-package-lock"], {
195
+ cwd: getProjectRoot(),
196
+ stdio: "inherit",
197
+ });
193
198
  if (result.error) {
194
- throw new error_1.AppError("INTERNAL_NPM_FAILED", `npm install failed: ${result.error.message}`, { next_actions: ["确认本机已安装 npm,可 --verbose 查看执行详情"] });
199
+ throw new error_1.AppError("INTERNAL_NPM_FAILED", `npm install failed: ${result.error.message}`, {
200
+ next_actions: ["确认本机已安装 npm,可 --verbose 查看执行详情"],
201
+ });
195
202
  }
196
203
  if (result.status !== 0) {
197
204
  throw new error_1.AppError("INTERNAL_NPM_FAILED", `npm install failed with exit code ${String(result.status)}`, { next_actions: ["检查上方 npm 输出日志定位具体错误"] });
@@ -200,7 +207,9 @@ function installMissingDeps(deps) {
200
207
  function npmInstall(tgzPath) {
201
208
  const result = (0, node_child_process_1.spawnSync)("npm", ["install", tgzPath, "--no-save", "--no-package-lock", "--ignore-scripts"], { cwd: getProjectRoot(), stdio: "inherit" });
202
209
  if (result.error) {
203
- throw new error_1.AppError("INTERNAL_NPM_FAILED", `npm install failed: ${result.error.message}`, { next_actions: ["确认本机已安装 npm,可 --verbose 查看执行详情"] });
210
+ throw new error_1.AppError("INTERNAL_NPM_FAILED", `npm install failed: ${result.error.message}`, {
211
+ next_actions: ["确认本机已安装 npm,可 --verbose 查看执行详情"],
212
+ });
204
213
  }
205
214
  if (result.status !== 0) {
206
215
  throw new error_1.AppError("INTERNAL_NPM_FAILED", `npm install failed with exit code ${String(result.status)}`, { next_actions: ["检查上方 npm 输出日志定位具体错误"] });
@@ -232,7 +241,9 @@ function listCapabilityIds() {
232
241
  function readCapability(id) {
233
242
  const filePath = node_path_1.default.join(getCapabilitiesDir(), `${id}.json`);
234
243
  if (!node_fs_1.default.existsSync(filePath)) {
235
- throw new error_1.AppError("CAPABILITY_NOT_FOUND", `Capability not found: ${id}`, { next_actions: ["运行 miaoda plugin list 查看所有可用 capability id"] });
244
+ throw new error_1.AppError("CAPABILITY_NOT_FOUND", `Capability not found: ${id}`, {
245
+ next_actions: ["运行 miaoda plugin list 查看所有可用 capability id"],
246
+ });
236
247
  }
237
248
  try {
238
249
  const content = node_fs_1.default.readFileSync(filePath, "utf-8");
@@ -240,7 +251,9 @@ function readCapability(id) {
240
251
  }
241
252
  catch (error) {
242
253
  if (error instanceof SyntaxError) {
243
- throw new error_1.AppError("INVALID_JSON", `Invalid JSON in capability file: ${id}.json`, { next_actions: [`检查 server/capabilities/${id}.json 的 JSON 语法`] });
254
+ throw new error_1.AppError("INVALID_JSON", `Invalid JSON in capability file: ${id}.json`, {
255
+ next_actions: [`检查 server/capabilities/${id}.json 的 JSON 语法`],
256
+ });
244
257
  }
245
258
  throw error;
246
259
  }
@@ -303,7 +316,9 @@ async function loadPlugin(pluginKey) {
303
316
  }
304
317
  catch (error) {
305
318
  if (error.code === "MODULE_NOT_FOUND") {
306
- throw new error_1.AppError("PLUGIN_NOT_FOUND", `Plugin not installed: ${pluginKey}`, { next_actions: [`运行 miaoda plugin install ${pluginKey}`] });
319
+ throw new error_1.AppError("PLUGIN_NOT_FOUND", `Plugin not installed: ${pluginKey}`, {
320
+ next_actions: [`运行 miaoda plugin install ${pluginKey}`],
321
+ });
307
322
  }
308
323
  throw new error_1.AppError("INTERNAL_PLUGIN_LOAD_FAILED", `Failed to load plugin ${pluginKey}: ${error instanceof Error ? error.message : String(error)}`);
309
324
  }
@@ -314,8 +329,7 @@ async function hydrateCapability(capability) {
314
329
  if (manifest.actions.length === 0) {
315
330
  throw new error_1.AppError("INTERNAL_PLUGIN_LOAD_FAILED", `Plugin ${capability.pluginKey} has no actions defined`);
316
331
  }
317
- const hasDynamic = manifest.actions.some((action) => isDynamicSchema(action.inputSchema) ||
318
- isDynamicSchema(action.outputSchema));
332
+ const hasDynamic = manifest.actions.some((action) => isDynamicSchema(action.inputSchema) || isDynamicSchema(action.outputSchema));
319
333
  let pluginInstance = null;
320
334
  if (hasDynamic) {
321
335
  const plugin = await loadPlugin(capability.pluginKey);
@@ -44,7 +44,9 @@ const output_1 = require("../../../utils/output");
44
44
  const error_1 = require("../../../utils/error");
45
45
  const logger_1 = require("../../../utils/logger");
46
46
  const plugin_local_1 = require("./plugin-local");
47
- const log = (msg) => { (0, logger_1.log)("plugin", msg); };
47
+ const log = (msg) => {
48
+ (0, logger_1.log)("plugin", msg);
49
+ };
48
50
  // ── Install ──
49
51
  function syncActionPluginsRecord(name, version) {
50
52
  const plugins = (0, plugin_local_1.readActionPlugins)();
@@ -63,7 +65,9 @@ async function installOne(nameWithVersion) {
63
65
  if (actualVersion === requestedVersion) {
64
66
  log(`${name}@${requestedVersion} already installed`);
65
67
  syncActionPluginsRecord(name, actualVersion);
66
- api.plugin.reportCreateInstanceEvent(name, actualVersion).catch(() => { });
68
+ api.plugin.reportCreateInstanceEvent(name, actualVersion).catch(() => {
69
+ /* fire-and-forget */
70
+ });
67
71
  return { name, version: actualVersion, success: true, skipped: true };
68
72
  }
69
73
  }
@@ -74,7 +78,9 @@ async function installOne(nameWithVersion) {
74
78
  if (actualVersion === targetVersion) {
75
79
  log(`${name} already up to date (${actualVersion})`);
76
80
  syncActionPluginsRecord(name, actualVersion);
77
- api.plugin.reportCreateInstanceEvent(name, actualVersion).catch(() => { });
81
+ api.plugin.reportCreateInstanceEvent(name, actualVersion).catch(() => {
82
+ /* fire-and-forget */
83
+ });
78
84
  return { name, version: actualVersion, success: true, skipped: true };
79
85
  }
80
86
  log(`Found newer version: ${targetVersion} (installed: ${actualVersion ?? "none"})`);
@@ -106,8 +112,12 @@ async function installOne(nameWithVersion) {
106
112
  (0, plugin_local_1.writeActionPlugins)(plugins);
107
113
  const source = fromCache ? "from cache" : "downloaded";
108
114
  log(`Installed ${name}@${installedVersion} (${source})`);
109
- api.plugin.reportInstallEvent(name, installedVersion).catch(() => { });
110
- api.plugin.reportCreateInstanceEvent(name, installedVersion).catch(() => { });
115
+ api.plugin.reportInstallEvent(name, installedVersion).catch(() => {
116
+ /* fire-and-forget */
117
+ });
118
+ api.plugin.reportCreateInstanceEvent(name, installedVersion).catch(() => {
119
+ /* fire-and-forget */
120
+ });
111
121
  return { name, version: installedVersion, success: true };
112
122
  }
113
123
  catch (error) {
@@ -193,7 +203,9 @@ async function handlePluginUpdate(opts) {
193
203
  function handlePluginRemove(opts) {
194
204
  const { name } = (0, plugin_local_1.parsePluginName)(opts.name);
195
205
  if (!(0, plugin_local_1.isPluginInstalled)(name)) {
196
- throw new error_1.AppError("PLUGIN_NOT_FOUND", `Plugin ${name} is not installed`, { next_actions: ["运行 miaoda plugin list-packages 查看已安装插件"] });
206
+ throw new error_1.AppError("PLUGIN_NOT_FOUND", `Plugin ${name} is not installed`, {
207
+ next_actions: ["运行 miaoda plugin list-packages 查看已安装插件"],
208
+ });
197
209
  }
198
210
  (0, plugin_local_1.removePluginDirectory)(name);
199
211
  const plugins = (0, plugin_local_1.readActionPlugins)();
@@ -263,7 +275,9 @@ async function handlePluginInit() {
263
275
  // ── List (capability configs) ──
264
276
  async function handlePluginList(opts) {
265
277
  if (!(0, plugin_local_1.capabilitiesDirExists)()) {
266
- throw new error_1.AppError("CAPABILITIES_DIR_NOT_FOUND", "server/capabilities directory not found", { next_actions: ["当前目录必须是含 server/capabilities/ 的应用项目"] });
278
+ throw new error_1.AppError("CAPABILITIES_DIR_NOT_FOUND", "server/capabilities directory not found", {
279
+ next_actions: ["当前目录必须是含 server/capabilities/ 的应用项目"],
280
+ });
267
281
  }
268
282
  if (opts.id) {
269
283
  const capability = (0, plugin_local_1.readCapability)(opts.id);
package/dist/cli/help.js CHANGED
@@ -115,7 +115,9 @@ class MiaodaHelp extends commander_1.Help {
115
115
  out.push("Usage:", ` ${helper.commandUsage(cmd)}`, "");
116
116
  // 3. Commands(仅父级命令组有,spec 要求 Commands 在 Flags 前)
117
117
  // spec 不展示 Arguments 段,参数说明放在 description 文本里
118
- const subs = helper.visibleCommands(cmd).map((c) => formatItem(helper.subcommandTerm(c), helper.subcommandDescription(c)));
118
+ const subs = helper
119
+ .visibleCommands(cmd)
120
+ .map((c) => formatItem(helper.subcommandTerm(c), helper.subcommandDescription(c)));
119
121
  if (subs.length) {
120
122
  out.push("Commands:", formatList(subs), "");
121
123
  }
@@ -125,7 +127,8 @@ class MiaodaHelp extends commander_1.Help {
125
127
  // - `-h, --help` 永远不放 Flags 段,统一放 Global Flags(spec 约定)
126
128
  const isParent = subs.length > 0;
127
129
  if (!isRoot && !isParent) {
128
- const opts = helper.visibleOptions(cmd)
130
+ const opts = helper
131
+ .visibleOptions(cmd)
129
132
  .filter((o) => !isHelpOption(o))
130
133
  .map((o) => formatItem(helper.optionTerm(o), helper.optionDescription(o)));
131
134
  if (opts.length) {