diff --git a/apps/ui/eslint.config.mjs b/apps/ui/eslint.config.mjs
index d7bc54d4..6db837e3 100644
--- a/apps/ui/eslint.config.mjs
+++ b/apps/ui/eslint.config.mjs
@@ -70,6 +70,8 @@ const eslintConfig = defineConfig([
AbortSignal: 'readonly',
Audio: 'readonly',
ScrollBehavior: 'readonly',
+ URL: 'readonly',
+ URLSearchParams: 'readonly',
// Timers
setTimeout: 'readonly',
setInterval: 'readonly',
diff --git a/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx b/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx
index c7d8f26b..27cc38d8 100644
--- a/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx
+++ b/apps/ui/src/components/views/board-view/worktree-panel/components/worktree-actions-dropdown.tsx
@@ -143,8 +143,12 @@ export function WorktreeActionsDropdown({
Dev Server Running (:{devServerInfo?.port})
- onOpenDevServerUrl(worktree)} className="text-xs">
-
+ onOpenDevServerUrl(worktree)}
+ className="text-xs"
+ aria-label={`Open dev server on port ${devServerInfo?.port} in browser`}
+ >
+
Open in Browser
onOpenDevServerUrl(worktree)}
- title={`Open dev server (port ${devServerInfo?.port})`}
- >
-
-
+
+
+
+
+
+
+
Open dev server (:{devServerInfo?.port})
+
+
+
)}
{
const serverInfo = runningDevServers.get(getWorktreeKey(worktree));
- if (serverInfo) {
- window.open(serverInfo.url, '_blank');
+ if (!serverInfo) {
+ logger.warn('No dev server info found for worktree:', getWorktreeKey(worktree));
+ toast.error('Dev server not found', {
+ description: 'The dev server may have stopped. Try starting it again.',
+ });
+ return;
+ }
+
+ try {
+ // Rewrite URL hostname to match the current browser's hostname.
+ // This ensures dev server URLs work when accessing Automaker from
+ // remote machines (e.g., 192.168.x.x or hostname.local instead of localhost).
+ const devServerUrl = new URL(serverInfo.url);
+
+ // Security: Only allow http/https protocols to prevent potential attacks
+ // via data:, javascript:, file:, or other dangerous URL schemes
+ if (devServerUrl.protocol !== 'http:' && devServerUrl.protocol !== 'https:') {
+ logger.error('Invalid dev server URL protocol:', devServerUrl.protocol);
+ toast.error('Invalid dev server URL', {
+ description: 'The server returned an unsupported URL protocol.',
+ });
+ return;
+ }
+
+ devServerUrl.hostname = window.location.hostname;
+ window.open(devServerUrl.toString(), '_blank');
+ } catch (error) {
+ logger.error('Failed to parse dev server URL:', error);
+ toast.error('Failed to open dev server', {
+ description: 'The server URL could not be processed. Please try again.',
+ });
}
},
[runningDevServers, getWorktreeKey]