@uwa4d/openapi-mcp 0.2.0-beta.5 → 0.2.0-beta.8

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.
@@ -0,0 +1,33 @@
1
+ import type { StackMetric } from './stack-agg.js';
2
+ /**
3
+ * Overview 堆栈采集模式(来自 overview/stack/overall/tree/presign 的 testMode)。
4
+ * 仅适用于 Overview 报告;Mono 报告走独立堆栈,不使用本枚举。
5
+ */
6
+ export type OverviewStackTestMode = 'CPU_ONLY' | 'LUA_MEM_ONLY' | 'CPU_AND_LUA_MEM';
7
+ /** @deprecated 使用 OverviewStackTestMode */
8
+ export type StackTestMode = OverviewStackTestMode;
9
+ export interface OverviewStackTestModeProfile {
10
+ stackTestMode: OverviewStackTestMode;
11
+ availableMetrics: StackMetric[];
12
+ availableFields: readonly string[];
13
+ description: string;
14
+ }
15
+ /** @deprecated 使用 OverviewStackTestModeProfile */
16
+ export type StackTestModeProfile = OverviewStackTestModeProfile;
17
+ export declare const OVERVIEW_STACK_TEST_MODE_PROFILES: Record<OverviewStackTestMode, OverviewStackTestModeProfile>;
18
+ export declare const STACK_TEST_MODE_PROFILES: Record<OverviewStackTestMode, OverviewStackTestModeProfile>;
19
+ export declare function normalizeOverviewStackTestMode(raw: unknown): OverviewStackTestMode | null;
20
+ /** @deprecated 使用 normalizeOverviewStackTestMode */
21
+ export declare const normalizeStackTestMode: typeof normalizeOverviewStackTestMode;
22
+ export declare function inferOverviewStackTestMode(flags: {
23
+ hasTime: boolean;
24
+ hasMem: boolean;
25
+ }): OverviewStackTestMode | null;
26
+ /** @deprecated 使用 inferOverviewStackTestMode */
27
+ export declare const inferStackTestMode: typeof inferOverviewStackTestMode;
28
+ export declare function resolveOverviewDefaultMetric(stackTestMode: OverviewStackTestMode): StackMetric;
29
+ /** @deprecated 使用 resolveOverviewDefaultMetric */
30
+ export declare const resolveDefaultMetric: typeof resolveOverviewDefaultMetric;
31
+ export declare function assertOverviewMetricSupported(metric: StackMetric, profile: OverviewStackTestModeProfile): void;
32
+ /** @deprecated 使用 assertOverviewMetricSupported */
33
+ export declare const assertMetricSupported: typeof assertOverviewMetricSupported;
@@ -0,0 +1,60 @@
1
+ export const OVERVIEW_STACK_TEST_MODE_PROFILES = {
2
+ CPU_ONLY: {
3
+ stackTestMode: 'CPU_ONLY',
4
+ availableMetrics: ['selfTime', 'totalTime', 'callCount'],
5
+ availableFields: ['selfTimeMean', 'totalTimeMean', 'callCountTotal'],
6
+ description: 'CPU_ONLY:仅有耗时(selfTimeMean/totalTimeMean,ms)与调用次数(callCountTotal);内存字段恒为 null。',
7
+ },
8
+ LUA_MEM_ONLY: {
9
+ stackTestMode: 'LUA_MEM_ONLY',
10
+ availableMetrics: ['callCount', 'selfMemory'],
11
+ availableFields: ['callCountTotal', 'selfMemoryMean', 'selfMemoryTotal'],
12
+ description: 'LUA_MEM_ONLY:仅有调用次数(callCountTotal)与内存(selfMemoryMean/selfMemoryTotal);耗时字段恒为 null。',
13
+ },
14
+ CPU_AND_LUA_MEM: {
15
+ stackTestMode: 'CPU_AND_LUA_MEM',
16
+ availableMetrics: ['selfTime', 'totalTime', 'callCount', 'selfMemory'],
17
+ availableFields: [
18
+ 'selfTimeMean',
19
+ 'totalTimeMean',
20
+ 'callCountTotal',
21
+ 'selfMemoryMean',
22
+ 'selfMemoryTotal',
23
+ ],
24
+ description: 'CPU_AND_LUA_MEM:耗时、调用次数、内存均可用。',
25
+ },
26
+ };
27
+ export const STACK_TEST_MODE_PROFILES = OVERVIEW_STACK_TEST_MODE_PROFILES;
28
+ export function normalizeOverviewStackTestMode(raw) {
29
+ const tm = String(raw ?? '').toUpperCase();
30
+ if (tm === 'CPU_ONLY' || tm === 'LUA_MEM_ONLY' || tm === 'CPU_AND_LUA_MEM') {
31
+ return tm;
32
+ }
33
+ return null;
34
+ }
35
+ /** @deprecated 使用 normalizeOverviewStackTestMode */
36
+ export const normalizeStackTestMode = normalizeOverviewStackTestMode;
37
+ export function inferOverviewStackTestMode(flags) {
38
+ if (flags.hasTime && flags.hasMem)
39
+ return 'CPU_AND_LUA_MEM';
40
+ if (flags.hasMem && !flags.hasTime)
41
+ return 'LUA_MEM_ONLY';
42
+ if (flags.hasTime)
43
+ return 'CPU_ONLY';
44
+ return null;
45
+ }
46
+ /** @deprecated 使用 inferOverviewStackTestMode */
47
+ export const inferStackTestMode = inferOverviewStackTestMode;
48
+ export function resolveOverviewDefaultMetric(stackTestMode) {
49
+ return stackTestMode === 'LUA_MEM_ONLY' ? 'selfMemory' : 'selfTime';
50
+ }
51
+ /** @deprecated 使用 resolveOverviewDefaultMetric */
52
+ export const resolveDefaultMetric = resolveOverviewDefaultMetric;
53
+ export function assertOverviewMetricSupported(metric, profile) {
54
+ if (!profile.availableMetrics.includes(metric)) {
55
+ throw new Error(`当前 Overview stackTestMode=${profile.stackTestMode} 不支持 metric=${metric}。` +
56
+ `可用:${profile.availableMetrics.join('、')}。${profile.description}`);
57
+ }
58
+ }
59
+ /** @deprecated 使用 assertOverviewMetricSupported */
60
+ export const assertMetricSupported = assertOverviewMetricSupported;
@@ -7,8 +7,13 @@ export interface FunctionRow {
7
7
  source: string;
8
8
  label?: string;
9
9
  methodId?: string;
10
- selfTimeMs?: number;
11
- totalTimeMs?: number;
10
+ stackMethodId?: string;
11
+ selfTimeMean?: number | null;
12
+ totalTimeMean?: number | null;
13
+ callCountTotal?: number | null;
14
+ selfMemoryMean?: number | null;
15
+ selfMemoryTotal?: number | null;
16
+ selfMemoryUnit?: string | null;
12
17
  callCount?: number;
13
18
  nodeCount?: number;
14
19
  }
