feat: 接入本地执行内核与运行面板

This commit is contained in:
yu committed 2026-10-26 17:41:39 +08:00
1 parent 32ca0a1af8
commit 6a2cc24898
23 files changed
+2937 -41

No files matched your search

+86
View File
@@ -0,0 +1,86 @@
import { app, type WebContents } from "electron";
import fs from "node:fs";
import path from "node:path";
import type { FlowDefinition, RunEvent } from "../../shared/types";
import { loadCatalog } from "../catalog";
import { runFlow } from "../engine/runtime";
import { BrowserSession } from "../browser/session";
const active = new Map<string, AbortController>();
let currentRunId: string | null = null;
function newRunId(): string {
return `run_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
}
export function startRun(flow: FlowDefinition, sender: WebContents): { runId: string } {
if (currentRunId) active.get(currentRunId)?.abort();
const runId = newRunId();
const root = path.join(app.getPath("userData"), "runs", runId);
const evidenceDir = path.join(root, "evidence");
fs.mkdirSync(evidenceDir, { recursive: true });
const controller = new AbortController();
active.set(runId, controller);
currentRunId = runId;
const catalog = loadCatalog();
const components = new Map(catalog.components.map((item) => [item.id, item]));
const emit = (event: Omit<RunEvent, "runId">) => {
if (!sender.isDestroyed()) sender.send("run:event", { ...event, runId });
};
const log: (message: string, level?: "info" | "warn" | "error", nodeId?: string) => void = (
message,
level = "info",
nodeId
) => emit({ type: "node.log", nodeId, level, message });
void (async () => {
try {
const result = await runFlow(flow, {
runId,
evidenceDir,
signal: controller.signal,
emit,
log,
componentOf: (node) => components.get(node.componentId)
});
fs.writeFileSync(path.join(root, "run.json"), JSON.stringify(result, null, 2), "utf8");
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
emit({ type: "run.finished", status: "failed", durationMs: 0, error: { code: "unknown", reason: message } });
fs.writeFileSync(
path.join(root, "run.json"),
JSON.stringify({ runId, status: "failed", error: message }, null, 2),
"utf8"
);
} finally {
active.delete(runId);
if (currentRunId === runId) currentRunId = null;
}
})();
return { runId };
}
export function stopRun(runId?: string): boolean {
const id = runId || currentRunId;
if (!id) return false;
const controller = active.get(id);
if (!controller) return false;
controller.abort();
return true;
}
export async function closeBrowserSession(): Promise<void> {
if (globalThis.__rpaBrowserWindow && !globalThis.__rpaBrowserWindow.isDestroyed()) {
const session = await BrowserSession.create();
await session.close();
}
}
export function isRunning(): boolean {
return currentRunId !== null;
}