- Server: wrap licenseKeys.activate() in try/catch — if activation limit
reached, treat as already-activated and proceed to store the key
- Web: switch activateFromCheckout from form() to command() pattern for
programmatic invocation with proper error handling
- Activate page: auto-fires on mount, shows spinner/error states, retry button
- Use $env/dynamic/private instead of process.env (SvelteKit convention)
- Fail fast if USESEND_API_KEY is missing
- Await sendEmail with try/catch + console.error (was silently discarded)
- Fallback for user.name in email greeting
- Fix open redirect on login redirect param
- Add license activation flow: server validates/activates keys via Polar SDK
- Auto-activate from Polar checkout redirect (checkout_id in URL)
- Gate dashboard behind license activation (redirect to /activate if no plan)
- Preserve redirect URL through login flow for post-purchase activation
- Show plan status badge in sidebar and overview page
- Add account section to settings (email, plan, license key)
- Add svelte-sonner toasts with custom black theme and iconify icons
- Improve validation messages across all forms
- Update API key prefix to droidclaw_
- Add cursor pointer globally for clickable elements
- Support Polar sandbox mode via POLAR_SANDBOX env flag
Railway proxy closes idle DB connections after ~60s, causing
CONNECTION_CLOSED errors on stale sockets. Set idle_timeout=20s and
max_lifetime=5m so postgres-js recycles connections before they die.
Also fix sendCommand to fall back to persistent device ID on reconnect.
Cookie forwarding between dash.droidclaw.ai and tunnel.droidclaw.ai was
unreliable. Now the web app passes userId + shared internal secret via
headers. Also removes debug logging from device auth and session middleware.
- Android: fetch installed apps via PackageManager, send to server on connect
- Android: add QUERY_ALL_PACKAGES permission for full app visibility
- Android: fix duplicate Intent import, increase accessibility retry window
- Android: default server URL to ws:// instead of wss://
- Server: store installed apps in device metadata JSONB
- Server: inject installed apps context into LLM prompt
- Server: preprocessor resolves app names from device's actual installed apps
- Server: add POST /goals/stop endpoint with AbortController cancellation
- Server: rewrite session middleware to direct DB token lookup
- Server: goals route fetches user's saved LLM config from DB
- Web: show installed apps in device detail Overview tab with search
- Web: add Stop button for running goals
- Web: replace API routes with remote commands (submitGoal, stopGoal)
- Web: add error display for goal submission failures
- Shared: add InstalledApp type and apps message to protocol
- Add device/session/step DB persistence in server agent loop
- Add goal preprocessor for compound goals (e.g., "open YouTube and search X")
- Add step-level logging to agent loop
- Fix dashboard WebSocket auth (direct DB token lookup instead of auth.api)
- Fix web layout to use locals.session.token instead of cookie
- Add dashboard-ws.svelte.ts WebSocket store with auto-reconnect
- Rewrite devices page with direct DB queries and real-time updates
- Add device detail page with live step display and session history
- Add Android companion app resources, themes, and screen capture consent
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add deleteKeySchema for proper FormData validation
- Return { deleted: true } from deleteKey for change tracking
- Use $state for keysPromise to refresh list after create/delete
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>