gensx
Version:
`GenSX command line tools.
137 lines • 7.71 kB
JavaScript
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