Security:
- validateWebhookUrl() rejects non-http/https and private/internal IPs
- Validate webhook URL on workflow create and update
- Replace error.message in all HTTP 500 responses with 'Internal server error'
- Add requireJSON middleware (HTTP 415 if Content-Type wrong) on POST/PUT routes
- Reject missing API keys in worker heartbeat (not just wrong ones)
- Validate prompt response against allowed options before accepting
Bugs fixed:
- goto infinite loop protection: stepVisits[] counter, fails at GOTO_MAX_VISITS (100)
- wait step: validate duration (no NaN/negative), cap at WAIT_STEP_MAX_MS (24h)
- _executionPrompts now stores {resolve, options} for option validation
- JSON.parse wrapped in try/catch: workflows/:id, executions/:id, internal/executions/:id, POST /api/executions, scheduler worker_ids
- pong handler uses ws.dbWorkerId (set on connect) not message.worker_id
- Worker disconnect now marks worker offline in DB and broadcasts update
- command validation (type + empty check) on POST /api/workers/:id/command
- workflow_id required check on POST /api/executions
Performance & reliability:
- markStaleWorkersOffline() runs every 60s, marks workers without recent heartbeat offline
- Named constants: PROMPT_TIMEOUT_MS, COMMAND_TIMEOUT_MS, QUICK_CMD_TIMEOUT_MS,
WEBHOOK_TIMEOUT_MS, WAIT_STEP_MAX_MS, GOTO_MAX_VISITS, WORKER_STALE_MINUTES
New features:
- GET /api/health (auth required): version, uptime, worker counts
- DELETE /api/executions/completed: bulk delete finished executions (admin)
- schedule_value positive-integer validation for interval/hourly schedule types
- Request logging middleware: [HTTP] METHOD /path STATUS Xms
Code quality:
- All console.log on error paths changed to console.error
- Removed stray debug console.log in POST /api/workflows
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Rate limiting: 300 req/15min general, 20 req/min on POST /api/executions
- Cron schedule type support using cron-parser for full cron expressions
- Webhook notifications: POST to workflow webhook_url on execution complete/failed
- Dry-run mode: simulate workflow execution without running any commands
- Global execution timeout via EXECUTION_MAX_MINUTES env var (default 60min)
- Execution filtering: status, workflow_id, started_by, after, before, search
- Event-driven command result delivery (replaces 500ms DB polling)
- Atomic log appends via JSON_ARRAY_APPEND (no read-modify-write race)
- Separate browserClients/workerClients sets (workers no longer receive broadcasts)
- Stale execution cleanup on startup (mark running→failed after crash)
- Scheduler overlap prevention (skip if same workflow already running)
- Frontend: webhook_url field in create/edit workflow modals
- Frontend: dry-run checkbox in workflow param modal
- Frontend: ESC closes modals, ws.onerror handler added
- Frontend: selectedExecutions changed from Array to Set (O(1) ops)
- Frontend: XSS fixes via escapeHtml() on all user-controlled innerHTML
- Frontend: param modal keydown listener deduplication fix
- Remove unused npm packages (bcryptjs, body-parser, cors, js-yaml, jsonwebtoken)
- Add express-rate-limit and cron-parser dependencies
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Automated executions (started_by gandalf: or scheduler:) no longer
trigger success/failure toast alerts for connected browser users.
Server now includes is_automated flag in command_result broadcasts.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Worker does not echo command_id back in command_result message.
Previously this caused all workflow steps to time out after 120s.
Now: find the command_sent entry for the commandId, then take the
next command_result after it — safe since steps run sequentially.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- server.js: add authenticateGandalf middleware (X-Gandalf-API-Key header)
and two internal endpoints used by Gandalf link diagnostics:
POST /api/internal/command — submit SSH command to a worker, returns execution_id
GET /api/internal/executions/:id — poll execution status/logs
Also tag automated executions as started_by 'gandalf:*' / 'scheduler:*';
add hide_internal query param to GET /api/executions; change cleanup
from daily/30d to hourly/1d to keep execution history lean
- index.html: add Manual / Automated sub-tabs on Execution History tab so
Gandalf diagnostic runs don't clutter the manual run view; persists
selected tab to localStorage; dashboard recent-run strip filters to
manual runs only; sub-tabs show live counts
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This will help diagnose the 'Cannot read properties of undefined' error
by logging each step of the workflow creation process.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Removed old/obsolete workflow execution system that was conflicting
with the new executeWorkflowSteps() engine:
Removed:
- activeExecutions Map (old tracking system)
- executeWorkflow() - old workflow executor
- executeNextStep() - old step processor
- executeCommandStep() - old command executor (duplicate)
- handleUserInput() - unimplemented prompt handler
- Duplicate app.post('/api/executions') endpoint
- app.post('/api/executions/:id/respond') endpoint
This was causing "Cannot read properties of undefined (reading 'target')"
error because the old code was being called instead of the new engine.
The new executeWorkflowSteps() engine is now the only workflow system.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Added comprehensive command scheduling system:
Backend:
- New scheduled_commands database table
- Scheduler processor runs every minute
- Support for three schedule types: interval, hourly, daily
- calculateNextRun() function for intelligent scheduling
- API endpoints: GET, POST, PUT (toggle), DELETE
- Executions automatically created and tracked
- Enable/disable schedules without deleting
Frontend:
- New Scheduler tab in navigation
- Create Schedule modal with worker selection
- Dynamic schedule input based on type
- Schedule list showing status, next/last run times
- Enable/Disable toggle for each schedule
- Delete schedule functionality
- Terminal-themed scheduler UI
- Integration with existing worker and execution systems
Schedule Types:
- Interval: Every X minutes (e.g., 30 for every 30 min)
- Hourly: Every X hours (e.g., 2 for every 2 hours)
- Daily: At specific time (e.g., 03:00 for 3 AM daily)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
Server-side:
- Added automatic cleanup of old executions (runs daily)
- Configurable retention period via EXECUTION_RETENTION_DAYS env var (default: 30 days)
- Cleanup runs on server startup and every 24 hours
- Only cleans completed/failed executions, keeps running ones
- Added pagination support to /api/executions endpoint
- Returns total count, limit, offset, and hasMore flag
Client-side:
- Implemented "Load More" button for execution pagination
- Loads 50 executions at a time
- Appends additional executions when "Load More" clicked
- Shows total execution count info
- Backward compatible with old API format
Benefits:
- Automatic database maintenance
- Prevents execution table from growing indefinitely
- Better performance with large execution histories
- User can browse all executions via pagination
- Configurable retention policy per deployment
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Problem:
- Workers generate random UUID on startup (runtime ID)
- Database stores workers with persistent IDs (database ID)
- UI sends commands using database ID
- Server couldn't find worker connection (stored by runtime ID)
- Result: 400 Bad Request "Worker not connected"
Solution:
- When worker connects, look up database ID by worker name
- Store WebSocket connection in Map using BOTH IDs:
* Runtime ID (from worker_connect message)
* Database ID (from database lookup by name)
- Commands from UI use database ID → finds correct WebSocket
- Cleanup both IDs when worker disconnects
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
- Removed duplicate /api/executions/:id endpoint that didn't parse logs
- Added workers Map to track worker_id -> WebSocket connection
- Store worker connections when they send worker_connect message
- Send commands to specific worker instead of broadcasting to all clients
- Clean up workers Map when worker disconnects
- Update execution status to completed/failed when command results arrive
- Add proper error handling when worker is not connected
Fixes:
- execution.logs.forEach is not a function (logs now properly parsed)
- Commands stuck in "running" status (now update to completed/failed)
- Commands not reaching workers (now sent to specific worker WebSocket)
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
- Removed all migration code from server.js
- Database schema fixed directly via MySQL:
* Dropped users.role column (SSO only)
* Dropped users.password column (SSO only)
* Added executions.started_by column
* Added workflows.created_by column
* All tables now match expected schema
- Server startup will be faster without migrations
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
- Drop password column from users table (SSO authentication only)
- PULSE uses Authelia SSO, not password-based authentication
- Fixes 500 error: Field 'password' doesn't have a default value
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
- Add display_name, email, and groups columns to existing users table
- Handle MariaDB lack of IF NOT EXISTS in ALTER TABLE
- Gracefully skip columns that already exist
- Fixes 500 error when authenticating users
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
- Modified executions table schema to allow NULL workflow_id
- Removed foreign key constraint that prevented NULL values
- Added migration to update existing table structure
- Quick commands can now be stored without a workflow reference
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Changes:
- Create execution record in database when quick command is sent
- Store initial log entry with command details
- Broadcast execution_started event to update UI
- Display quick commands as "[Quick Command]" in execution list
- Fix worker communication to properly track all executions
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>