Fix prompt buttons, add command output to prompt steps, add worker to repo

- Fix onclick buttons broken by JSON.stringify double-quotes inside HTML
  attributes — use data-opt attribute + this.dataset.opt instead
- Track last command stdout in execution state when command_result arrives
- executePromptStep: include last command output in log entry and broadcast
  so users can review results alongside the question in the same view
- GET /api/executions/🆔 propagate output field to pending prompt response
- Add .prompt-output CSS class for scrollable terminal-style output block
- Fix MariaDB CAST(? AS JSON) → JSON_EXTRACT(?, '$') (MariaDB 10.11 compat)
- Add worker/worker.js to repo (deployed on pulse-worker-01 / LXC 153)
  Fix: worker was not echoing command_id back in result — resolvers always
  got undefined, causing every workflow step to timeout and fail

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-17 19:06:02 -04:00
parent 3f6e04d1ab
commit 2290d52f8b
3 changed files with 297 additions and 8 deletions

View File

@@ -188,7 +188,7 @@ async function initDatabase() {
[exec.id]
);
await connection.query(
"UPDATE executions SET logs = JSON_ARRAY_APPEND(COALESCE(logs, '[]'), '$', CAST(? AS JSON)) WHERE id = ?",
"UPDATE executions SET logs = JSON_ARRAY_APPEND(COALESCE(logs, '[]'), '$', JSON_EXTRACT(?, '$')) WHERE id = ?",
[JSON.stringify({ action: 'server_restart_recovery', message: 'Execution marked failed due to server restart', timestamp: new Date().toISOString() }), exec.id]
);
}
@@ -415,6 +415,12 @@ wss.on('connection', (ws) => {
}
}
// Store last command output in execution state so prompt steps can surface it
const execStateForOutput = _executionState.get(execution_id);
if (execStateForOutput) {
execStateForOutput.state._lastCommandOutput = stdout || '';
}
// Broadcast result to browser clients only
broadcast({
type: 'command_result',
@@ -564,7 +570,7 @@ function broadcast(data) {
async function addExecutionLog(executionId, logEntry) {
try {
await pool.query(
"UPDATE executions SET logs = JSON_ARRAY_APPEND(COALESCE(logs, '[]'), '$', CAST(? AS JSON)) WHERE id = ?",
"UPDATE executions SET logs = JSON_ARRAY_APPEND(COALESCE(logs, '[]'), '$', JSON_EXTRACT(?, '$')) WHERE id = ?",
[JSON.stringify(logEntry), executionId]
);
} catch (error) {
@@ -899,15 +905,21 @@ async function executePromptStep(executionId, step, stepNumber) {
const message = step.message || 'Please choose an option:';
const options = step.options || ['Continue'];
await addExecutionLog(executionId, {
// 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 = {
step: stepNumber, step_name: step.name, action: 'prompt',
message, options, timestamp: new Date().toISOString()
});
};
if (lastOutput) logEntry.output = lastOutput;
await addExecutionLog(executionId, logEntry);
broadcast({
type: 'execution_prompt',
execution_id: executionId,
prompt: { message, options, step: stepNumber, step_name: step.name }
prompt: { message, options, step: stepNumber, step_name: step.name, output: lastOutput || undefined }
});
return new Promise(resolve => {
@@ -1648,7 +1660,11 @@ app.get('/api/executions/:id', authenticateSSO, async (req, res) => {
if (waitingForInput) {
for (let i = parsedLogs.length - 1; i >= 0; i--) {
if (parsedLogs[i].action === 'prompt') {
pendingPrompt = { message: parsedLogs[i].message, options: parsedLogs[i].options };
pendingPrompt = {
message: parsedLogs[i].message,
options: parsedLogs[i].options,
output: parsedLogs[i].output || null
};
break;
}
}