feat: upgrade investigation and presentation runtime
Generic Container CI/CD / test-build-publish (push) Successful in 1m6s
Agent CI/CD / build-test-publish-and-deploy (push) Successful in 1m7s

This commit is contained in:
2026-10-30 17:30:19 +08:00
parent 3b52a0ddab
commit 6336157ff4
104 changed files with 6541 additions and 961 deletions
+19 -66
View File
@@ -1,7 +1,7 @@
import { z } from "zod";
import { viewCommandVariants } from "./command.js";
import type { FrontendActionManifest } from "./types.js";
type Definition = { manifest: FrontendActionManifest; inputSchema: z.ZodTypeAny; outputSchema: z.ZodTypeAny };
export { safeChartInput } from "./command.js";
const deviceId = z.string().trim().min(1).max(128);
const scadaLevel = z.enum(["high", "medium", "low", "unrated"]);
const levelCounts = z.object({
@@ -10,68 +10,21 @@ const levelCounts = z.object({
low: z.number().int().nonnegative(),
unrated: z.number().int().nonnegative(),
}).strict();
const renderScadaAnalysisInput = z.object({
items: z.array(z.object({ device_id: deviceId, level: scadaLevel }).strict()).min(1).max(100),
}).strict().superRefine((value, context) => {
const seen = new Set<string>();
value.items.forEach((item, index) => {
if (seen.has(item.device_id)) {
context.addIssue({
code: z.ZodIssueCode.custom,
message: "device_id values must be unique",
path: ["items", index, "device_id"],
});
}
seen.add(item.device_id);
});
const cleared = z.object({ cleared: z.literal(true) }).strict();
const outputs: Record<string, z.ZodTypeAny> = {
highlight_features: z.object({ highlighted_ids: z.array(deviceId).min(1), missing_ids: z.array(deviceId), fitted: z.boolean() }).strict(),
close_chart: z.object({ closed: z.literal(true) }).strict(),
set_overlay: z.object({ displayed: z.literal(true), feature_count: z.number().int().positive() }).strict(),
locate_features: z.object({ located_ids: z.array(deviceId).min(1), missing_ids: z.array(deviceId), fitted: z.literal(true) }).strict(),
zoom_to_map: z.object({ fitted: z.literal(true) }).strict(),
open_chart: z.object({ displayed: z.literal(true), series_count: z.number().int().positive() }).strict(),
render_scada_analysis: z.object({ rendered_ids: z.array(deviceId), missing_ids: z.array(deviceId), level_counts: levelCounts, fitted: z.literal(true) }).strict(),
};
const definitions = viewCommandVariants.map(inputSchema => {
const id = inputSchema.shape.action.value;
const manifest: FrontendActionManifest = { id, version: "frontend-action@2", risk: "view", timeoutMs: 15000, supportedSurfaces: id.includes("chart") ? ["side_panel", "canvas"] : ["map_overlay"] };
return { manifest, inputSchema, outputSchema: outputs[id] ?? cleared };
});
const viewManifest = (id: string, map = false): FrontendActionManifest => ({ id, version: "frontend-action@1", risk: "view", timeoutMs: 15_000, supportedSurfaces: map ? ["map_overlay"] : ["side_panel", "canvas"] });
export const safeChartInput = z.object({
title: z.string().max(200), chart_type: z.enum(["line", "bar"]),
x_data: z.array(z.string().max(128)).min(1).max(100),
series: z.array(z.object({ name: z.string().max(128), data: z.array(z.number().finite()).min(1).max(100) }).strict()).min(1).max(8),
}).strict().refine((value) => value.series.every((series) => series.data.length === value.x_data.length), "series must align with x_data");
const definitions: Definition[] = [
{ manifest: viewManifest("locate_features", true),
inputSchema: z.object({ layer: z.enum(["junctions", "pipes", "valves", "pumps", "tanks", "reservoirs", "scada"]), feature_ids: z.array(deviceId).min(1).max(100) }).strict(),
outputSchema: z.object({ located_ids: z.array(deviceId).min(1), missing_ids: z.array(deviceId), fitted: z.literal(true) }).strict() },
{ manifest: viewManifest("zoom_to_map", true),
inputSchema: z.object({ center: z.tuple([z.number().min(-180).max(180), z.number().min(-90).max(90)]), zoom: z.number().min(0).max(24) }).strict(),
outputSchema: z.object({ fitted: z.literal(true) }).strict() },
{ manifest: viewManifest("show_chart"), inputSchema: safeChartInput,
outputSchema: z.object({ displayed: z.literal(true), series_count: z.number().int().positive() }).strict() },
{ manifest: viewManifest("view_history"),
inputSchema: z.object({ element_type: z.enum(["junction", "pipe"]), element_id: deviceId, start: z.string().datetime({ offset: true }), end: z.string().datetime({ offset: true }), chart: safeChartInput.optional() }).strict(),
outputSchema: z.object({ displayed: z.literal(true), series_count: z.number().int().positive() }).strict() },
{
manifest: {
id: "render_scada_analysis",
version: "frontend-action@1",
risk: "view",
timeoutMs: 15_000,
supportedSurfaces: ["map_overlay"],
},
inputSchema: renderScadaAnalysisInput,
outputSchema: z.object({
rendered_ids: z.array(deviceId),
missing_ids: z.array(deviceId),
level_counts: levelCounts,
fitted: z.literal(true),
}).strict(),
},
{
manifest: {
id: "clear_scada_analysis",
version: "frontend-action@1",
risk: "view",
timeoutMs: 15_000,
supportedSurfaces: ["map_overlay"],
},
inputSchema: z.object({}).strict(),
outputSchema: z.object({ cleared: z.literal(true) }).strict(),
},
];
export const frontendActionRegistry = definitions.map((item) => item.manifest);
export const getFrontendActionDefinition = (name: string) => definitions.find((item) => item.manifest.id === name);
export const frontendActionRegistryResponse = { schema_version: "frontend-action-registry@1" as const, actions: frontendActionRegistry };
export const frontendActionRegistry = definitions.map(item => item.manifest);
export const getFrontendActionDefinition = (name: string) => definitions.find(item => item.manifest.id === name);
export const frontendActionRegistryResponse = { schema_version: "frontend-action-registry@2" as const, actions: frontendActionRegistry };