303 lines
8.5 KiB
TypeScript
303 lines
8.5 KiB
TypeScript
import React, { useState, useRef, useEffect } from "react";
|
|
import { MdClose } from "react-icons/md";
|
|
import { scriptsApi } from "../api/scripts.api";
|
|
import { useTerminalStore } from "@/modules/terminal/store/useTerminalStore";
|
|
import { useAgentStore } from "@/app/providers/layout/store/agent.store";
|
|
|
|
interface RunScriptModalProps {
|
|
scriptPath: string;
|
|
scriptId: number;
|
|
onClose: () => void;
|
|
}
|
|
|
|
export const RunScriptModal: React.FC<RunScriptModalProps> = ({
|
|
scriptPath,
|
|
scriptId,
|
|
onClose,
|
|
}) => {
|
|
const [selectedAgentIdx, setSelectedAgentIdx] = useState(0);
|
|
const [stdinValue, setStdinValue] = useState("");
|
|
const [loading, setLoading] = useState(false);
|
|
const [error, setError] = useState<string | null>(null);
|
|
const inputRef = useRef<HTMLSelectElement>(null);
|
|
|
|
const agents = useAgentStore((s) => s.agents);
|
|
const addJob = useTerminalStore((s) => s.addJob);
|
|
const openTerminal = useTerminalStore((s) => s.openTerminal);
|
|
|
|
const selectedAgent = agents[selectedAgentIdx];
|
|
|
|
useEffect(() => {
|
|
inputRef.current?.focus();
|
|
}, []);
|
|
|
|
const handleRun = async () => {
|
|
if (!selectedAgent) {
|
|
setError("No agents available");
|
|
return;
|
|
}
|
|
|
|
setLoading(true);
|
|
setError(null);
|
|
|
|
try {
|
|
// 1. Запускаем скрипт
|
|
const runResult = await scriptsApi.runScript(scriptId, {
|
|
stdin: stdinValue,
|
|
token: selectedAgent.token,
|
|
});
|
|
|
|
// 2. Добавляем джоб в терминал
|
|
addJob({
|
|
id: runResult.id,
|
|
scriptPath,
|
|
command: runResult.command,
|
|
});
|
|
|
|
// 3. Открываем терминал
|
|
openTerminal();
|
|
|
|
// 4. Ждём завершения по id
|
|
const jobResult = await scriptsApi.waitJob(runResult.id);
|
|
|
|
// 5. Обновляем существующий джоб (не создаём новый!)
|
|
const terminalStore = useTerminalStore.getState();
|
|
terminalStore.updateJob(runResult.id, {
|
|
command: jobResult.command,
|
|
stdin: jobResult.stdin,
|
|
status: jobResult.status,
|
|
stdout: jobResult.stdout,
|
|
stderr: jobResult.stderr,
|
|
isRunning: false,
|
|
});
|
|
|
|
onClose();
|
|
} catch (e: any) {
|
|
console.error("Failed to run script:", e);
|
|
setError(e?.response?.data?.detail || "Failed to run script");
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
};
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
position: "fixed",
|
|
top: 0,
|
|
left: 0,
|
|
right: 0,
|
|
bottom: 0,
|
|
backgroundColor: "rgba(0, 0, 0, 0.5)",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
zIndex: 2000,
|
|
}}
|
|
onClick={onClose}
|
|
>
|
|
<div
|
|
style={{
|
|
backgroundColor: "var(--card-bg)",
|
|
border: "1px solid var(--border)",
|
|
borderRadius: "8px",
|
|
width: "420px",
|
|
maxWidth: "90vw",
|
|
boxShadow: "0 8px 32px rgba(0, 0, 0, 0.4)",
|
|
}}
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
{/* Header */}
|
|
<div
|
|
style={{
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "space-between",
|
|
padding: "16px 20px",
|
|
borderBottom: "1px solid var(--border)",
|
|
}}
|
|
>
|
|
<h3
|
|
style={{
|
|
margin: 0,
|
|
color: "var(--text-primary)",
|
|
fontSize: "14px",
|
|
fontWeight: 600,
|
|
}}
|
|
>
|
|
Run Script
|
|
</h3>
|
|
<button
|
|
onClick={onClose}
|
|
style={{
|
|
background: "transparent",
|
|
border: "none",
|
|
color: "var(--text-secondary)",
|
|
cursor: "pointer",
|
|
padding: "4px",
|
|
borderRadius: "4px",
|
|
display: "flex",
|
|
}}
|
|
>
|
|
<MdClose size={18} />
|
|
</button>
|
|
</div>
|
|
|
|
{/* Content */}
|
|
<div style={{ padding: "20px" }}>
|
|
{/* Script path */}
|
|
<div style={{ marginBottom: "16px" }}>
|
|
<label
|
|
style={{
|
|
display: "block",
|
|
color: "var(--text-secondary)",
|
|
fontSize: "12px",
|
|
marginBottom: "6px",
|
|
}}
|
|
>
|
|
Script
|
|
</label>
|
|
<div
|
|
style={{
|
|
padding: "8px 12px",
|
|
backgroundColor: "var(--bg-secondary)",
|
|
borderRadius: "4px",
|
|
color: "var(--text-primary)",
|
|
fontSize: "13px",
|
|
fontFamily: "monospace",
|
|
border: "1px solid var(--border)",
|
|
}}
|
|
>
|
|
{scriptPath}
|
|
</div>
|
|
</div>
|
|
|
|
{/* Agent selector */}
|
|
<div style={{ marginBottom: "16px" }}>
|
|
<label
|
|
style={{
|
|
display: "block",
|
|
color: "var(--text-secondary)",
|
|
fontSize: "12px",
|
|
marginBottom: "6px",
|
|
}}
|
|
>
|
|
Agent <span style={{ color: "#f44747" }}>*</span>
|
|
</label>
|
|
<select
|
|
ref={inputRef}
|
|
value={selectedAgentIdx}
|
|
onChange={(e) => setSelectedAgentIdx(Number(e.target.value))}
|
|
style={{
|
|
width: "100%",
|
|
padding: "8px 12px",
|
|
backgroundColor: "var(--input-bg)",
|
|
border: "1px solid var(--border)",
|
|
borderRadius: "4px",
|
|
color: "var(--text-primary)",
|
|
fontSize: "13px",
|
|
outline: "none",
|
|
}}
|
|
>
|
|
{agents.length === 0 && (
|
|
<option value="">No agents available</option>
|
|
)}
|
|
{agents.map((agent, idx) => (
|
|
<option key={agent.label} value={idx}>
|
|
{agent.label}
|
|
</option>
|
|
))}
|
|
</select>
|
|
</div>
|
|
|
|
{/* Stdin (optional) */}
|
|
<div style={{ marginBottom: "16px" }}>
|
|
<label
|
|
style={{
|
|
display: "block",
|
|
color: "var(--text-secondary)",
|
|
fontSize: "12px",
|
|
marginBottom: "6px",
|
|
}}
|
|
>
|
|
Stdin <span style={{ color: "#858585" }}>(optional)</span>
|
|
</label>
|
|
<textarea
|
|
value={stdinValue}
|
|
onChange={(e) => setStdinValue(e.target.value)}
|
|
placeholder="Enter input data..."
|
|
rows={4}
|
|
style={{
|
|
width: "100%",
|
|
padding: "8px 12px",
|
|
backgroundColor: "var(--input-bg)",
|
|
border: "1px solid var(--border)",
|
|
borderRadius: "4px",
|
|
color: "var(--text-primary)",
|
|
fontSize: "13px",
|
|
fontFamily: "monospace",
|
|
resize: "vertical",
|
|
outline: "none",
|
|
}}
|
|
/>
|
|
</div>
|
|
|
|
{/* Error */}
|
|
{error && (
|
|
<div
|
|
style={{
|
|
padding: "8px 12px",
|
|
backgroundColor: "rgba(244, 71, 71, 0.1)",
|
|
border: "1px solid #f44747",
|
|
borderRadius: "4px",
|
|
color: "#f44747",
|
|
fontSize: "12px",
|
|
marginBottom: "16px",
|
|
}}
|
|
>
|
|
{error}
|
|
</div>
|
|
)}
|
|
|
|
{/* Run button */}
|
|
<button
|
|
onClick={handleRun}
|
|
disabled={loading || !selectedAgent}
|
|
style={{
|
|
width: "100%",
|
|
padding: "10px",
|
|
backgroundColor: loading || !selectedAgent ? "#555" : "#0e639c",
|
|
border: "none",
|
|
borderRadius: "4px",
|
|
color: "#ffffff",
|
|
fontSize: "13px",
|
|
fontWeight: 500,
|
|
cursor: loading || !selectedAgent ? "not-allowed" : "pointer",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
justifyContent: "center",
|
|
gap: "8px",
|
|
}}
|
|
>
|
|
{loading ? (
|
|
<>
|
|
<span
|
|
style={{
|
|
display: "inline-block",
|
|
animation: "spin 1s linear infinite",
|
|
}}
|
|
>
|
|
⏳
|
|
</span>
|
|
Running...
|
|
</>
|
|
) : (
|
|
<>▶ Run</>
|
|
)}
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|