feat(remote): add desktop SSH remote instances lifecycle and UX (#515)

* feat(remote): add desktop SSH remote instances lifecycle and settings UX

* fix(remote): cancel SSH modal connect and correct desktop runtime detection

* fix(remote): unblock ssh auth flow and disconnect on instance removal

* fix(remote-ssh): harden remote lifecycle, auth probing, and port forwarding reliability

Improve SSH remote stability by correctly handling authenticated external probes, accepting ControlMaster handoff behavior, making reconnect detection tunnel-aware, and applying extra forwards through the shared master connection with local listener validation.

---------

Co-authored-by: Bohdan Triapitsyn <artmore@protonmail.com>
This commit is contained in:
shekohex
2026-02-26 20:26:12 +02:00
committed by GitHub
co-authored by Bohdan Triapitsyn
parent 5250a20578
commit 27321d454b
12 changed files with 6335 additions and 128 deletions
@@ -27,6 +27,7 @@ import {
RiListUnordered,
RiRobot2Line,
RiRestartLine,
RiServerLine,
RiSlashCommands2,
} from '@remixicon/react';
import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip';
@@ -41,6 +42,8 @@ import { SkillsSidebar } from '@/components/sections/skills/SkillsSidebar';
import { SkillsPage } from '@/components/sections/skills/SkillsPage';
import { ProjectsSidebar } from '@/components/sections/projects/ProjectsSidebar';
import { ProjectsPage } from '@/components/sections/projects/ProjectsPage';
import { RemoteInstancesSidebar } from '@/components/sections/remote-instances/RemoteInstancesSidebar';
import { RemoteInstancesPage } from '@/components/sections/remote-instances/RemoteInstancesPage';
import { ProvidersSidebar } from '@/components/sections/providers/ProvidersSidebar';
import { ProvidersPage } from '@/components/sections/providers/ProvidersPage';
import { UsageSidebar } from '@/components/sections/usage/UsageSidebar';
@@ -51,7 +54,7 @@ import { OpenChamberPage } from '@/components/sections/openchamber/OpenChamberPa
import { AboutSettings } from '@/components/sections/openchamber/AboutSettings';
import { McpIcon } from '@/components/icons/McpIcon';
import { useDeviceInfo } from '@/lib/device';
import { isVSCodeRuntime, isWebRuntime } from '@/lib/desktop';
import { isDesktopShell, isVSCodeRuntime, isWebRuntime } from '@/lib/desktop';
import { reloadOpenCodeConfiguration } from '@/stores/useAgentsStore';
import {
SETTINGS_PAGE_METADATA,
@@ -85,6 +88,7 @@ const pageOrder: SettingsPageSlug[] = [
'shortcuts',
'git',
'projects',
'remote-instances',
'agents',
'commands',
'mcp',
@@ -97,7 +101,7 @@ const pageOrder: SettingsPageSlug[] = [
function buildRuntimeContext(isDesktop: boolean): SettingsRuntimeContext {
const isVSCode = isVSCodeRuntime();
const isWeb = isWebRuntime();
const isWeb = !isDesktop && isWebRuntime();
return { isVSCode, isWeb, isDesktop };
}
@@ -112,6 +116,8 @@ function getSettingsNavIcon(slug: SettingsPageSlug): React.ComponentType<{ class
switch (slug) {
case 'projects':
return RiFoldersLine;
case 'remote-instances':
return RiServerLine;
case 'appearance':
return RiPaletteLine;
case 'chat':
@@ -245,8 +251,7 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
const containerRef = React.useRef<HTMLDivElement>(null);
const isDesktopApp = React.useMemo(() => {
if (typeof window === 'undefined') return false;
return Boolean((window as unknown as { __TAURI__?: unknown }).__TAURI__);
return isDesktopShell();
}, []);
// keep platform check available for future window chrome tweaks
@@ -379,6 +384,8 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
switch (slug) {
case 'projects':
return <ProjectsSidebar onItemSelect={opts.onItemSelect} />;
case 'remote-instances':
return <RemoteInstancesSidebar onItemSelect={opts.onItemSelect} />;
case 'agents':
return <AgentsSidebar onItemSelect={opts.onItemSelect} />;
case 'commands':
@@ -407,6 +414,8 @@ export const SettingsView: React.FC<SettingsViewProps> = ({ onClose, forceMobile
return <SettingsHome onOpen={openPage} />;
case 'projects':
return <ProjectsPage />;
case 'remote-instances':
return <RemoteInstancesPage />;
case 'agents':
return <AgentsPage />;
case 'commands':