- Added RECORDING_TIMEOUT_MS and recordingTimeouts Map to track sessions
- Timer starts after browser is added to pool; emits recording-timeout
socket event then destroys the remote browser after 1s delay (gives
frontend time to handle the event before socket teardown)
- clearRecordingTimeout() exported and called in destroyRemoteBrowser
and the /stop/:browserId route to cancel timer on normal save/discard
- RecordingPage listens for recording-timeout: broadcasts via
BroadcastChannel then calls window.close(); falls back to
navigate('/robots') if close is blocked by the browser
- PageWrapper (always mounted in original tab) listens on
BroadcastChannel, navigates back to the page the recording was
started from (stored in sessionStorage), and shows warning notification
- RobotCreate stores current pathname in sessionStorage before opening
the recording tab so PageWrapper can navigate back on timeout
- Added timeout_discarded translation key to all 6 locale files
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Move LLM duplicate-name check before the expensive LLM call to avoid
burning API time/cost on a name that will be rejected anyway
- Hoist trimmedName declaration in PUT endpoint to eliminate the
redundant second name.trim() call outside the validation block
- Guard recording_meta.name type in Generator pre-check so legacy rows
with missing/non-string names don't throw; normalize both sides to
match the DB index semantics (lower + trim)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Reject whitespace-only names in all create endpoints (scrape, crawl,
search, llm) using explicit string trim + fallback instead of ||.trim()
- Validate name type and emptiness in PUT endpoint with 400 responses
- Catch SequelizeUniqueConstraintError in all create/update/Generator
paths and return 409/emit nameExists to close TOCTOU race window
- Add blank-name guard in Generator.saveNewWorkflow before DB write
- Propagate isDuplicate flag from updateRecording for i18n-aware error
display in RobotEditPage
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add isRobotNameTaken() helper with case-insensitive, trimmed comparison
using lower(trim(recording_meta->>'name')) in Sequelize queries
- Add 409 uniqueness checks in POST /recordings/scrape, /llm, /crawl,
/search, PUT /recordings/:id (edit), and POST /recordings/:id/duplicate
- Trim all robot names before uniqueness check and before saving
- Add DB migration: unique index robot_user_name_unique on
("userId", lower(trim(recording_meta->>'name')))
- Generator.ts: trim fileName, case-insensitive conflict check before
creating new robot, emit fileSaved { actionType: 'nameExists' } on conflict
- Frontend API: re-throw server error messages for all create/update/
duplicate functions so callers surface the conflict message
- RobotCreate: check for duplicate name before optimistic update + navigation
for LLM robot flow; try-catch for scrape/crawl/search flows
- RobotEditPage: use error.message in catch to surface conflict
- RobotDuplicatePage: add Robot Name field (pre-filled, auto-updates with URL)
- SaveRecording: replace confirm-to-overwrite with hard block using
case-insensitive trimmed check; handle nameExists socket action
- Add name_exists and robot_duplication name translations for all 6 locales
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>