mirror of
https://github.com/Syngnat/GoNavi.git
synced 2026-08-06 06:13:51 +08:00
- 新增 JMX/Endpoint/Agent 三种 JVM 连接模式与配置归一化链路 - 支持资源浏览、变更预览、写入应用、审计记录与只读约束 - 接入 AI 结构化写入计划与诊断计划回填能力 - 新增 Agent Bridge、Arthas Tunnel、JMX Helper 诊断传输实现 - 增加诊断控制台、命令模板、输出历史与自动补全交互 - 补齐前后端契约、运行夹具与 JVM 相关回归测试
136 lines
3.6 KiB
TypeScript
136 lines
3.6 KiB
TypeScript
import type {
|
|
JVMDiagnosticPlan,
|
|
JVMDiagnosticPlanContext,
|
|
SavedConnection,
|
|
TabData,
|
|
} from "../types";
|
|
|
|
const planFencePattern = /```json\s*([\s\S]*?)```/gi;
|
|
const allowedTransports = new Set<JVMDiagnosticPlan["transport"]>([
|
|
"agent-bridge",
|
|
"arthas-tunnel",
|
|
]);
|
|
const allowedRiskLevels = new Set<JVMDiagnosticPlan["riskLevel"]>([
|
|
"low",
|
|
"medium",
|
|
"high",
|
|
]);
|
|
|
|
const asTrimmedString = (value: unknown): string => String(value ?? "").trim();
|
|
|
|
const isRecord = (value: unknown): value is Record<string, unknown> =>
|
|
!!value && typeof value === "object" && !Array.isArray(value);
|
|
|
|
const normalizeTransport = (value: unknown): JVMDiagnosticPlan["transport"] => {
|
|
const transport = asTrimmedString(value) as JVMDiagnosticPlan["transport"];
|
|
return allowedTransports.has(transport) ? transport : "agent-bridge";
|
|
};
|
|
|
|
const normalizeRiskLevel = (value: unknown): JVMDiagnosticPlan["riskLevel"] => {
|
|
const riskLevel = asTrimmedString(value) as JVMDiagnosticPlan["riskLevel"];
|
|
return allowedRiskLevels.has(riskLevel) ? riskLevel : "low";
|
|
};
|
|
|
|
const normalizePlan = (value: unknown): JVMDiagnosticPlan | null => {
|
|
if (!isRecord(value)) {
|
|
return null;
|
|
}
|
|
|
|
if (typeof value.command !== "string") {
|
|
return null;
|
|
}
|
|
const command = asTrimmedString(value.command);
|
|
if (!command) {
|
|
return null;
|
|
}
|
|
|
|
const intent = asTrimmedString(value.intent) || "generic_diagnostic";
|
|
const reason = asTrimmedString(value.reason) || `AI 诊断计划:${intent}`;
|
|
|
|
return {
|
|
intent,
|
|
transport: normalizeTransport(value.transport),
|
|
command,
|
|
riskLevel: normalizeRiskLevel(value.riskLevel),
|
|
reason,
|
|
expectedSignals: Array.isArray(value.expectedSignals)
|
|
? value.expectedSignals
|
|
.map((item) => asTrimmedString(item))
|
|
.filter(Boolean)
|
|
: [],
|
|
};
|
|
};
|
|
|
|
const tryParsePlan = (content: string): JVMDiagnosticPlan | null => {
|
|
try {
|
|
return normalizePlan(JSON.parse(content));
|
|
} catch {
|
|
return null;
|
|
}
|
|
};
|
|
|
|
const resolveDiagnosticTransport = (
|
|
connection?: Pick<SavedConnection, "config">,
|
|
): JVMDiagnosticPlan["transport"] =>
|
|
normalizeTransport(connection?.config?.jvm?.diagnostic?.transport);
|
|
|
|
export const parseJVMDiagnosticPlan = (
|
|
content: string,
|
|
): JVMDiagnosticPlan | null => {
|
|
const source = String(content || "").trim();
|
|
if (!source) {
|
|
return null;
|
|
}
|
|
|
|
planFencePattern.lastIndex = 0;
|
|
let match: RegExpExecArray | null;
|
|
while ((match = planFencePattern.exec(source)) !== null) {
|
|
const parsed = tryParsePlan(match[1]);
|
|
if (parsed) {
|
|
return parsed;
|
|
}
|
|
}
|
|
|
|
return tryParsePlan(source);
|
|
};
|
|
|
|
export const matchesJVMDiagnosticPlanTargetTab = (
|
|
tab: Pick<TabData, "id" | "type" | "connectionId">,
|
|
connections: Pick<SavedConnection, "id" | "config">[],
|
|
context?: JVMDiagnosticPlanContext,
|
|
): boolean => {
|
|
if (!context || tab.type !== "jvm-diagnostic") {
|
|
return false;
|
|
}
|
|
|
|
const connection = connections.find((item) => item.id === tab.connectionId);
|
|
return (
|
|
tab.connectionId === context.connectionId &&
|
|
resolveDiagnosticTransport(connection) === normalizeTransport(context.transport)
|
|
);
|
|
};
|
|
|
|
export const resolveJVMDiagnosticPlanTargetTabId = (
|
|
tabs: TabData[],
|
|
connections: Pick<SavedConnection, "id" | "config">[],
|
|
context?: JVMDiagnosticPlanContext,
|
|
): string => {
|
|
if (!context) {
|
|
return "";
|
|
}
|
|
|
|
const exactMatch = tabs.find(
|
|
(tab) =>
|
|
tab.id === context.tabId &&
|
|
matchesJVMDiagnosticPlanTargetTab(tab, connections, context),
|
|
);
|
|
if (exactMatch) {
|
|
return exactMatch.id;
|
|
}
|
|
|
|
const fallbackMatch = tabs.find((tab) =>
|
|
matchesJVMDiagnosticPlanTargetTab(tab, connections, context),
|
|
);
|
|
return fallbackMatch?.id || "";
|
|
};
|