UNPKG

gensx

Version:
137 lines 7.71 kB
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime"; import { createWriteStream } from "node:fs"; import { writeFile } from "node:fs/promises"; import { Box, Static, Text, useApp } from "ink"; import { useCallback, useState } from "react"; import { EnvironmentResolver } from "../components/EnvironmentResolver.js"; import { ErrorMessage } from "../components/ErrorMessage.js"; import { FirstTimeSetup } from "../components/FirstTimeSetup.js"; import { LoadingSpinner } from "../components/LoadingSpinner.js"; import { useProjectName } from "../hooks/useProjectName.js"; import { getAuth } from "../utils/config.js"; import { USER_AGENT } from "../utils/user-agent.js"; export const RunWorkflowUI = ({ workflowName, options }) => { const { exit } = useApp(); const [phase, setPhase] = useState("resolveEnv"); const [error, setError] = useState(null); const [logLines, setLogLines] = useState([]); const [streamContent, setStreamContent] = useState(""); const [workflowOutput, setWorkflowOutput] = useState(null); const { loading, error: projectError, projectName, isFromConfig, } = useProjectName(options.project); if (options.output && !options.wait) { return (_jsx(ErrorMessage, { message: "Output file cannot be specified without --wait." })); } const runWorkflow = useCallback(async (environment) => { try { setPhase("running"); const auth = await getAuth(); if (!auth) { throw new Error("Not authenticated. Please run 'gensx login' first."); } const inputJson = JSON.parse(options.input); // Decide which endpoint to hit based on --wait flag const basePath = `/org/${auth.org}/projects/${encodeURIComponent(projectName)}/environments/${encodeURIComponent(environment)}/workflows/${encodeURIComponent(workflowName)}`; const url = new URL(options.wait ? basePath : `${basePath}/start`, auth.apiBaseUrl); const response = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json", Authorization: `Bearer ${auth.token}`, "User-Agent": USER_AGENT, }, body: JSON.stringify(inputJson), }); if (response.status >= 400) { const errorBody = (await response.json().catch(() => ({}))); throw new Error(`Failed to start workflow (${response.status} ${response.statusText})\n\n${errorBody.error ?? ""}`); } if (!options.wait) { const body = (await response.json()); setLogLines((ls) => [ ...ls, `Workflow execution started with id: ${body.executionId}`, ]); setPhase("done"); exit(); return; } // WAIT=true path – handle streaming vs JSON response const isStream = response.headers .get("Content-Type") ?.includes("stream"); if (isStream) { setPhase("streaming"); await handleStream(response.body, options.output, setStreamContent); exit(); } else { const body = (await response.json()); if (body.executionStatus === "failed") { throw new Error("Workflow failed"); } // Write or display output if (options.output) { await writeFile(options.output, JSON.stringify(body.output, null, 2)); setLogLines((ls) => [ ...ls, `Workflow output written to ${options.output}`, ]); } else { setWorkflowOutput(body.output); } setPhase("done"); setTimeout(() => { exit(); }, 100); } } catch (err) { setError(err.message); setPhase("error"); setTimeout(() => { exit(); }, 100); } }, [workflowName, options, projectName, exit]); // Streaming helper — outside render tree const handleStream = async (stream, outputPath, setContent) => { if (!stream) { throw new Error("No stream returned by server"); } const reader = stream.getReader(); const decoder = new TextDecoder(); let fileStream; if (outputPath) { fileStream = createWriteStream(outputPath); setContent(`Streaming response output to ${outputPath}\n`); } let done = false; while (!done) { const { value, done: readerDone } = await reader.read(); if (readerDone) { done = true; } else { const chunk = decoder.decode(value); if (fileStream) { fileStream.write(chunk); } else { setContent((prev) => prev + chunk); } } } fileStream?.end(); }; return (_jsxs(Box, { flexDirection: "column", gap: 1, children: [_jsx(FirstTimeSetup, {}), options.output && !options.wait && (_jsx(ErrorMessage, { message: "Output file cannot be specified without --wait." })), !(options.output && !options.wait) && (error ?? projectError) && (_jsx(ErrorMessage, { message: error ?? projectError?.message ?? "Unknown error" })), !(options.output && !options.wait) && !(error ?? projectError) && (loading || !projectName) && (_jsx(LoadingSpinner, { message: "Resolving project..." })), !(options.output && !options.wait) && !(error ?? projectError) && !(loading || !projectName) && (_jsxs(_Fragment, { children: [isFromConfig && phase === "resolveEnv" && (_jsxs(Text, { children: [_jsx(Text, { color: "cyan", children: "\u2139" }), " Using project name from gensx.yaml:", " ", _jsx(Text, { color: "cyan", children: projectName })] })), phase === "resolveEnv" && (_jsx(EnvironmentResolver, { projectName: projectName, specifiedEnvironment: options.env, allowCreate: false, yes: options.yes, onResolved: (env) => { void runWorkflow(env); } })), phase === "running" && (_jsx(LoadingSpinner, { message: "Running workflow..." })), phase === "streaming" && (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "white", bold: true, children: "Streaming output:" }), _jsx(Box, { children: _jsx(Text, { color: "cyan", children: streamContent }) })] })), phase === "done" && (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "green", bold: true, children: "\u2714" }), _jsx(Text, { children: " Workflow execution completed" })] }), workflowOutput !== null && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { color: "white", children: "Workflow Output:" }), _jsx(Box, { children: _jsx(Text, { color: "cyan", children: typeof workflowOutput === "string" ? workflowOutput : JSON.stringify(workflowOutput, null, 2) }) })] }))] })), logLines.length > 0 && (_jsx(Static, { items: logLines, children: (line, index) => _jsx(Text, { children: line }, index) }))] }))] })); }; //# sourceMappingURL=run.js.map