Add parse/route step types, wider modal, automated link troubleshooter
- Add 'parse' step type: reads KEY=VALUE lines from last command output into execution state
- Add 'route' step type: evaluates JS conditions list, auto-jumps to first match with no user input
- Support condition field on parse steps (skip when conditional execute was also skipped)
- Widen execution detail modal to min(1100px, 96vw) to eliminate horizontal scrolling
- Show last command output in prompt boxes so user has context before clicking
- Add formatLogEntry support for parse_complete and route_taken log actions
- Apply applyParams() substitution to prompt messages ({{server_name}}, {{iface}}, etc.)
- Fix prompt button onclick using data-opt attribute to avoid JSON double-quote breakage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
49
server.js
49
server.js
@@ -855,6 +855,47 @@ async function executeWorkflowSteps(executionId, workflowId, definition, usernam
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
await new Promise(r => setTimeout(r, ms));
|
||||
|
||||
} else if (step.type === 'parse') {
|
||||
// Parse KEY=VALUE lines from last command output into execution state.
|
||||
// Matches lines of the form: UPPER_KEY=value (key must start with uppercase letter)
|
||||
const condOkParse = !step.condition || evalCondition(step.condition, execState?.state || {}, execState?.params || {});
|
||||
const parseState = condOkParse ? _executionState.get(executionId) : null;
|
||||
if (parseState) {
|
||||
const output = parseState.state._lastCommandOutput || '';
|
||||
const parsed = {};
|
||||
for (const line of output.split('\n')) {
|
||||
const m = line.match(/^([A-Z][A-Z0-9_]*)=(.*)$/);
|
||||
if (m) {
|
||||
const k = m[1].toLowerCase();
|
||||
const v = m[2].trim();
|
||||
parsed[k] = v;
|
||||
parseState.state[k] = v;
|
||||
}
|
||||
}
|
||||
await addExecutionLog(executionId, {
|
||||
step: currentIndex + 1, step_name: stepLabel, action: 'parse_complete',
|
||||
parsed_count: Object.keys(parsed).length, parsed,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
} else if (step.type === 'route') {
|
||||
// Evaluate conditions in order; jump to the first match (no user input needed).
|
||||
const routeState = execState?.state || {};
|
||||
const routeParams = execState?.params || {};
|
||||
let routeTaken = null;
|
||||
for (const cond of (step.conditions || [])) {
|
||||
const matches = cond.default || evalCondition(cond.if || 'false', routeState, routeParams);
|
||||
if (matches) { routeTaken = cond; gotoId = cond.goto || null; break; }
|
||||
}
|
||||
await addExecutionLog(executionId, {
|
||||
step: currentIndex + 1, step_name: stepLabel, action: 'route_taken',
|
||||
condition: routeTaken?.if || 'default',
|
||||
label: routeTaken?.label || null,
|
||||
goto: gotoId,
|
||||
timestamp: new Date().toISOString()
|
||||
});
|
||||
}
|
||||
|
||||
await addExecutionLog(executionId, {
|
||||
@@ -902,11 +943,15 @@ async function executeWorkflowSteps(executionId, workflowId, definition, usernam
|
||||
// Pause execution and wait for user to respond via POST /api/executions/:id/respond.
|
||||
// Resolves with the chosen option string, or null on 60-minute timeout.
|
||||
async function executePromptStep(executionId, step, stepNumber) {
|
||||
const message = step.message || 'Please choose an option:';
|
||||
const execState = _executionState.get(executionId);
|
||||
// Apply param substitution to the message so {{server_name}}, {{iface}} etc. work
|
||||
let message = step.message || 'Please choose an option:';
|
||||
if (execState && Object.keys(execState.params).length > 0) {
|
||||
message = applyParams(message, execState.params);
|
||||
}
|
||||
const options = step.options || ['Continue'];
|
||||
|
||||
// Include the last command output so the user can review results alongside the question
|
||||
const execState = _executionState.get(executionId);
|
||||
const lastOutput = (execState?.state?._lastCommandOutput) || null;
|
||||
|
||||
const logEntry = {
|
||||
|
||||
Reference in New Issue
Block a user