FN-7038: add live automation run output
Add configurable automation tool access with live manual-run streaming for schedules and routines. - Add an automation tool allowlist model and dashboard selectors for AI prompt steps. - Stream manual automation and routine run progress, output, step updates, and tool events over SSE. - Surface live run transcripts in automation/routine cards and document the operator workflow. - Cover validation, runner callbacks, API streaming, and UI behavior with targeted tests. Files changed: .changeset/fn-7038-automation-tools-live-output.md | 7 + docs/dashboard-guide.md | 9 + packages/core/src/__tests__/automation.test.ts | 27 +- packages/core/src/automation.ts | 18 + packages/core/src/index.ts | 4 +- packages/core/src/types.ts | 6 + packages/dashboard/app/api/legacy.ts | 48 +++ packages/dashboard/app/components/RoutineCard.tsx | 16 +- packages/dashboard/app/components/ScheduleForm.tsx | 53 ++- .../app/components/ScheduleStepsEditor.tsx | 49 ++- .../app/components/ScheduledTasksModal.tsx | 72 +++- packages/dashboard/app/components/ScriptsModal.css | 79 ++++ .../app/components/__tests__/RoutineCard.test.tsx | 45 +++ .../app/components/__tests__/ScheduleForm.test.tsx | 100 +++++- .../__tests__/ScheduleStepsEditor.test.tsx | 67 +++- .../__tests__/ScheduledTasksModal.test.tsx | 36 +- .../src/__tests__/routes-automation.test.ts | 151 +++++++- packages/dashboard/src/routes.ts | 400 ++++++++++++++++++++- packages/dashboard/src/server.ts | 7 +- packages/engine/src/__tests__/cron-runner.test.ts | 56 ++- .../src/__tests__/pi-create-fn-agent.test.ts | 44 +++ .../engine/src/__tests__/routine-runner.test.ts | 28 ++ packages/engine/src/cron-runner.ts | 20 +- packages/engine/src/pi.ts | 32 +- packages/engine/src/routine-runner.ts | 39 +- packages/i18n/locales/en/app.json | 9 + packages/i18n/src/resources.d.ts | 21 ++ 27 files changed, 1374 insertions(+), 69 deletions(-) Fusion-Task-Id: FN-7038 Fusion-Task-Lineage: 7f5871d0-1de0-4d22-a9c9-ee9418658677
This commit is contained in:
@@ -5142,6 +5142,21 @@ export function reorderAutomationSteps(id: string, stepIds: string[], options?:
|
||||
export interface RoutineRunResponse {
|
||||
routine: Routine;
|
||||
result: RoutineExecutionResult;
|
||||
liveRunId?: string;
|
||||
}
|
||||
|
||||
export type RoutineRunStreamEvent =
|
||||
| { type: "run"; runId?: string; scheduleId?: string; status?: string }
|
||||
| { type: "step"; runId?: string; stepIndex?: number; stepId?: string; stepName?: string; stepType?: string; status?: string; success?: boolean; error?: string }
|
||||
| { type: "output"; runId?: string; text?: string }
|
||||
| { type: "tool"; runId?: string; status?: string; name?: string; args?: unknown; isError?: boolean; result?: unknown }
|
||||
| { type: "complete"; runId?: string; result?: RoutineExecutionResult }
|
||||
| { type: "error"; runId?: string; message?: string; result?: RoutineExecutionResult };
|
||||
|
||||
export interface RoutineRunStreamHandlers {
|
||||
onEvent: (event: RoutineRunStreamEvent) => void;
|
||||
onConnectionStateChange?: (state: StreamConnectionState) => void;
|
||||
onFatalError?: (message: string) => void;
|
||||
}
|
||||
|
||||
export function fetchRoutines(options?: SchedulingScopeOptions): Promise<Routine[]> {
|
||||
@@ -5180,6 +5195,39 @@ export function runRoutine(id: string, options?: SchedulingScopeOptions): Promis
|
||||
});
|
||||
}
|
||||
|
||||
export function streamRoutineRun(id: string, handlers: RoutineRunStreamHandlers, options?: SchedulingScopeOptions & { runId?: string }) {
|
||||
const baseUrl = withSchedulingScope(`/routines/${id}/run/stream`, options);
|
||||
const separator = baseUrl.includes("?") ? "&" : "?";
|
||||
const url = options?.runId ? `${baseUrl}${separator}runId=${encodeURIComponent(options.runId)}` : baseUrl;
|
||||
const parse = (type: RoutineRunStreamEvent["type"], event: MessageEvent) => {
|
||||
let data: Record<string, unknown> = {};
|
||||
try {
|
||||
data = event.data ? JSON.parse(event.data) : {};
|
||||
} catch {
|
||||
data = { message: event.data };
|
||||
}
|
||||
handlers.onEvent({ type, ...data } as RoutineRunStreamEvent);
|
||||
};
|
||||
return createResilientEventSource(
|
||||
url,
|
||||
{
|
||||
events: {
|
||||
run: (event) => parse("run", event),
|
||||
step: (event) => parse("step", event),
|
||||
output: (event) => parse("output", event),
|
||||
tool: (event) => parse("tool", event),
|
||||
complete: (event) => parse("complete", event),
|
||||
error: (event) => parse("error", event),
|
||||
},
|
||||
},
|
||||
{
|
||||
maxReconnectAttempts: 2,
|
||||
onConnectionStateChange: handlers.onConnectionStateChange,
|
||||
onFatalError: handlers.onFatalError,
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export function fetchRoutineRuns(id: string, options?: SchedulingScopeOptions): Promise<RoutineExecutionResult[]> {
|
||||
return api<RoutineExecutionResult[]>(withSchedulingScope(`/routines/${id}/runs`, options));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user