@lark-apaas/miaoda-cli 0.1.4 → 0.1.5-beta.27ede41
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 +6 -5
- package/dist/api/deploy/index.js +13 -1
- package/dist/api/deploy/modern-types.js +23 -0
- package/dist/api/deploy/modern.js +78 -0
- package/dist/api/deploy/plugin-instances-types.js +6 -0
- package/dist/api/deploy/plugin-instances.js +22 -0
- package/dist/api/observability/api.js +10 -0
- package/dist/api/observability/index.js +2 -1
- package/dist/cli/commands/app/index.js +84 -2
- package/dist/cli/commands/deploy/modern.js +50 -0
- package/dist/cli/commands/index.js +33 -5
- package/dist/cli/commands/observability/index.js +62 -6
- package/dist/cli/commands/shared.js +5 -0
- package/dist/cli/commands/skills/index.js +63 -0
- package/dist/cli/handlers/app/index.js +6 -1
- package/dist/cli/handlers/app/init.js +88 -0
- package/dist/cli/handlers/deploy/modern.js +33 -0
- package/dist/cli/handlers/observability/helpers.js +4 -0
- package/dist/cli/handlers/observability/index.js +3 -1
- package/dist/cli/handlers/observability/log.js +8 -2
- package/dist/cli/handlers/observability/source-stack.js +389 -0
- package/dist/cli/handlers/observability/trace.js +7 -1
- package/dist/cli/handlers/skills/index.js +7 -0
- package/dist/cli/handlers/skills/status.js +31 -0
- package/dist/cli/handlers/skills/sync.js +38 -0
- package/dist/services/app/init/index.js +12 -0
- package/dist/services/app/init/install.js +101 -0
- package/dist/services/app/init/template.js +87 -0
- package/dist/services/deploy/modern/atoms/build.js +59 -0
- package/dist/services/deploy/modern/atoms/context.js +27 -0
- package/dist/services/deploy/modern/atoms/index.js +17 -0
- package/dist/services/deploy/modern/atoms/local-release.js +27 -0
- package/dist/services/deploy/modern/atoms/pre-release.js +13 -0
- package/dist/services/deploy/modern/atoms/save-plugin-instances.js +72 -0
- package/dist/services/deploy/modern/atoms/upload.js +246 -0
- package/dist/services/deploy/modern/check.js +53 -0
- package/dist/services/deploy/modern/constants.js +13 -0
- package/dist/services/deploy/modern/index.js +16 -0
- package/dist/services/deploy/modern/pipelines/index.js +5 -0
- package/dist/services/deploy/modern/pipelines/local.js +75 -0
- package/dist/services/deploy/modern/protocol.js +122 -0
- package/dist/services/deploy/modern/run-types.js +4 -0
- package/dist/services/deploy/modern/run.js +13 -0
- package/dist/services/deploy/modern/template-key-map.js +22 -0
- package/dist/services/skills/index.js +5 -0
- package/dist/services/skills/status.js +37 -0
- package/dist/utils/coding-steering.js +85 -0
- package/dist/utils/git.js +22 -0
- package/dist/utils/http.js +21 -11
- package/dist/utils/npm-pack.js +55 -0
- package/dist/utils/spark-meta.js +42 -0
- package/package.json +1 -1
|
@@ -1,7 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.handleAppUpdate = exports.handleAppGet = void 0;
|
|
3
|
+
exports.SUPPORTED_STACKS = exports.handleAppInit = exports.handleAppUpdate = exports.handleAppGet = void 0;
|
|
4
4
|
var get_1 = require("./get");
|
|
5
5
|
Object.defineProperty(exports, "handleAppGet", { enumerable: true, get: function () { return get_1.handleAppGet; } });
|
|
6
6
|
var update_1 = require("./update");
|
|
7
7
|
Object.defineProperty(exports, "handleAppUpdate", { enumerable: true, get: function () { return update_1.handleAppUpdate; } });
|
|
8
|
+
var init_1 = require("./init");
|
|
9
|
+
Object.defineProperty(exports, "handleAppInit", { enumerable: true, get: function () { return init_1.handleAppInit; } });
|
|
10
|
+
// commands 层渲染 help 时需要这份枚举;从 handler barrel 转发,避免 commands → services 越界
|
|
11
|
+
var index_1 = require("../../../services/app/init/index");
|
|
12
|
+
Object.defineProperty(exports, "SUPPORTED_STACKS", { enumerable: true, get: function () { return index_1.SUPPORTED_STACKS; } });
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.handleAppInit = handleAppInit;
|
|
7
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
8
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
9
|
+
const index_1 = require("../../../services/app/init/index");
|
|
10
|
+
const coding_steering_1 = require("../../../utils/coding-steering");
|
|
11
|
+
const error_1 = require("../../../utils/error");
|
|
12
|
+
const output_1 = require("../../../utils/output");
|
|
13
|
+
/**
|
|
14
|
+
* miaoda app init
|
|
15
|
+
*
|
|
16
|
+
* 只负责本地脚手架:render template、sync skills、install deps,把 {stack, version, archType}
|
|
17
|
+
* 写入 .spark/meta.json。appId 不在此处持久化——deploy 等运行端通过 MIAODA_APP_ID env 拿。
|
|
18
|
+
*
|
|
19
|
+
* 幂等:.spark/meta.json 存在即表示已完整成功一次,直接退出,不重跑任何子步骤。
|
|
20
|
+
* 半渲染状态(package.json 在但 meta.json 不在)会重新跑完整流程。
|
|
21
|
+
*/
|
|
22
|
+
async function handleAppInit(opts) {
|
|
23
|
+
await Promise.resolve();
|
|
24
|
+
const targetDir = opts.targetDir ?? process.cwd();
|
|
25
|
+
const metaPath = node_path_1.default.join(targetDir, '.spark', 'meta.json');
|
|
26
|
+
if (node_fs_1.default.existsSync(metaPath)) {
|
|
27
|
+
if (!(0, output_1.isJsonMode)()) {
|
|
28
|
+
process.stdout.write(`Already initialized: ${metaPath}\n`);
|
|
29
|
+
}
|
|
30
|
+
(0, output_1.emit)({
|
|
31
|
+
data: {
|
|
32
|
+
initialized: false,
|
|
33
|
+
reason: 'already_initialized',
|
|
34
|
+
targetDir,
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
const stack = opts.template;
|
|
40
|
+
if (!stack) {
|
|
41
|
+
throw new error_1.AppError('ARGS_INVALID', '缺少 --template <stack>', {
|
|
42
|
+
next_actions: [`可用 stack:${index_1.SUPPORTED_STACKS.join(', ')}`],
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
if (!index_1.SUPPORTED_STACKS.includes(stack)) {
|
|
46
|
+
throw new error_1.AppError('ARGS_INVALID', `不支持的 template: ${stack}`, {
|
|
47
|
+
next_actions: [`可用 stack:${index_1.SUPPORTED_STACKS.join(', ')}`],
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
const version = opts.conf?.version;
|
|
51
|
+
const projectName = node_path_1.default.basename(targetDir);
|
|
52
|
+
const tplResult = (0, index_1.renderTemplate)({ stack, version, targetDir, projectName });
|
|
53
|
+
const steeringResult = (0, coding_steering_1.syncCodingSteering)({
|
|
54
|
+
stack,
|
|
55
|
+
targetDir,
|
|
56
|
+
version: opts.conf?.steeringVersion,
|
|
57
|
+
logPrefix: 'init',
|
|
58
|
+
});
|
|
59
|
+
const installResult = (0, index_1.installDependencies)({
|
|
60
|
+
targetDir,
|
|
61
|
+
skip: opts.skipInstall,
|
|
62
|
+
quietStdout: (0, output_1.isJsonMode)(),
|
|
63
|
+
});
|
|
64
|
+
(0, index_1.writeSparkMeta)(targetDir, {
|
|
65
|
+
stack,
|
|
66
|
+
version: tplResult.version,
|
|
67
|
+
archType: tplResult.archType,
|
|
68
|
+
});
|
|
69
|
+
if (!(0, output_1.isJsonMode)()) {
|
|
70
|
+
process.stdout.write(`✓ Initialized ${stack} (template ${tplResult.version}, steering ${steeringResult.version}, install ${installResult.source}) in ${targetDir}\n`);
|
|
71
|
+
}
|
|
72
|
+
(0, output_1.emit)({
|
|
73
|
+
data: {
|
|
74
|
+
initialized: true,
|
|
75
|
+
template: stack,
|
|
76
|
+
templatePackage: tplResult.packageName,
|
|
77
|
+
templateVersion: tplResult.version,
|
|
78
|
+
steeringVersion: steeringResult.version,
|
|
79
|
+
archType: tplResult.archType,
|
|
80
|
+
syncedSkills: steeringResult.syncedSkills,
|
|
81
|
+
techSynced: steeringResult.techSynced,
|
|
82
|
+
installed: installResult.installed,
|
|
83
|
+
installSource: installResult.source,
|
|
84
|
+
installHash: installResult.hash,
|
|
85
|
+
cacheZip: installResult.cacheZip,
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.handleDeployModern = handleDeployModern;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const output_1 = require("../../../utils/output");
|
|
9
|
+
const index_1 = require("../../../services/deploy/modern/index");
|
|
10
|
+
/**
|
|
11
|
+
* miaoda deploy(modern scene 专用,CLI 表面对齐 openclaw-cli)
|
|
12
|
+
*
|
|
13
|
+
* 进程信息走 stderr(log_id / step),最终结果通过 emit 输出 envelope 到 stdout。
|
|
14
|
+
*/
|
|
15
|
+
async function handleDeployModern(opts) {
|
|
16
|
+
const projectDir = node_path_1.default.resolve(opts.dir);
|
|
17
|
+
const result = await (0, index_1.runModernDeploy)({
|
|
18
|
+
projectDir,
|
|
19
|
+
appId: opts.appId,
|
|
20
|
+
skipBuild: opts.skipBuild ?? false,
|
|
21
|
+
});
|
|
22
|
+
(0, output_1.emit)({
|
|
23
|
+
data: {
|
|
24
|
+
appId: result.appId,
|
|
25
|
+
version: result.version,
|
|
26
|
+
url: result.url,
|
|
27
|
+
releaseID: result.releaseID,
|
|
28
|
+
preReleaseID: result.preReleaseID,
|
|
29
|
+
},
|
|
30
|
+
next_cursor: null,
|
|
31
|
+
has_more: false,
|
|
32
|
+
});
|
|
33
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.ceilMsToBucket = exports.floorMsToBucket = exports.msToSec = exports.msToNs = exports.parseToSec = exports.parseToNs = exports.parseToMs = exports.parseTimeToMs = void 0;
|
|
4
4
|
exports.eqFilter = eqFilter;
|
|
5
|
+
exports.inFilter = inFilter;
|
|
5
6
|
exports.rangeFilter = rangeFilter;
|
|
6
7
|
exports.fuzzyFilter = fuzzyFilter;
|
|
7
8
|
exports.buildFieldFilters = buildFieldFilters;
|
|
@@ -27,6 +28,9 @@ function eqFilter(value, type = 'str') {
|
|
|
27
28
|
const v = type !== 'str' ? Number(value) : value;
|
|
28
29
|
return { [type]: { eq: v } };
|
|
29
30
|
}
|
|
31
|
+
function inFilter(strs) {
|
|
32
|
+
return { str: { in: strs } };
|
|
33
|
+
}
|
|
30
34
|
/** i64 数值范围过滤 → { i64: { gte, lte } };输入接受 string(CLI flag)或 number */
|
|
31
35
|
function rangeFilter(opts) {
|
|
32
36
|
const i64 = {};
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.handleObservabilityAnalytics = exports.handleObservabilityMetric = exports.handleObservabilityTraceGet = exports.handleObservabilityTraceList = exports.handleObservabilityLog = void 0;
|
|
3
|
+
exports.handleObservabilityAnalytics = exports.handleObservabilityMetric = exports.handleObservabilitySourceStack = exports.handleObservabilityTraceGet = exports.handleObservabilityTraceList = exports.handleObservabilityLog = void 0;
|
|
4
4
|
var log_1 = require("./log");
|
|
5
5
|
Object.defineProperty(exports, "handleObservabilityLog", { enumerable: true, get: function () { return log_1.handleObservabilityLog; } });
|
|
6
6
|
var trace_1 = require("./trace");
|
|
7
7
|
Object.defineProperty(exports, "handleObservabilityTraceList", { enumerable: true, get: function () { return trace_1.handleObservabilityTraceList; } });
|
|
8
8
|
Object.defineProperty(exports, "handleObservabilityTraceGet", { enumerable: true, get: function () { return trace_1.handleObservabilityTraceGet; } });
|
|
9
|
+
var source_stack_1 = require("./source-stack");
|
|
10
|
+
Object.defineProperty(exports, "handleObservabilitySourceStack", { enumerable: true, get: function () { return source_stack_1.handleObservabilitySourceStack; } });
|
|
9
11
|
var metric_1 = require("./metric");
|
|
10
12
|
Object.defineProperty(exports, "handleObservabilityMetric", { enumerable: true, get: function () { return metric_1.handleObservabilityMetric; } });
|
|
11
13
|
var analytics_1 = require("./analytics");
|
|
@@ -38,6 +38,12 @@ const api = __importStar(require("../../../api/index"));
|
|
|
38
38
|
const output_1 = require("../../../utils/output");
|
|
39
39
|
const index_1 = require("../../../api/observability/index");
|
|
40
40
|
const helpers_1 = require("./helpers");
|
|
41
|
+
function stringIdFilter(value) {
|
|
42
|
+
if (Array.isArray(value)) {
|
|
43
|
+
return value.length > 0 ? (0, helpers_1.inFilter)(value) : undefined;
|
|
44
|
+
}
|
|
45
|
+
return value ? (0, helpers_1.eqFilter)(value) : undefined;
|
|
46
|
+
}
|
|
41
47
|
/** miaoda observability log */
|
|
42
48
|
async function handleObservabilityLog(opts) {
|
|
43
49
|
const appID = opts.appId;
|
|
@@ -50,8 +56,8 @@ async function handleObservabilityLog(opts) {
|
|
|
50
56
|
// - duration_ms 暂未在 BAM 描述里明确列出,留 TODO 等 e2e 验证
|
|
51
57
|
const fieldFilters = (0, helpers_1.buildFieldFilters)([
|
|
52
58
|
{ key: 'severity_text', value: opts.level ? (0, helpers_1.eqFilter)(opts.level) : undefined },
|
|
53
|
-
{ key: 'attributes.ob_data_id', value:
|
|
54
|
-
{ key: 'trace_id', value:
|
|
59
|
+
{ key: 'attributes.ob_data_id', value: stringIdFilter(opts.logId) },
|
|
60
|
+
{ key: 'trace_id', value: stringIdFilter(opts.traceId) },
|
|
55
61
|
{ key: 'module', value: opts.module ? (0, helpers_1.eqFilter)(opts.module) : undefined },
|
|
56
62
|
{ key: 'user_id', value: opts.userId ? (0, helpers_1.eqFilter)(opts.userId, 'i64') : undefined },
|
|
57
63
|
{ key: 'page', value: opts.page ? (0, helpers_1.eqFilter)(opts.page) : undefined },
|
|
@@ -0,0 +1,389 @@
|
|
|
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.handleObservabilitySourceStack = handleObservabilitySourceStack;
|
|
37
|
+
const api = __importStar(require("../../../api/index"));
|
|
38
|
+
const output_1 = require("../../../utils/output");
|
|
39
|
+
const args_1 = require("../../../utils/args");
|
|
40
|
+
const helpers_1 = require("./helpers");
|
|
41
|
+
const SOURCE_MAP_FILE_PREFIX = 'client/assets/';
|
|
42
|
+
/** miaoda observability source-stack */
|
|
43
|
+
async function handleObservabilitySourceStack(opts) {
|
|
44
|
+
const logIds = normalizeValues(opts.logId);
|
|
45
|
+
const traceIds = normalizeValues(opts.traceId);
|
|
46
|
+
if (logIds.length === 0 && traceIds.length === 0) {
|
|
47
|
+
(0, args_1.failArgs)('至少传入线上环境日志 ID 或 trace ID');
|
|
48
|
+
}
|
|
49
|
+
const logs = await collectCandidateLogs(opts, { logIds, traceIds });
|
|
50
|
+
const results = await resolveCandidateLogs(opts.appId, logs);
|
|
51
|
+
(0, output_1.emit)({ data: results, next_cursor: null, has_more: false });
|
|
52
|
+
}
|
|
53
|
+
async function collectCandidateLogs(opts, params) {
|
|
54
|
+
const limit = (0, helpers_1.validateLimit)(opts.limit ?? 50);
|
|
55
|
+
const searches = [];
|
|
56
|
+
if (params.logIds.length > 0) {
|
|
57
|
+
searches.push(searchLogs(opts, {
|
|
58
|
+
limit,
|
|
59
|
+
fieldFilters: (0, helpers_1.buildFieldFilters)([
|
|
60
|
+
{ key: 'attributes.ob_data_id', value: stringIdFilter(params.logIds) },
|
|
61
|
+
]),
|
|
62
|
+
}));
|
|
63
|
+
}
|
|
64
|
+
if (params.traceIds.length > 0) {
|
|
65
|
+
searches.push(searchLogs(opts, {
|
|
66
|
+
limit,
|
|
67
|
+
fieldFilters: (0, helpers_1.buildFieldFilters)([
|
|
68
|
+
{ key: 'trace_id', value: stringIdFilter(params.traceIds) },
|
|
69
|
+
]),
|
|
70
|
+
}));
|
|
71
|
+
}
|
|
72
|
+
const allLogs = (await Promise.all(searches)).flat();
|
|
73
|
+
const deduped = new Map();
|
|
74
|
+
for (const log of allLogs) {
|
|
75
|
+
deduped.set(log.id, log);
|
|
76
|
+
}
|
|
77
|
+
return Array.from(deduped.values());
|
|
78
|
+
}
|
|
79
|
+
async function searchLogs(opts, params) {
|
|
80
|
+
const req = {
|
|
81
|
+
appID: opts.appId,
|
|
82
|
+
appEnv: 'runtime',
|
|
83
|
+
startTimestampNs: (0, helpers_1.parseToNs)(opts.since),
|
|
84
|
+
endTimestampNs: (0, helpers_1.parseToNs)(opts.until),
|
|
85
|
+
limit: params.limit,
|
|
86
|
+
fieldFilters: params.fieldFilters,
|
|
87
|
+
};
|
|
88
|
+
const resp = await api.observability.searchLogs(req);
|
|
89
|
+
return resp.logItems ?? [];
|
|
90
|
+
}
|
|
91
|
+
async function resolveCandidateLogs(appID, logs) {
|
|
92
|
+
const results = [];
|
|
93
|
+
for (const log of logs) {
|
|
94
|
+
const attrs = log.attributes ?? {};
|
|
95
|
+
const rawStack = pickAttr(attrs, ['stack', 'error_stack', 'errorStack']) ??
|
|
96
|
+
pickBodyStack(log.body) ??
|
|
97
|
+
log.body;
|
|
98
|
+
const frames = parseGeneratedFrames(rawStack);
|
|
99
|
+
const commitID = pickAttr(attrs, [
|
|
100
|
+
'release_commit_id',
|
|
101
|
+
'releaseCommitID',
|
|
102
|
+
'commit_id',
|
|
103
|
+
'commitID',
|
|
104
|
+
]);
|
|
105
|
+
const message = pickErrorMessage(log, rawStack);
|
|
106
|
+
const base = {
|
|
107
|
+
log_id: log.id,
|
|
108
|
+
trace_id: log.traceID ?? attrs.trace_id,
|
|
109
|
+
commit_id: commitID,
|
|
110
|
+
source_map_files: sourceMapFilesForFrames(frames),
|
|
111
|
+
};
|
|
112
|
+
const reason = missingReason({ frames, commitID });
|
|
113
|
+
if (reason) {
|
|
114
|
+
results.push({
|
|
115
|
+
...base,
|
|
116
|
+
status: 'unresolved',
|
|
117
|
+
reason,
|
|
118
|
+
error_message_stack: formatStack(message, frames),
|
|
119
|
+
});
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
const resolvedCommitID = commitID;
|
|
123
|
+
if (!resolvedCommitID)
|
|
124
|
+
continue;
|
|
125
|
+
let resolvedFrames;
|
|
126
|
+
try {
|
|
127
|
+
resolvedFrames = await resolveFramesBySourceMap(appID, resolvedCommitID, frames);
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
results.push({
|
|
131
|
+
...base,
|
|
132
|
+
status: 'unresolved',
|
|
133
|
+
reason: `调用 resolve_stack_trace 失败:${errorMessage(err)}`,
|
|
134
|
+
error_message_stack: formatStack(message, frames),
|
|
135
|
+
});
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
results.push({
|
|
139
|
+
...base,
|
|
140
|
+
status: 'resolved',
|
|
141
|
+
error_message_stack: formatStack(message, resolvedFrames),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
return results;
|
|
145
|
+
}
|
|
146
|
+
function normalizeValues(value) {
|
|
147
|
+
if (Array.isArray(value)) {
|
|
148
|
+
return value.filter((item) => item.length > 0);
|
|
149
|
+
}
|
|
150
|
+
return value ? [value] : [];
|
|
151
|
+
}
|
|
152
|
+
function stringIdFilter(values) {
|
|
153
|
+
if (values.length === 0)
|
|
154
|
+
return undefined;
|
|
155
|
+
return values.length === 1 ? (0, helpers_1.eqFilter)(values[0]) : (0, helpers_1.inFilter)(values);
|
|
156
|
+
}
|
|
157
|
+
function pickAttr(attrs, keys) {
|
|
158
|
+
for (const key of keys) {
|
|
159
|
+
const value = attrs[key];
|
|
160
|
+
if (value)
|
|
161
|
+
return value;
|
|
162
|
+
}
|
|
163
|
+
return undefined;
|
|
164
|
+
}
|
|
165
|
+
async function resolveFramesBySourceMap(appID, commitID, frames) {
|
|
166
|
+
const resp = await api.observability.resolveStackTrace({
|
|
167
|
+
appID,
|
|
168
|
+
commitID,
|
|
169
|
+
sourceMapFilePrefix: SOURCE_MAP_FILE_PREFIX,
|
|
170
|
+
frames,
|
|
171
|
+
});
|
|
172
|
+
return resp.frames ?? frames;
|
|
173
|
+
}
|
|
174
|
+
function parseGeneratedFrames(stack) {
|
|
175
|
+
if (!stack)
|
|
176
|
+
return [];
|
|
177
|
+
const jsonFrames = parseJsonStackFrames(stack);
|
|
178
|
+
if (jsonFrames.length > 0)
|
|
179
|
+
return jsonFrames;
|
|
180
|
+
const frames = [];
|
|
181
|
+
const parenFramePattern = /\((.+):(\d+):(\d+)\)$/;
|
|
182
|
+
const plainFramePattern = /(?:at\s+)?(.+):(\d+):(\d+)$/;
|
|
183
|
+
for (const line of stack.split('\n')) {
|
|
184
|
+
const trimmed = line.trim();
|
|
185
|
+
const match = parenFramePattern.exec(trimmed) ?? plainFramePattern.exec(trimmed);
|
|
186
|
+
if (!match)
|
|
187
|
+
continue;
|
|
188
|
+
const [, fileName, lineNumber, columnNumber] = match;
|
|
189
|
+
if (!fileName || !lineNumber || !columnNumber)
|
|
190
|
+
continue;
|
|
191
|
+
frames.push({
|
|
192
|
+
fileName: normalizeFrameFileName(fileName),
|
|
193
|
+
line: Number(lineNumber),
|
|
194
|
+
column: Number(columnNumber),
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
return frames.slice(0, 2000);
|
|
198
|
+
}
|
|
199
|
+
function parseJsonStackFrames(stack) {
|
|
200
|
+
const trimmed = stack.trim();
|
|
201
|
+
if (!trimmed.startsWith('['))
|
|
202
|
+
return [];
|
|
203
|
+
let parsed;
|
|
204
|
+
try {
|
|
205
|
+
parsed = JSON.parse(trimmed);
|
|
206
|
+
}
|
|
207
|
+
catch {
|
|
208
|
+
return [];
|
|
209
|
+
}
|
|
210
|
+
if (!Array.isArray(parsed))
|
|
211
|
+
return [];
|
|
212
|
+
return parsed
|
|
213
|
+
.map((item) => normalizeJsonFrame(item))
|
|
214
|
+
.filter((frame) => Boolean(frame))
|
|
215
|
+
.slice(0, 2000);
|
|
216
|
+
}
|
|
217
|
+
function normalizeJsonFrame(item) {
|
|
218
|
+
if (!isRecord(item))
|
|
219
|
+
return undefined;
|
|
220
|
+
const fileName = item.fileName;
|
|
221
|
+
const line = item.line;
|
|
222
|
+
const column = item.column;
|
|
223
|
+
if (typeof fileName !== 'string')
|
|
224
|
+
return undefined;
|
|
225
|
+
const lineNumber = toFiniteNumber(line);
|
|
226
|
+
const columnNumber = toFiniteNumber(column);
|
|
227
|
+
if (lineNumber === undefined || columnNumber === undefined)
|
|
228
|
+
return undefined;
|
|
229
|
+
return {
|
|
230
|
+
fileName: normalizeFrameFileName(fileName),
|
|
231
|
+
line: lineNumber,
|
|
232
|
+
column: columnNumber,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
function pickErrorMessage(log, rawStack) {
|
|
236
|
+
return (pickBodyErrorMessage(log.body) ??
|
|
237
|
+
firstTextStackLine(rawStack) ??
|
|
238
|
+
pickAttr(log.attributes ?? {}, ['error_message', 'errorMessage', 'message']));
|
|
239
|
+
}
|
|
240
|
+
function pickBodyStack(body) {
|
|
241
|
+
const parsed = parseBodyRecord(body);
|
|
242
|
+
if (!parsed)
|
|
243
|
+
return undefined;
|
|
244
|
+
return (stringProp(parsed, 'stack') ??
|
|
245
|
+
nestedStringProp(parsed, ['error', 'stack']) ??
|
|
246
|
+
nestedStringProp(parsed, ['response', 'error', 'stack']) ??
|
|
247
|
+
pickConsoleArgsStack(parsed));
|
|
248
|
+
}
|
|
249
|
+
function pickBodyErrorMessage(body) {
|
|
250
|
+
const parsed = parseBodyRecord(body);
|
|
251
|
+
if (!parsed)
|
|
252
|
+
return undefined;
|
|
253
|
+
return (stringProp(parsed, 'error_message') ??
|
|
254
|
+
stringProp(parsed, 'message') ??
|
|
255
|
+
nestedStringProp(parsed, ['error', 'message']) ??
|
|
256
|
+
nestedStringProp(parsed, ['response', 'error', 'message']) ??
|
|
257
|
+
pickConsoleArgsMessage(parsed));
|
|
258
|
+
}
|
|
259
|
+
function pickConsoleArgsMessage(record) {
|
|
260
|
+
const parts = Object.entries(record)
|
|
261
|
+
.filter(([key]) => /^\d+$/.test(key))
|
|
262
|
+
.sort(([left], [right]) => Number(left) - Number(right))
|
|
263
|
+
.map(([, value]) => consoleArgMessage(value))
|
|
264
|
+
.filter((message) => Boolean(message));
|
|
265
|
+
if (parts.length === 0)
|
|
266
|
+
return undefined;
|
|
267
|
+
return parts.join(' ').replace(/\s+/g, ' ').trim();
|
|
268
|
+
}
|
|
269
|
+
function pickConsoleArgsStack(record) {
|
|
270
|
+
return Object.entries(record)
|
|
271
|
+
.filter(([key]) => /^\d+$/.test(key))
|
|
272
|
+
.sort(([left], [right]) => Number(left) - Number(right))
|
|
273
|
+
.map(([, value]) => consoleArgStack(value))
|
|
274
|
+
.find((stack) => Boolean(stack));
|
|
275
|
+
}
|
|
276
|
+
function consoleArgStack(value) {
|
|
277
|
+
if (typeof value === 'string') {
|
|
278
|
+
const stack = value.trim();
|
|
279
|
+
return looksLikeTextStack(stack) ? stack : undefined;
|
|
280
|
+
}
|
|
281
|
+
if (!isRecord(value))
|
|
282
|
+
return undefined;
|
|
283
|
+
return (stringProp(value, 'stack') ??
|
|
284
|
+
nestedStringProp(value, ['error', 'stack']) ??
|
|
285
|
+
nestedStringProp(value, ['response', 'error', 'stack']));
|
|
286
|
+
}
|
|
287
|
+
function consoleArgMessage(value) {
|
|
288
|
+
if (typeof value === 'string') {
|
|
289
|
+
const message = value.trim();
|
|
290
|
+
return message.length > 0 ? message : undefined;
|
|
291
|
+
}
|
|
292
|
+
if (!isRecord(value))
|
|
293
|
+
return undefined;
|
|
294
|
+
return errorRecordMessage(value);
|
|
295
|
+
}
|
|
296
|
+
function errorRecordMessage(record) {
|
|
297
|
+
const stackTitle = firstTextStackLine(stringProp(record, 'stack'));
|
|
298
|
+
if (stackTitle)
|
|
299
|
+
return stackTitle;
|
|
300
|
+
const name = stringProp(record, 'name');
|
|
301
|
+
const message = stringProp(record, 'message') ??
|
|
302
|
+
nestedStringProp(record, ['error', 'message']) ??
|
|
303
|
+
nestedStringProp(record, ['response', 'error', 'message']);
|
|
304
|
+
if (name && message && !message.startsWith(`${name}:`)) {
|
|
305
|
+
return `${name}: ${message}`;
|
|
306
|
+
}
|
|
307
|
+
return message ?? name;
|
|
308
|
+
}
|
|
309
|
+
function parseBodyRecord(body) {
|
|
310
|
+
if (!body)
|
|
311
|
+
return undefined;
|
|
312
|
+
let parsed;
|
|
313
|
+
try {
|
|
314
|
+
parsed = JSON.parse(body);
|
|
315
|
+
}
|
|
316
|
+
catch {
|
|
317
|
+
return undefined;
|
|
318
|
+
}
|
|
319
|
+
return isRecord(parsed) ? parsed : undefined;
|
|
320
|
+
}
|
|
321
|
+
function firstTextStackLine(stack) {
|
|
322
|
+
const trimmed = stack?.trim();
|
|
323
|
+
if (!trimmed || trimmed.startsWith('['))
|
|
324
|
+
return undefined;
|
|
325
|
+
return trimmed
|
|
326
|
+
.split('\n')
|
|
327
|
+
.map((line) => line.trim())
|
|
328
|
+
.find((line) => line.length > 0 && !line.startsWith('at '));
|
|
329
|
+
}
|
|
330
|
+
function looksLikeTextStack(value) {
|
|
331
|
+
return value.length > 0 && /:\d+:\d+(?:\)|$)/.test(value);
|
|
332
|
+
}
|
|
333
|
+
function missingReason(params) {
|
|
334
|
+
if (!params.commitID)
|
|
335
|
+
return '日志缺少 release_commit_id / commit_id,无法定位构建产物';
|
|
336
|
+
if (params.frames.length === 0)
|
|
337
|
+
return '日志中没有可解析的 generated stack frame';
|
|
338
|
+
return undefined;
|
|
339
|
+
}
|
|
340
|
+
function normalizeFrameFileName(fileName) {
|
|
341
|
+
return fileName.split(/[/?#]/).filter(Boolean).pop() ?? fileName;
|
|
342
|
+
}
|
|
343
|
+
function formatStack(message, frames) {
|
|
344
|
+
const stack = frames
|
|
345
|
+
.map((frame) => {
|
|
346
|
+
if (frame.sourceFileName) {
|
|
347
|
+
return ` at ${frame.sourceFileName}:${String(frame.sourceLine)}:${String(frame.sourceColumn)}`;
|
|
348
|
+
}
|
|
349
|
+
return ` at ${frame.fileName}:${String(frame.line)}:${String(frame.column)}`;
|
|
350
|
+
})
|
|
351
|
+
.join('\n');
|
|
352
|
+
if (message && stack)
|
|
353
|
+
return `${message}\n${stack}`;
|
|
354
|
+
return message ?? stack;
|
|
355
|
+
}
|
|
356
|
+
function isRecord(value) {
|
|
357
|
+
return typeof value === 'object' && value !== null;
|
|
358
|
+
}
|
|
359
|
+
function toFiniteNumber(value) {
|
|
360
|
+
if (typeof value === 'number' && Number.isFinite(value))
|
|
361
|
+
return value;
|
|
362
|
+
if (typeof value === 'string' && value.trim()) {
|
|
363
|
+
const numberValue = Number(value);
|
|
364
|
+
return Number.isFinite(numberValue) ? numberValue : undefined;
|
|
365
|
+
}
|
|
366
|
+
return undefined;
|
|
367
|
+
}
|
|
368
|
+
function stringProp(record, key) {
|
|
369
|
+
const value = record[key];
|
|
370
|
+
return typeof value === 'string' && value.length > 0 ? value : undefined;
|
|
371
|
+
}
|
|
372
|
+
function nestedStringProp(record, path) {
|
|
373
|
+
let current = record;
|
|
374
|
+
for (const key of path) {
|
|
375
|
+
if (!isRecord(current))
|
|
376
|
+
return undefined;
|
|
377
|
+
current = current[key];
|
|
378
|
+
}
|
|
379
|
+
return typeof current === 'string' && current.length > 0 ? current : undefined;
|
|
380
|
+
}
|
|
381
|
+
function errorMessage(err) {
|
|
382
|
+
return err instanceof Error ? err.message : String(err);
|
|
383
|
+
}
|
|
384
|
+
function sourceMapFileForFrame(fileName) {
|
|
385
|
+
return `${SOURCE_MAP_FILE_PREFIX}${fileName}.map`;
|
|
386
|
+
}
|
|
387
|
+
function sourceMapFilesForFrames(frames) {
|
|
388
|
+
return Array.from(new Set(frames.map((frame) => sourceMapFileForFrame(frame.fileName))));
|
|
389
|
+
}
|
|
@@ -40,6 +40,12 @@ const output_1 = require("../../../utils/output");
|
|
|
40
40
|
const args_1 = require("../../../utils/args");
|
|
41
41
|
const index_1 = require("../../../api/observability/index");
|
|
42
42
|
const helpers_1 = require("./helpers");
|
|
43
|
+
function stringIdFilter(value) {
|
|
44
|
+
if (Array.isArray(value)) {
|
|
45
|
+
return value.length > 0 ? (0, helpers_1.inFilter)(value) : undefined;
|
|
46
|
+
}
|
|
47
|
+
return value ? (0, helpers_1.eqFilter)(value) : undefined;
|
|
48
|
+
}
|
|
43
49
|
/** miaoda observability trace list */
|
|
44
50
|
async function handleObservabilityTraceList(opts) {
|
|
45
51
|
const appID = opts.appId;
|
|
@@ -51,7 +57,7 @@ async function handleObservabilityTraceList(opts) {
|
|
|
51
57
|
// - 入口 span:BAM 没有 entrySpanName fieldFilter,统一走 fuzzyFilter(trace endpoint
|
|
52
58
|
// 默认作用于 span name),所以 --root-span 不进 fieldFilters
|
|
53
59
|
const fieldFilters = (0, helpers_1.buildFieldFilters)([
|
|
54
|
-
{ key: 'trace_id', value:
|
|
60
|
+
{ key: 'trace_id', value: stringIdFilter(opts.traceId) },
|
|
55
61
|
{ key: 'user_id', value: opts.userId ? (0, helpers_1.eqFilter)(opts.userId, 'i64') : undefined },
|
|
56
62
|
]);
|
|
57
63
|
const req = {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.handleSkillsStatus = exports.handleSkillsSync = void 0;
|
|
4
|
+
var sync_1 = require("./sync");
|
|
5
|
+
Object.defineProperty(exports, "handleSkillsSync", { enumerable: true, get: function () { return sync_1.handleSkillsSync; } });
|
|
6
|
+
var status_1 = require("./status");
|
|
7
|
+
Object.defineProperty(exports, "handleSkillsStatus", { enumerable: true, get: function () { return status_1.handleSkillsStatus; } });
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.handleSkillsStatus = handleSkillsStatus;
|
|
7
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
8
|
+
const index_1 = require("../../../services/skills/index");
|
|
9
|
+
const spark_meta_1 = require("../../../utils/spark-meta");
|
|
10
|
+
const output_1 = require("../../../utils/output");
|
|
11
|
+
/**
|
|
12
|
+
* miaoda skills status [--dir <path>]
|
|
13
|
+
*
|
|
14
|
+
* 从 .spark/meta.json 读 stack,扫描 <dir>/.agent/steering/<stack>/,
|
|
15
|
+
* 给出当前装了哪些 skills、tech.md 是否在。纯本地操作,不联网。
|
|
16
|
+
* meta 缺 stack 时返回 present=false(不抛错,方便和 init 的引导分开)。
|
|
17
|
+
*/
|
|
18
|
+
async function handleSkillsStatus(opts) {
|
|
19
|
+
await Promise.resolve();
|
|
20
|
+
const targetDir = node_path_1.default.resolve(opts.dir ?? process.cwd());
|
|
21
|
+
const meta = (0, spark_meta_1.readSparkMeta)(targetDir);
|
|
22
|
+
const status = (0, index_1.readSkillsStatus)({ targetDir, stack: meta.stack });
|
|
23
|
+
(0, output_1.emit)({
|
|
24
|
+
data: {
|
|
25
|
+
stack: meta.stack ?? null,
|
|
26
|
+
present: status.present,
|
|
27
|
+
skills: status.skills,
|
|
28
|
+
techSynced: status.techSynced,
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
}
|