@@ -1,12 +1,19 @@
1
1
  import { z } from './types.js';
2
2
  import { errorResult, jsonToolResult, requireReportKey, topNOf } from './helpers.js';
3
3
  import { fetchReportIdentity } from './route-overview.js';
4
- import { aggregateMonoStack, aggregateOverviewStack } from './stack-agg.js';
5
- function resolveMetric(raw, mode) {
4
+ import { aggregateMonoStack, aggregateOverviewStackFromStatistic } from './stack-agg.js';
5
+ import { filterRowsByNamePattern } from './name-pattern.js';
6
+ function resolveOverviewMetricInput(raw) {
6
7
  if (raw === 'totalTime' || raw === 'callCount' || raw === 'selfMemory' || raw === 'selfTime') {
7
8
  return raw;
8
9
  }
9
- return mode === 'mono' ? 'selfMemory' : 'selfTime';
10
+ return undefined;
11
+ }
12
+ function resolveMonoMetric(raw) {
13
+ if (raw === 'totalTime' || raw === 'callCount' || raw === 'selfMemory' || raw === 'selfTime') {
14
+ return raw;
15
+ }
16
+ return 'selfMemory';
10
17
  }
11
18
  export async function runTopFunctions(client, args) {
12
19
  const key = requireReportKey(args);
@@ -17,33 +24,42 @@ export async function runTopFunctions(client, args) {
17
24
  const identity = await fetchReportIdentity(client, key);
18
25
  const apisUsed = ['get_report_detail'];
19
26
  const mode = subtype ?? identity.serviceSubtype ?? 'overview';
20
- const metric = resolveMetric(args['metric'], mode);
21
27
  if (mode === 'mono') {
28
+ const metric = resolveMonoMetric(args['metric']);
22
29
  if (identity.engine !== 'unity') {
23
30
  return {
24
31
  engine: identity.engine,
25
32
  dataKey: identity.dataKey,
33
+ serviceSubtype: 'mono',
26
34
  items: [],
27
35
  _apisUsed: apisUsed,
28
- _note: 'Mono 模式仅支持 Unity 报告。',
36
+ _note: 'Mono 模式仅支持 Unity 报告(与 Overview stackTestMode 无关)。',
29
37
  _limitations: ['engine!=unity'],
30
38
  };
31
39
  }
32
40
  try {
33
- const agg = await aggregateMonoStack(client, identity, topN, metric);
41
+ const namePattern = typeof args['namePattern'] === 'string' && args['namePattern'].trim()
42
+ ? String(args['namePattern']).trim()
43
+ : undefined;
44
+ const fetchN = namePattern ? Math.max(topN, 200) : topN;
45
+ const agg = await aggregateMonoStack(client, identity, fetchN, metric);
46
+ const filtered = filterRowsByNamePattern(agg.rows, namePattern);
47
+ const items = filtered.rows.slice(0, topN);
34
48
  return {
35
49
  engine: identity.engine,
36
50
  dataKey: identity.dataKey,
37
51
  serviceSubtype: 'mono',
38
52
  topN,
39
53
  metric: agg.metric,
40
- testMode: agg.testMode,
41
- items: agg.rows,
54
+ namePattern: namePattern ?? null,
55
+ matchedFunctions: namePattern ? filtered.matchedTotal : agg.uniqueFunctions,
56
+ items,
42
57
  totalNodes: agg.totalNodes,
43
58
  uniqueFunctions: agg.uniqueFunctions,
44
59
  _apisUsed: [...apisUsed, ...agg.apisUsed],
45
- _note: `已下载 Mono 正向堆栈树并按函数名合并同名节点(共 ${agg.totalNodes} 个节点 → ${agg.uniqueFunctions} 个函数),` +
46
- `按 ${agg.metric} 降序取 Top ${agg.rows.length}。`,
60
+ _note: `Mono 模式:已下载正向堆栈树合并取 Top ${items.length}(共 ${agg.totalNodes} 节点)。` +
61
+ `Mono 不使用 Overview stackTestMode;默认按 selfMemory 排序。` +
62
+ (namePattern ? ` namePattern「${String(namePattern)}」命中 ${filtered.matchedTotal} 个。` : ''),
47
63
  };
48
64
  }
49
65
  catch (e) {
@@ -58,25 +74,44 @@ export async function runTopFunctions(client, args) {
58
74
  };
59
75
  }
60
76
  }
61
- // Overview:必须以堆栈树 + idmap 为准;统计接口里的「卡顿重点函数 / func / 自定义函数组」都不完整。
77
+ const threadName = typeof args['threadName'] === 'string' && args['threadName'].trim()
78
+ ? String(args['threadName']).trim()
79
+ : undefined;
80
+ const metricInput = resolveOverviewMetricInput(args['metric']);
81
+ const namePattern = typeof args['namePattern'] === 'string' && args['namePattern'].trim()
82
+ ? String(args['namePattern']).trim()
83
+ : undefined;
62
84
  try {
63
- const agg = await aggregateOverviewStack(client, identity, topN, metric);
85
+ const agg = await aggregateOverviewStackFromStatistic(client, identity, topN, metricInput, threadName, namePattern);
86
+ const nameFilterNote = namePattern
87
+ ? agg.rows.length === 0
88
+ ? ` namePattern「${namePattern}」无匹配函数。`
89
+ : ` namePattern「${namePattern}」已过滤后取 Top ${agg.rows.length}。`
90
+ : '';
64
91
  return {
65
92
  engine: identity.engine,
66
93
  dataKey: identity.dataKey,
67
94
  createDate: identity.createDate,
68
95
  sdkVersion: identity.sdkVersion,
69
- serviceSubtype: identity.serviceSubtype,
96
+ serviceSubtype: 'overview',
97
+ stackTestMode: agg.stackTestMode,
98
+ availableMetrics: agg.availableMetrics,
99
+ availableFields: agg.availableFields,
70
100
  topN,
71
101
  metric: agg.metric,
72
- testMode: agg.testMode,
102
+ namePattern: namePattern ?? null,
73
103
  items: agg.rows,
74
104
  totalNodes: agg.totalNodes,
75
105
  uniqueFunctions: agg.uniqueFunctions,
76
106
  _apisUsed: [...apisUsed, ...agg.apisUsed],
77
- _note: `已下载主线程堆栈树并按函数名合并同名节点(共 ${agg.totalNodes} 个节点 → ${agg.uniqueFunctions} 个函数),` +
78
- `按 ${agg.metric}(${agg.unit})降序取 Top ${agg.rows.length}。` +
79
- `默认排序字段为 selfTime(自身耗时),不是统计接口里的抽样指标。`,
107
+ _note: `Overview 模式 stackTestMode=${agg.stackTestMode}。${agg.metricGuide ?? ''} ` +
108
+ `本次按 ${agg.metric} 排序,Top ${agg.rows.length} / 共 ${agg.totalNodes} 函数。` +
109
+ `数据来自 method/idmap/statistic v1.0.2(非堆栈树解析)。` +
110
+ `逐帧曲线:method/curve/presign v1.0.2 + stackMethodId。` +
111
+ (identity.engine === 'unreal'
112
+ ? ` UE 默认 threadName=GameThread${threadName ? `(本次 ${threadName})` : ''};UE 固定 CPU_ONLY。`
113
+ : '') +
114
+ nameFilterNote,
80
115
  };
81
116
  }
82
117
  catch (e) {
@@ -85,12 +120,12 @@ export async function runTopFunctions(client, args) {
85
120
  dataKey: identity.dataKey,
86
121
  createDate: identity.createDate,
87
122
  sdkVersion: identity.sdkVersion,
88
- serviceSubtype: identity.serviceSubtype,
123
+ serviceSubtype: 'overview',
89
124
  items: [],
90
125
  _apisUsed: apisUsed,
91
- _note: `堆栈树聚合失败:${e instanceof Error ? e.message : String(e)}。` +
92
- `Overview 函数 Top 依赖 stack/overall/tree + stack/id/map(通常需 SDK ≥ 2.5.1 且开启 CPU 堆栈)。`,
93
- _limitations: ['overview_stack_unavailable'],
126
+ _note: `Overview 函数 Top 失败:${e instanceof Error ? e.message : String(e)}。` +
127
+ `通常需 SDK ≥ 2.5.1 且报告开启堆栈。`,
128
+ _limitations: ['overview_idmap_statistic_unavailable'],
94
129
  };
95
130
  }
96
131
  }
@@ -100,26 +135,36 @@ export const topFunctionsTool = {
100
135
  engines: ['unity', 'unreal'],
101
136
  filterKeys: ['top_functions', 'composite', 'preset.default', 'overview', 'mono'],
102
137
  description: [
103
- '从堆栈树聚合指定报告的函数 Top N(按自身耗时合并同名节点)。',
138
+ '从函数统计聚合指定报告的函数 Top N',
104
139
  '适用引擎:Unity / UE|复合工具',
105
- 'Overview:下载主线程堆栈树(stack/overall/tree/presign)+ 函数 idmap(stack/id/map),',
106
- ' methodId 还原为函数名,并把树上同一函数的多个节点合并(selfTime/callCount 相加),再取 Top N。',
107
- '不要用统计接口里的「卡顿重点函数」或自定义函数组——那不是全量函数耗时。',
108
- 'Mono(仅 Unity):走 Mono 正向堆栈树 + Mono 函数 idmap,默认按自身内存排序。',
109
- '需要原始树时请直接调对应的 stack/presign 原子工具。',
140
+ 'Overview:method/idmap/statistic v1.0.2;stackTestMode 决定可用字段(仅 Overview):',
141
+ ' CPU_ONLY selfTimeMean/totalTimeMean/callCountTotal;',
142
+ ' LUA_MEM_ONLY → callCountTotal/selfMemoryMean/selfMemoryTotal;',
143
+ ' CPU_AND_LUA_MEM 全部;UE 固定 CPU_ONLY。',
144
+ 'Mono(仅 Unity):独立堆栈树,不使用 stackTestMode,默认 selfMemory。',
145
+ '逐帧曲线:method/curve/presign v1.0.2 + stackMethodId。',
146
+ 'namePattern:可选,按函数名子串或 /正则/ 过滤(Overview 在 idmap 结果上过滤;Mono 在合并后的函数列表上过滤)。',
110
147
  ].join('\n'),
111
148
  inputSchema: {
112
149
  dataKey: z.string().optional().describe('报告 dataKey,与 recordId 二选一'),
113
150
  recordId: z.union([z.string(), z.number()]).optional().describe('报告 recordId,与 dataKey 二选一'),
114
151
  topN: z.number().optional().describe('返回条数,默认 20,最大 200'),
152
+ namePattern: z
153
+ .string()
154
+ .optional()
155
+ .describe('函数名过滤:默认子串匹配(忽略大小写);正则写 /pattern/i,如 /Canvas\\./ 或 Update'),
115
156
  metric: z
116
157
  .enum(['selfTime', 'totalTime', 'callCount', 'selfMemory'])
117
158
  .optional()
118
- .describe('排序指标,默认 Overview selfTime(自身耗时 ms),Mono selfMemory(KB)'),
159
+ .describe('排序指标。Overview 须与 stackTestMode 匹配(CPU_ONLY 无 selfMemory;LUA_MEM_ONLY 无 selfTime/totalTime);未传时 Overview 按模式默认 selfTime selfMemory'),
160
+ threadName: z
161
+ .string()
162
+ .optional()
163
+ .describe('UE Overview 线程名,默认 GameThread;Unity 忽略'),
119
164
  serviceSubtype: z
120
165
  .enum(['overview', 'mono'])
121
166
  .optional()
122
- .describe('报告模式,默认跟随报告详情;mono 仅 Unity'),
167
+ .describe('overview(默认)或 mono;stackTestModeoverview 有效'),
123
168
  },
124
169
  async handler(args, ctx) {
125
170
  try {
@@ -0,0 +1,12 @@
1
+ /** indicatorDashboards 合法面板传参名称(与 got-query OverviewIndicatorDashboardCatalogService 同源)。 */
2
+ export declare const INDICATOR_DASHBOARD_KEYS: readonly string[];
3
+ /** 常见误传:统计维度后缀 / 子指标名,不是面板标识符。 */
4
+ export declare const INDICATOR_DASHBOARD_NEGATIVE_EXAMPLES: readonly ["fps_mean", "fps_maximum", "frametime_min", "frametime_gt_40_pct", "temperature_mean", "drawcall_maximum"];
5
+ export declare function splitIndicatorDashboards(raw: unknown): string[];
6
+ export declare function findUnknownIndicatorDashboards(raw: unknown): string[];
7
+ /** 拆成已接受 / 未识别面板名(保持请求顺序,已接受去重)。 */
8
+ export declare function partitionIndicatorDashboards(raw: unknown): {
9
+ accepted: string[];
10
+ unknown: string[];
11
+ all: string[];
12
+ };
@@ -0,0 +1,49 @@
1
+ import { readFileSync } from 'node:fs';
2
+ import { dirname, join } from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ const keysJson = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), 'indicator-dashboard-keys.json'), 'utf8'));
5
+ /** indicatorDashboards 合法面板传参名称(与 got-query OverviewIndicatorDashboardCatalogService 同源)。 */
6
+ export const INDICATOR_DASHBOARD_KEYS = keysJson.keys;
7
+ /** 常见误传:统计维度后缀 / 子指标名,不是面板标识符。 */
8
+ export const INDICATOR_DASHBOARD_NEGATIVE_EXAMPLES = [
9
+ 'fps_mean',
10
+ 'fps_maximum',
11
+ 'frametime_min',
12
+ 'frametime_gt_40_pct',
13
+ 'temperature_mean',
14
+ 'drawcall_maximum',
15
+ ];
16
+ export function splitIndicatorDashboards(raw) {
17
+ if (typeof raw !== 'string' || !raw.trim())
18
+ return [];
19
+ return raw
20
+ .split(',')
21
+ .map((s) => s.trim())
22
+ .filter(Boolean);
23
+ }
24
+ export function findUnknownIndicatorDashboards(raw) {
25
+ const parts = splitIndicatorDashboards(raw);
26
+ if (!parts.length)
27
+ return [];
28
+ const valid = new Set(INDICATOR_DASHBOARD_KEYS);
29
+ return parts.filter((k) => !valid.has(k));
30
+ }
31
+ /** 拆成已接受 / 未识别面板名(保持请求顺序,已接受去重)。 */
32
+ export function partitionIndicatorDashboards(raw) {
33
+ const all = splitIndicatorDashboards(raw);
34
+ const valid = new Set(INDICATOR_DASHBOARD_KEYS);
35
+ const accepted = [];
36
+ const unknown = [];
37
+ const seen = new Set();
38
+ for (const k of all) {
39
+ if (!valid.has(k)) {
40
+ unknown.push(k);
41
+ continue;
42
+ }
43
+ if (seen.has(k))
44
+ continue;
45
+ seen.add(k);
46
+ accepted.push(k);
47
+ }
48
+ return { accepted, unknown, all };
49
+ }