feat: harden local shell, SFTP, and desktop session reliability

Finder-launched terminals now inherit a login PATH so tools like ollama resolve, destructive SFTP/editor actions require confirmation, and terminal search plus window dragging work again. SSH forwards and RDP sessions keep listeners and survive reconnect without FastPath crashes.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Logan.LiandCursor committed 2026-08-16 16:08:11 +08:00
1 parent 188f3556ed
commit 08696896f9
223 files changed
+13493 -3423

No files matched your search

+132 -54
View File
@@ -7,7 +7,7 @@ import type { ConnectionState } from '../../stores/connection'
import { useConnectionPanelStore } from '../../stores/connectionPanelStore'
import { useSessionStore, useEntitlementStore } from '../../stores'
import type { HostMetrics } from '../../../shared/types/host-metrics.types'
import { formatLocaleTime } from '../../i18n/format'
import { formatLocaleTime, translateErrorMessage } from '../../i18n/format'
import { useToast } from '../../hooks/useToast'
import ForwardRuleDrawer, {
DEFAULT_DYNAMIC_PORT,
@@ -17,8 +17,18 @@ import ForwardRuleDrawer, {
import {
parseForwardRules,
portForwardStorageKey,
resolveForwardTargetHost,
resolveLocalBindHost,
resolveRemoteBindHost,
type ForwardRule,
} from '../connection/forwardRules'
import {
forgetForwardIntent,
forwardIntentKey,
rememberForwardIntent,
restoreForwardIntent,
tunnelCoversRule,
} from '../connection/forwardIntent'
import type { SSHForwardStatus } from '../../../shared/types/ipc.types'
// ============== Status Display Config ==============
@@ -34,37 +44,37 @@ const STATUS_CONFIG: Record<string, Omit<StatusDisplay, 'labelKey'> & { labelKey
connected: {
icon: <Wifi className="w-3 h-3" />,
labelKey: 'status.connected',
colorClass: 'text-green-400',
colorClass: 'text-feedback-success',
animateClass: '',
},
connecting: {
icon: <Loader2 className="w-3 h-3 animate-spin" />,
labelKey: 'status.connecting',
colorClass: 'text-yellow-400',
colorClass: 'text-feedback-warning',
animateClass: 'animate-pulse',
},
background: {
icon: <Wifi className="w-3 h-3" />,
labelKey: 'status.background',
colorClass: 'text-green-400',
colorClass: 'text-feedback-success',
animateClass: '',
},
suspended: {
icon: <WifiOff className="w-3 h-3" />,
labelKey: 'status.suspended',
colorClass: 'text-yellow-400',
colorClass: 'text-feedback-warning',
animateClass: '',
},
reconnecting: {
icon: <Loader2 className="w-3 h-3 animate-spin" />,
labelKey: 'status.reconnecting',
colorClass: 'text-yellow-400',
colorClass: 'text-feedback-warning',
animateClass: 'animate-pulse',
},
error: {
icon: <AlertCircle className="w-3 h-3" />,
labelKey: 'status.error',
colorClass: 'text-red-400',
colorClass: 'text-feedback-error',
animateClass: '',
},
disconnected: {
@@ -127,7 +137,7 @@ function formatCpuPercent(pct: number): string {
function pctClass(pct?: number): string {
if (pct == null) return 'text-txt-faint'
if (pct >= WARN_PCT) return 'text-amber-400'
if (pct >= WARN_PCT) return 'text-feedback-warning'
return 'text-txt-muted'
}
@@ -382,7 +392,6 @@ export default function StatusBar() {
connectionState: undefined,
hostLabel: undefined,
stats: undefined,
tabKind: activeTab?.type as string | undefined,
connectionId: undefined,
isRemoteSsh: false,
sessionOsPretty: undefined as string | undefined,
@@ -399,7 +408,6 @@ export default function StatusBar() {
: activeTab.title
: activeTab.title,
stats: undefined,
tabKind: activeTab.type,
connectionId: undefined,
isRemoteSsh: false,
sessionOsPretty: undefined as string | undefined,
@@ -411,7 +419,6 @@ export default function StatusBar() {
connectionState: mapTabStatusToConnectionState(activeTab.connectionStatus),
hostLabel: t('terminal:toolbar.labelLocal'),
stats: undefined,
tabKind: activeTab.type,
connectionId: undefined,
isRemoteSsh: false,
sessionOsPretty: undefined as string | undefined,
@@ -431,7 +438,6 @@ export default function StatusBar() {
connectionState: conn.state,
hostLabel: `${conn.config.username}@${conn.config.host}:${conn.config.port || 22}`,
stats: conn.stats,
tabKind: activeTab.type,
connectionId: instance.connectionId,
isRemoteSsh: true,
sessionOsPretty,
@@ -444,7 +450,6 @@ export default function StatusBar() {
connectionState: mapTabStatusToConnectionState(activeTab.connectionStatus),
hostLabel: `${session.username}@${session.host}:${session.port || 22}`,
stats: undefined,
tabKind: activeTab.type,
connectionId: instance?.connectionId,
isRemoteSsh: Boolean(instance?.connectionId),
sessionOsPretty,
@@ -461,7 +466,6 @@ export default function StatusBar() {
connectionState: state,
hostLabel: label,
stats: undefined,
tabKind: activeTab.type,
connectionId: instance?.connectionId,
isRemoteSsh: Boolean(instance?.connectionId),
sessionOsPretty,
@@ -481,6 +485,11 @@ export default function StatusBar() {
return portForwardStorageKey(activeTab?.sessionId, activeTabId, connectionId)
}, [activeTab?.sessionId, activeTabId, connectionId])
const intentKey = useMemo(
() => (activeTabId ? forwardIntentKey(activeTab?.sessionId, activeTabId) : null),
[activeTab?.sessionId, activeTabId]
)
const forwardRuntimes = useMemo(() => {
const map = new Map<string, { running: boolean; boundPort?: number }>()
for (const tunnel of forwardTunnels) {
@@ -489,10 +498,13 @@ export default function StatusBar() {
boundPort: tunnel.boundPort,
})
}
const dynamic = forwardRules.find((rule) => rule.type === 'dynamic')
const dynamicTunnel = forwardTunnels.find((tunnel) => tunnel.kind === 'dynamic' && tunnel.running)
if (dynamic && dynamicTunnel && !map.get(dynamic.id)?.running) {
map.set(dynamic.id, { running: true, boundPort: dynamicTunnel.boundPort })
// Rule ids can diverge from runtime ids (reconnect / legacy). Match by kind+listen port.
for (const rule of forwardRules) {
if (map.get(rule.id)?.running) continue
const tunnel = forwardTunnels.find((item) => tunnelCoversRule([item], rule))
if (tunnel) {
map.set(rule.id, { running: true, boundPort: tunnel.boundPort })
}
}
return map
}, [forwardRules, forwardTunnels])
@@ -585,6 +597,11 @@ export default function StatusBar() {
return () => window.clearInterval(timer)
}, [socksAvailable, loadForwardRules, refreshForwardState])
const previousForwardConnectionRef = useRef<string | undefined>(undefined)
useEffect(() => {
previousForwardConnectionRef.current = undefined
}, [intentKey])
const openForwardDrawer = useCallback(() => {
if (!connectionId || !activeTabId) return
if (!requirePro('portForward')) return
@@ -594,6 +611,10 @@ export default function StatusBar() {
...emptyForwardDraft(),
type: 'local',
name: t('common:portForward.defaultLocalName'),
localBindHost: '127.0.0.1',
localPort: 8080,
remoteHost: '127.0.0.1',
remotePort: 80,
})
setForwardDrawerError(null)
setForwardDrawerOpen(true)
@@ -617,17 +638,19 @@ export default function StatusBar() {
result = await window.opsxApi.sshForwardLocal(
connectionId,
rule.localPort,
rule.remoteHost || '',
resolveForwardTargetHost(rule.remoteHost),
rule.remotePort || 0,
rule.id
rule.id,
resolveLocalBindHost(rule.localBindHost)
)
} else if (rule.type === 'remote') {
result = await window.opsxApi.sshForwardRemote(
connectionId,
rule.remotePort || 0,
'127.0.0.1',
resolveForwardTargetHost(rule.remoteHost),
rule.localPort,
rule.id
rule.id,
resolveRemoteBindHost(rule.remoteBindHost)
)
} else {
result = await window.opsxApi.sshForwardDynamic(
@@ -637,48 +660,73 @@ export default function StatusBar() {
)
}
if (!result?.success) {
setForwardDrawerError(result?.error || t('common:portForward.errorStartFailed'))
setForwardDrawerError(
result?.error
? translateErrorMessage(result.error)
: t('common:portForward.errorStartFailed')
)
return false
}
if (intentKey) rememberForwardIntent(intentKey, rule)
await refreshForwardState()
return true
},
[connectionId, refreshForwardState, t]
[connectionId, intentKey, refreshForwardState, t]
)
useEffect(() => {
if (!socksAvailable || !connectionId || !intentKey) return
const previous = previousForwardConnectionRef.current
previousForwardConnectionRef.current = connectionId
if (!previous || previous === connectionId) return
void (async () => {
const listed = await window.opsxApi?.sshForwardList?.(connectionId)
const tunnels = listed?.success ? listed.tunnels || [] : []
const restored = await restoreForwardIntent(
intentKey,
(rule) => tunnelCoversRule(tunnels, rule),
(rule) => startForwardRule(rule)
)
if (restored > 0) {
toast.success(t('common:statusBar.forwardResumed', { count: restored }))
}
})()
}, [socksAvailable, connectionId, intentKey, startForwardRule, t, toast])
const stopForwardRule = useCallback(
async (rule: ForwardRule): Promise<boolean> => {
if (!window.opsxApi?.sshForwardStop) return false
const result = await window.opsxApi.sshForwardStop(rule.id)
if (!result?.success) {
// Dynamic may have been started with a different id historically.
if (rule.type === 'dynamic' && connectionId) {
const listed = await window.opsxApi.sshForwardList(connectionId)
const match = listed?.tunnels?.find((tunnel) => tunnel.kind === 'dynamic' && tunnel.running)
if (match) {
const stopMatch = await window.opsxApi.sshForwardStop(match.forwardId)
if (!stopMatch?.success) {
setForwardDrawerError(
stopMatch?.error || result?.error || t('common:portForward.errorStopFailed')
)
return false
}
await refreshForwardState()
return true
}
let result = await window.opsxApi.sshForwardStop(rule.id)
if (!result?.success && connectionId) {
// Runtime id may differ from persisted rule id after reconnect.
const listed = await window.opsxApi.sshForwardList(connectionId)
const match = listed?.tunnels?.find((tunnel) => tunnelCoversRule([tunnel], rule))
if (match) {
result = await window.opsxApi.sshForwardStop(match.forwardId)
}
setForwardDrawerError(result?.error || t('common:portForward.errorStopFailed'))
}
if (!result?.success) {
setForwardDrawerError(
result?.error
? translateErrorMessage(result.error)
: t('common:portForward.errorStopFailed')
)
return false
}
if (intentKey) forgetForwardIntent(intentKey, rule.id)
await refreshForwardState()
return true
},
[connectionId, refreshForwardState, t]
[connectionId, intentKey, refreshForwardState, t]
)
const handleStartRule = useCallback(
async (rule: ForwardRule) => {
if (!connectionId || forwardSaving) return
if (forwardRuntimes.get(rule.id)?.running) {
toast.success(t('common:statusBar.forwardStarted'))
return
}
setForwardSaving(true)
setForwardDrawerError(null)
try {
@@ -701,16 +749,18 @@ export default function StatusBar() {
}
}
const ok = await startForwardRule(target)
if (ok && target.type === 'dynamic') {
if (ok) {
toast.success(
t('common:statusBar.forwardStartedDynamic', {
port: resolveDynamicPort(target.localPort),
})
target.type === 'dynamic'
? t('common:statusBar.forwardStartedDynamic', {
port: resolveDynamicPort(target.localPort),
})
: t('common:statusBar.forwardStarted')
)
}
} catch (error) {
setForwardDrawerError(
(error instanceof Error ? error.message : String(error)) ||
translateErrorMessage(error instanceof Error ? error.message : String(error)) ||
t('common:portForward.errorStartFailed')
)
} finally {
@@ -720,6 +770,7 @@ export default function StatusBar() {
[
connectionId,
forwardRules,
forwardRuntimes,
forwardSaving,
persistForwardRules,
startForwardRule,
@@ -800,11 +851,19 @@ export default function StatusBar() {
return
}
if (!validPort(forwardDraft.remotePort)) {
setForwardDrawerError(t('common:portForward.errorTargetPort'))
setForwardDrawerError(
forwardDraft.type === 'local'
? t('common:portForward.errorTargetAddress')
: t('common:portForward.errorRemoteListenAddress')
)
return
}
if (forwardDraft.type === 'local' && !forwardDraft.remoteHost?.trim()) {
setForwardDrawerError(t('common:portForward.errorRemoteHost'))
if (!forwardDraft.remoteHost?.trim()) {
setForwardDrawerError(
forwardDraft.type === 'local'
? t('common:portForward.errorTargetAddress')
: t('common:portForward.errorLocalTargetAddress')
)
return
}
@@ -813,7 +872,15 @@ export default function StatusBar() {
type: forwardDraft.type || 'local',
name: forwardDraft.name.trim(),
localPort: forwardDraft.localPort,
remoteHost: forwardDraft.remoteHost?.trim(),
localBindHost:
forwardDraft.type === 'local'
? resolveLocalBindHost(forwardDraft.localBindHost)
: undefined,
remoteBindHost:
forwardDraft.type === 'remote'
? resolveRemoteBindHost(forwardDraft.remoteBindHost)
: undefined,
remoteHost: resolveForwardTargetHost(forwardDraft.remoteHost),
remotePort: forwardDraft.remotePort,
}
@@ -836,7 +903,7 @@ export default function StatusBar() {
toast.success(t('common:statusBar.forwardStarted'))
} catch (error) {
setForwardDrawerError(
(error instanceof Error ? error.message : String(error)) ||
translateErrorMessage(error instanceof Error ? error.message : String(error)) ||
t('common:portForward.errorStartFailed')
)
} finally {
@@ -955,7 +1022,9 @@ export default function StatusBar() {
}`}
>
{forwardSaving ? <Loader2 className="h-3.5 w-3.5 animate-spin" /> : <Globe className="h-3.5 w-3.5" />}
<span className="tabular-nums">{t('common:statusBar.socksLabel')}</span>
<span className="tabular-nums">
{t('common:statusBar.socksLabel')} :{dynamicListenPort}
</span>
</button>
)}
@@ -972,6 +1041,7 @@ export default function StatusBar() {
rules={forwardRules}
runtimes={forwardRuntimes}
busy={forwardSaving}
viaSession={hostLabel}
onChange={(next) => {
setForwardDraft({
type: next.type || 'local',
@@ -980,6 +1050,14 @@ export default function StatusBar() {
next.type === 'dynamic'
? resolveDynamicPort(next.localPort)
: next.localPort || 0,
localBindHost:
next.type === 'local'
? resolveLocalBindHost(next.localBindHost)
: '',
remoteBindHost:
next.type === 'remote'
? resolveRemoteBindHost(next.remoteBindHost)
: '',
remoteHost: next.remoteHost || '',
remotePort: next.remotePort || 0,
})