export type CatalogWidget = | "text" | "textarea" | "number" | "select" | "radio" | "boolean" | "file" | "folder" | "element" | "code" | "secret" | "region" | "hidden"; export interface CatalogField { key: string; label: string; widget: CatalogWidget; type: "string" | "number" | "boolean" | "object"; displayType: string; required: boolean; defaultValue: string; placeholder: string; description: string; advanced: boolean; valueTypes: string[]; /** 下拉/单选选项:字符串(显示=存储值)或 { label, value }(显示与存储分离) */ options?: Array; unit?: string; } export interface CatalogOutput { label: string; key: string; suggestedName: string; valueTypes: string[]; fields: Array<{ key: string; label: string; defaultValue: string; valueTypes: string[] }>; } export interface CatalogComponent { id: string; name: string; kind: "vendor" | "native" | "trigger"; group: string; categories: string[]; categorySignature: string; description: string; messageTemplate: string; baseType: string; functionName: string; functionCalls: Array<{ function: string; args: string[]; assignTo: string }>; version: string; tags: string[]; platformTag: string | null; icon: string; risk: "low" | "high"; fields: CatalogField[]; output: CatalogOutput; slots: string[]; nodeKind?: string; /** 同名旧版本 / 重复别名指向的保留能力 ID。 */ duplicateOf?: string; } export interface CatalogGroup { id: string; label: string; icon: string; count: number; } export interface Catalog { schemaVersion: number; generatedAt: string; counts: { total: number; vendor: number; native: number; triggers: number; fields: number; }; groups: CatalogGroup[]; components: CatalogComponent[]; } export interface FlowVariable { name: string; type: string; defaultValue?: string; description?: string; scope?: "flow" | "local"; } export interface FlowNodeOptions { outputVar?: string; delayBeforeMs?: number; delayAfterMs?: number; timeoutMs?: number; retryCount?: number; retryIntervalMs?: number; skipOnError?: boolean; logException?: boolean; errorRouteEnabled?: boolean; } export interface FlowNode { id: string; componentId: string; label?: string; position: { x: number; y: number }; values: Record; options: FlowNodeOptions; parentId?: string | null; slot?: string; enabled?: boolean; width?: number; height?: number; } export interface FlowEdge { id: string; source: string; target: string; sourceHandle?: string | null; targetHandle?: string | null; label?: string; } export interface FlowDefinition { schemaVersion: 1; id: string; name: string; description?: string; enabled?: boolean; trigger?: { type: "manual" | "schedule" | "file"; label: string; }; variables: FlowVariable[]; nodes: FlowNode[]; edges: FlowEdge[]; meta: { createdAt: string; updatedAt: string; appVersion: string; }; } export interface FlowSaveOptions { /** 编辑器加载任务时磁盘上的 updatedAt,用于外部修改冲突检测 */ expectedUpdatedAt?: string; /** 为 true 时跳过冲突检测,强制覆盖磁盘 */ force?: boolean; } export type FlowSaveResult = | { conflict: false; flow: FlowDefinition } | { conflict: true; diskFlow: FlowDefinition }; export interface FlowSummary { id: string; name: string; updatedAt: string; nodeCount: number; enabled?: boolean; triggerLabel?: string; lastRunAt?: string; lastRunStatus?: "succeeded" | "failed" | "cancelled" | "never"; lastRunDurationMs?: number; lastRunError?: string; } export interface AppInfo { version: string; electron: string; chrome: string; node: string; userData: string; catalogPath: string; packaged: boolean; fixtureUrl?: string; } export interface AiServiceConfig { baseUrl: string; apiKey: string; model: string; /** 桌面 AI 拾取使用的视觉模型;留空时复用 model。 */ visionModel?: string; } export interface AppSettings { browserElementWaitMs: number; aiConfig: AiServiceConfig; } export interface ElementPickResult { selector: string; tag: string; id?: string; className?: string; name?: string; type?: string; placeholder?: string; label?: string; text?: string; value?: string; url: string; } export interface RunNodeState { nodeId: string; status: "idle" | "running" | "succeeded" | "failed" | "skipped" | "cancelled"; startedAt?: string; finishedAt?: string; durationMs?: number; output?: unknown; error?: RunError; evidence?: string[]; attempt?: number; } export interface RunError { code: string; reason: string; recommendation?: string; location?: { flowId?: string; nodeId?: string; componentId?: string; arg?: string }; detail?: string; } export interface RunEvent { type: | "run.started" | "node.started" | "node.log" | "node.finished" | "run.finished" | "run.total"; runId: string; nodeId?: string; level?: "info" | "warn" | "error"; message?: string; status?: string; output?: unknown; error?: RunError; durationMs?: number; evidence?: string[]; attempt?: number; startedAt?: string; nodeLabel?: string; total?: number; } export interface VisionLabSource { id: string; kind: "window" | "screen"; name: string; thumbnail: string; icon?: string; } export interface VisionLabBox { x: number; y: number; width: number; height: number; category: string; confidence: number; } export interface VisionLabDetectRequest { sourceId: string; modelPath?: string; scoreThreshold?: number; iouThreshold?: number; } export interface VisionLabDetection { image: string; width: number; height: number; boxes: VisionLabBox[]; captureMs: number; detectMs: number; modelPath: string; sourceName: string; }