Refine assembly editor layout and remove script_lines

This commit is contained in:
2025-10-03 15:30:44 -06:00
parent 2cbe82e498
commit c07c5c0bcc
4 changed files with 144 additions and 105 deletions

View File

@@ -5,14 +5,6 @@
"category": "script", "category": "script",
"type": "powershell", "type": "powershell",
"script": "# Define the file path\n$filePath = \"C:\\Canary.txt\"\n\n# Write some content into the file\n\"SYSTEM Canary is alive.\"ddss | Out-File -FilePath $filePath -Encoding UTF8\n", "script": "# Define the file path\n$filePath = \"C:\\Canary.txt\"\n\n# Write some content into the file\n\"SYSTEM Canary is alive.\"ddss | Out-File -FilePath $filePath -Encoding UTF8\n",
"script_lines": [
"# Define the file path",
"$filePath = \"C:\\Canary.txt\"",
"",
"# Write some content into the file",
"\"SYSTEM Canary is alive.\"ddss | Out-File -FilePath $filePath -Encoding UTF8",
""
],
"timeout_seconds": 3600, "timeout_seconds": 3600,
"sites": { "sites": {
"mode": "specific", "mode": "specific",

View File

@@ -212,13 +212,11 @@ function fromServerDocument(doc = {}, defaultType = "powershell") {
assembly.description = doc.description || ""; assembly.description = doc.description || "";
assembly.category = doc.category || assembly.category; assembly.category = doc.category || assembly.category;
assembly.type = doc.type || assembly.type; assembly.type = doc.type || assembly.type;
if (Array.isArray(doc.script_lines)) { const legacyScript = Array.isArray(doc.script_lines)
assembly.script = doc.script_lines ? doc.script_lines.map((line) => (line == null ? "" : String(line))).join("\n")
.map((line) => (line == null ? "" : String(line))) : "";
.join("\n"); const script = doc.script ?? doc.content ?? legacyScript;
} else { assembly.script = typeof script === "string" ? script : legacyScript;
assembly.script = doc.script ?? doc.content ?? "";
}
const timeout = doc.timeout_seconds ?? doc.timeout ?? assembly.timeoutSeconds; const timeout = doc.timeout_seconds ?? doc.timeout ?? assembly.timeoutSeconds;
assembly.timeoutSeconds = Number.isFinite(Number(timeout)) assembly.timeoutSeconds = Number.isFinite(Number(timeout))
? Number(timeout) ? Number(timeout)
@@ -238,7 +236,6 @@ function toServerDocument(assembly) {
const normalizedScript = typeof assembly.script === "string" const normalizedScript = typeof assembly.script === "string"
? assembly.script.replace(/\r\n/g, "\n") ? assembly.script.replace(/\r\n/g, "\n")
: ""; : "";
const scriptLines = normalizedScript ? normalizedScript.split("\n") : [];
const timeoutNumeric = Number(assembly.timeoutSeconds); const timeoutNumeric = Number(assembly.timeoutSeconds);
const timeoutSeconds = Number.isFinite(timeoutNumeric) ? Math.max(0, Math.round(timeoutNumeric)) : 3600; const timeoutSeconds = Number.isFinite(timeoutNumeric) ? Math.max(0, Math.round(timeoutNumeric)) : 3600;
return { return {
@@ -248,7 +245,6 @@ function toServerDocument(assembly) {
category: assembly.category || "script", category: assembly.category || "script",
type: assembly.type || "powershell", type: assembly.type || "powershell",
script: normalizedScript, script: normalizedScript,
script_lines: scriptLines,
timeout_seconds: timeoutSeconds, timeout_seconds: timeoutSeconds,
sites: { sites: {
mode: assembly.sites?.mode === "specific" ? "specific" : "all", mode: assembly.sites?.mode === "specific" ? "specific" : "all",
@@ -728,12 +724,16 @@ export default function AssemblyEditor({
value={assembly.description} value={assembly.description}
onChange={(e) => updateAssembly({ description: e.target.value })} onChange={(e) => updateAssembly({ description: e.target.value })}
multiline multiline
minRows={3} minRows={2}
maxRows={8}
fullWidth fullWidth
variant="outlined" variant="outlined"
sx={{ sx={{
...INPUT_BASE_SX, ...INPUT_BASE_SX,
"& .MuiOutlinedInput-inputMultiline": { padding: "4px 8px" } "& .MuiOutlinedInput-inputMultiline": {
padding: "6px 12px",
lineHeight: 1.4
}
}} }}
/> />
</Grid> </Grid>
@@ -906,109 +906,162 @@ export default function AssemblyEditor({
sx={{ sx={{
display: "flex", display: "flex",
flexWrap: "wrap", flexWrap: "wrap",
alignItems: "center", alignItems: "stretch",
gap: 2 gap: { xs: 2, lg: 1.5 },
pt: 1
}} }}
> >
<Box sx={{ flex: { xs: "1 1 100%", md: "0 1 23%" } }}> <Box sx={{ flex: { xs: "1 1 100%", lg: "0 1 30%" }, minWidth: { lg: 220 } }}>
<TextField <Tooltip
label="Variable Name" title="This is the name of the variable you will be referencing inside of the script. Within the script you will reference this variable with a prefixed \"$env:<variable>\". For example, a variable named \"message\" would be written in the script as \"$env:message\"."
value={variable.name} arrow
onChange={(e) => updateVariable(variable.id, { name: e.target.value })} placement="top-start"
fullWidth
variant="outlined"
sx={INPUT_BASE_SX}
/>
</Box>
<Box sx={{ flex: { xs: "1 1 100%", md: "0 1 18%" } }}>
<TextField
label="Display Label"
value={variable.label}
onChange={(e) => updateVariable(variable.id, { label: e.target.value })}
fullWidth
variant="outlined"
sx={INPUT_BASE_SX}
/>
</Box>
<Box sx={{ flex: { xs: "1 1 100%", md: "0 1 18%" } }}>
<TextField
select
fullWidth
label="Type"
value={variable.type}
onChange={(e) => updateVariable(variable.id, { type: e.target.value })}
sx={SELECT_BASE_SX}
SelectProps={{ MenuProps: MENU_PROPS }}
> >
{VARIABLE_TYPE_OPTIONS.map((opt) => ( <TextField
<MenuItem key={opt.key} value={opt.key}>{opt.label}</MenuItem> label="Variable"
))} value={variable.name}
</TextField> onChange={(e) => updateVariable(variable.id, { name: e.target.value })}
fullWidth
variant="outlined"
sx={INPUT_BASE_SX}
/>
</Tooltip>
</Box>
<Box sx={{ flex: { xs: "1 1 100%", lg: "0 1 22%" }, minWidth: { lg: 180 } }}>
<Tooltip
title="This is the name that will be given to the variable and seen by Borealis server operators."
arrow
placement="top-start"
>
<TextField
label="Display Label"
value={variable.label}
onChange={(e) => updateVariable(variable.id, { label: e.target.value })}
fullWidth
variant="outlined"
sx={INPUT_BASE_SX}
/>
</Tooltip>
</Box>
<Box sx={{ flex: { xs: "1 1 100%", lg: "0 1 22%" }, minWidth: { lg: 160 } }}>
<Tooltip
title="This defines the type of variable data the script should expect."
arrow
placement="top-start"
>
<TextField
select
fullWidth
label="Type"
value={variable.type}
onChange={(e) => updateVariable(variable.id, { type: e.target.value })}
sx={SELECT_BASE_SX}
SelectProps={{ MenuProps: MENU_PROPS }}
>
{VARIABLE_TYPE_OPTIONS.map((opt) => (
<MenuItem key={opt.key} value={opt.key}>{opt.label}</MenuItem>
))}
</TextField>
</Tooltip>
</Box> </Box>
<Box <Box
sx={{ sx={{
flex: { xs: "1 1 100%", md: "0 1 27%" }, flex: { xs: "1 1 100%", lg: "0 1 40%" },
minWidth: { lg: 220 },
display: "flex", display: "flex",
alignItems: "center" alignItems: "center"
}} }}
> >
{variable.type === "boolean" ? ( {variable.type === "boolean" ? (
<FormControlLabel <Tooltip
control={ title="This is the value that will be pre-populated in the assembly when ran. Use a sensible default value."
<Checkbox arrow
checked={Boolean(variable.defaultValue)} placement="top-start"
onChange={(e) => updateVariable(variable.id, { defaultValue: e.target.checked })} >
sx={{ color: "#58a6ff" }} <FormControlLabel
/> control={
} <Checkbox
label="Default Value" checked={Boolean(variable.defaultValue)}
sx={{ onChange={(e) => updateVariable(variable.id, { defaultValue: e.target.checked })}
color: "#9ba3b4", sx={{ color: "#58a6ff" }}
m: 0, />
"& .MuiFormControlLabel-label": { fontSize: "0.95rem" } }
}} label="Default Value"
/> sx={{
color: "#9ba3b4",
m: 0,
"& .MuiFormControlLabel-label": { fontSize: "0.95rem" }
}}
/>
</Tooltip>
) : ( ) : (
<TextField <Tooltip
label="Default Value" title="This is the value that will be pre-populated in the assembly when ran. Use a sensible default value."
value={variable.defaultValue ?? ""} arrow
onChange={(e) => updateVariable(variable.id, { defaultValue: e.target.value })} placement="top-start"
fullWidth >
variant="outlined" <TextField
sx={INPUT_BASE_SX} label="Default Value"
/> value={variable.defaultValue ?? ""}
onChange={(e) => updateVariable(variable.id, { defaultValue: e.target.value })}
fullWidth
variant="outlined"
sx={INPUT_BASE_SX}
/>
</Tooltip>
)} )}
</Box> </Box>
<Box <Box
sx={{ sx={{
flex: { xs: "1 1 100%", md: "0 0 6%" }, flex: { xs: "1 1 100%", lg: "0 0 100px" },
minWidth: { lg: 100 },
display: "flex", display: "flex",
flexDirection: "column",
alignItems: "center",
justifyContent: "center", justifyContent: "center",
alignItems: "center" gap: 0.5
}} }}
> >
<Tooltip title="Required"> <Typography variant="caption" sx={{ color: "#9ba3b4", fontSize: "0.75rem" }}>
<Checkbox Required
checked={Boolean(variable.required)} </Typography>
onChange={(e) => updateVariable(variable.id, { required: e.target.checked })} <Checkbox
sx={{ color: "#58a6ff" }} checked={Boolean(variable.required)}
onChange={(e) => updateVariable(variable.id, { required: e.target.checked })}
sx={{ color: "#58a6ff", p: 0.5 }}
inputProps={{ "aria-label": "Required" }}
/>
</Box>
<Box sx={{ flex: { xs: "1 1 100%", lg: "1 1 0%" }, minWidth: { lg: 220 } }}>
<Tooltip
title="Instruct the operator in why this variable exists and how to set it appropriately."
arrow
placement="top-start"
>
<TextField
label="Description"
value={variable.description}
onChange={(e) => updateVariable(variable.id, { description: e.target.value })}
fullWidth
variant="outlined"
sx={INPUT_BASE_SX}
/> />
</Tooltip> </Tooltip>
</Box> </Box>
<Box sx={{ flex: { xs: "1 1 100%", md: "1 1 0%" }, minWidth: { md: 120 } }}> <Box
<TextField sx={{
label="Description" flex: { xs: "1 1 100%", lg: "0 0 auto" },
value={variable.description} display: "flex",
onChange={(e) => updateVariable(variable.id, { description: e.target.value })} justifyContent: "flex-end",
fullWidth alignItems: "center",
variant="outlined" ml: { lg: "auto" }
sx={INPUT_BASE_SX} }}
/> >
</Box> <Tooltip title="Remove this Variable." arrow>
<Box sx={{ flex: "1 1 100%", display: "flex", justifyContent: "flex-end" }}> <IconButton onClick={() => removeVariable(variable.id)} sx={{ color: "#ff6b6b" }}>
<IconButton onClick={() => removeVariable(variable.id)} sx={{ color: "#ff6b6b" }}> <DeleteIcon />
<DeleteIcon /> </IconButton>
</IconButton> </Tooltip>
</Box> </Box>
</Box> </Box>
</Paper> </Paper>

View File

@@ -182,7 +182,6 @@ class JobScheduler:
"category": "application" if default_type == "ansible" else "script", "category": "application" if default_type == "ansible" else "script",
"type": default_type, "type": default_type,
"script": "", "script": "",
"script_lines": [],
"variables": [], "variables": [],
"files": [], "files": [],
"timeout_seconds": 3600, "timeout_seconds": 3600,
@@ -217,7 +216,6 @@ class JobScheduler:
doc["script"] = content_val doc["script"] = content_val
normalized_script = (doc["script"] or "").replace("\r\n", "\n") normalized_script = (doc["script"] or "").replace("\r\n", "\n")
doc["script"] = normalized_script doc["script"] = normalized_script
doc["script_lines"] = normalized_script.split("\n") if normalized_script else []
try: try:
timeout_raw = data.get("timeout_seconds", data.get("timeout")) timeout_raw = data.get("timeout_seconds", data.get("timeout"))
if timeout_raw is None: if timeout_raw is None:
@@ -271,7 +269,6 @@ class JobScheduler:
content = "" content = ""
normalized_script = (content or "").replace("\r\n", "\n") normalized_script = (content or "").replace("\r\n", "\n")
doc["script"] = normalized_script doc["script"] = normalized_script
doc["script_lines"] = normalized_script.split("\n") if normalized_script else []
return doc return doc
def _ansible_root(self) -> str: def _ansible_root(self) -> str:

View File

@@ -682,7 +682,6 @@ def _empty_assembly_document(default_type: str = "powershell") -> Dict[str, Any]
"category": "application" if (default_type or "").lower() == "ansible" else "script", "category": "application" if (default_type or "").lower() == "ansible" else "script",
"type": default_type or "powershell", "type": default_type or "powershell",
"script": "", "script": "",
"script_lines": [],
"timeout_seconds": 3600, "timeout_seconds": 3600,
"sites": {"mode": "all", "values": []}, "sites": {"mode": "all", "values": []},
"variables": [], "variables": [],
@@ -718,7 +717,6 @@ def _normalize_assembly_document(obj: Any, default_type: str, base_name: str) ->
doc["script"] = content_val doc["script"] = content_val
normalized_script = (doc["script"] or "").replace("\r\n", "\n") normalized_script = (doc["script"] or "").replace("\r\n", "\n")
doc["script"] = normalized_script doc["script"] = normalized_script
doc["script_lines"] = normalized_script.split("\n") if normalized_script else []
timeout_val = obj.get("timeout_seconds", obj.get("timeout")) timeout_val = obj.get("timeout_seconds", obj.get("timeout"))
if timeout_val is not None: if timeout_val is not None:
try: try:
@@ -798,7 +796,6 @@ def _load_assembly_document(abs_path: str, island: str, type_hint: str = "") ->
doc["name"] = base_name doc["name"] = base_name
normalized_script = (content or "").replace("\r\n", "\n") normalized_script = (content or "").replace("\r\n", "\n")
doc["script"] = normalized_script doc["script"] = normalized_script
doc["script_lines"] = normalized_script.split("\n") if normalized_script else []
if default_type == "ansible": if default_type == "ansible":
doc["category"] = "application" doc["category"] = "application"
return doc return doc