mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
refactor(pty): one remote-runtime PTY id predicate (#26930)
Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import { getPtyIpc } from '../../pty-host-bindings'
|
||||
import { parseAppSshPtyId } from '../../../providers/ssh-pty-id'
|
||||
import {
|
||||
@@ -34,7 +35,7 @@ async function findLocalPaneRootPid(deps: Deps, id: unknown): Promise<number | n
|
||||
// Why local only: SSH and WSL panes run Codex on another host, which must answer for itself.
|
||||
if (
|
||||
typeof id !== 'string' ||
|
||||
id.startsWith('remote:') ||
|
||||
isRemoteRuntimePtyId(id) ||
|
||||
parseAppSshPtyId(id) ||
|
||||
(ptyOwnership.get(id) ?? null) !== null
|
||||
) {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import { getPtyIpc } from '../../pty-host-bindings'
|
||||
import { parseAppSshPtyId } from '../../../providers/ssh-pty-id'
|
||||
import { inspectPtyProviderProcessForRenderer } from '../../../providers/pty-process-inspection'
|
||||
@@ -104,7 +105,7 @@ export function installPtyInspectIpcHandlers(deps: {
|
||||
typeof value !== 'string' ||
|
||||
value.length === 0 ||
|
||||
value.length > 512 ||
|
||||
value.startsWith('remote:') ||
|
||||
isRemoteRuntimePtyId(value) ||
|
||||
parseAppSshPtyId(value)
|
||||
) {
|
||||
return false
|
||||
@@ -144,7 +145,7 @@ export function installPtyInspectIpcHandlers(deps: {
|
||||
)
|
||||
|
||||
ipcMain.handle('pty:hasPty', async (_event, args: { id: string }): Promise<boolean | null> => {
|
||||
if (typeof args?.id !== 'string' || args.id.startsWith('remote:')) {
|
||||
if (typeof args?.id !== 'string' || isRemoteRuntimePtyId(args.id)) {
|
||||
// Why: same routing hazard pty:kill guards against — ptyOwnership never holds
|
||||
// a runtime terminal handle and parseAppSshPtyId ignores it, so the lookup
|
||||
// falls through to the local provider and its "not in my table" reads as an
|
||||
@@ -203,7 +204,7 @@ export function installPtyInspectIpcHandlers(deps: {
|
||||
}
|
||||
) => {
|
||||
// Why: same routing hazard as pty:hasPty — an unroutable id must read as client-only unverifiable, not as a local-provider answer or a raised IPC error.
|
||||
if (typeof args?.id !== 'string' || !args.id || args.id.startsWith('remote:')) {
|
||||
if (typeof args?.id !== 'string' || !args.id || isRemoteRuntimePtyId(args.id)) {
|
||||
return clientOnlyUnverifiableInspection('terminal_gone')
|
||||
}
|
||||
// Why: the pre-swap LocalPtyProvider does not own restored daemon ids, so
|
||||
@@ -309,7 +310,7 @@ export function installPtyInspectIpcHandlers(deps: {
|
||||
async (_event, args: { ptyId?: unknown }): Promise<void> => {
|
||||
if (
|
||||
typeof args?.ptyId !== 'string' ||
|
||||
!args.ptyId.startsWith('remote:') ||
|
||||
!isRemoteRuntimePtyId(args.ptyId) ||
|
||||
args.ptyId.length > 512
|
||||
) {
|
||||
return
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import { getPtyIpc } from '../../pty-host-bindings'
|
||||
import type { Store } from '../../../persistence'
|
||||
import type { OrcaRuntimeService } from '../../../runtime/orca-runtime'
|
||||
@@ -52,7 +53,7 @@ async function stopRendererOwnedPtyAs(
|
||||
args: { id: string; keepHistory?: boolean },
|
||||
intentionalStop: TerminalIntentionalStopKind | null
|
||||
): Promise<void> {
|
||||
if (typeof args?.id !== 'string' || !args.id || args.id.startsWith('remote:')) {
|
||||
if (typeof args?.id !== 'string' || !args.id || isRemoteRuntimePtyId(args.id)) {
|
||||
// Why: runtime terminal handles belong to terminal.close; unowned PTY routing could target the local provider.
|
||||
throw new Error('Invalid PTY provider id')
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import type { IPtyProvider } from '../../../providers/types'
|
||||
import { LocalPtyProvider } from '../../../providers/local-pty-provider'
|
||||
import { parseAppSshPtyId } from '../../../providers/ssh-pty-id'
|
||||
@@ -88,7 +89,7 @@ export async function probePtyLivenessFromRuntimeController(
|
||||
try {
|
||||
// Why: no locally routed provider can authoritatively answer for a
|
||||
// remote host's PTY, so remote-scoped ids stay unknown, never absent.
|
||||
if (ptyId.startsWith('remote:')) {
|
||||
if (isRemoteRuntimePtyId(ptyId)) {
|
||||
return null
|
||||
}
|
||||
const connectionId = ptyOwnership.get(ptyId) ?? parseAppSshPtyId(ptyId)?.connectionId
|
||||
@@ -235,7 +236,7 @@ export function hasPtyFromRuntimeController(
|
||||
try {
|
||||
// Why: no locally routed provider can authoritatively answer for a
|
||||
// remote host's PTY, so remote-scoped ids stay unknown, never absent.
|
||||
if (ptyId.startsWith('remote:')) {
|
||||
if (isRemoteRuntimePtyId(ptyId)) {
|
||||
return null
|
||||
}
|
||||
const connectionId = ptyOwnership.get(ptyId) ?? parseAppSshPtyId(ptyId)?.connectionId
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
// @ts-nocheck -- mechanically split from OrcaRuntimeService; behavior is covered by AST equivalence and characterization tests.
|
||||
import { isRemoteRuntimePtyId } from '../../shared/remote-runtime-pty-id'
|
||||
import { OrcaRuntimeWithRefreshFloatingWorkspacePtyLiveness } from './orca-runtime-refresh-floating-workspace-pty-liveness'
|
||||
import { writeOrchestrationPointerWithSettlement } from './orchestration/mailbox-pointer-pty-write'
|
||||
import type { WriteSettlement } from '../../shared/pty-write-settlement'
|
||||
@@ -127,7 +128,7 @@ export class OrcaRuntimeWithWriteOrchestrationPointerPty extends OrcaRuntimeWith
|
||||
provenLivePtyIds !== null &&
|
||||
leaf.ptyId !== null &&
|
||||
!provenLivePtyIds.has(leaf.ptyId) &&
|
||||
!leaf.ptyId.startsWith('remote:') &&
|
||||
!isRemoteRuntimePtyId(leaf.ptyId) &&
|
||||
parseAppSshPtyId(leaf.ptyId) === null &&
|
||||
this.ptyController?.hasPty?.(leaf.ptyId) !== true
|
||||
return {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import type { PtyTransport } from './pty-transport-types'
|
||||
import { isRemoteRuntimePtyId } from './pty-connection/paired-parked-terminal-restore'
|
||||
|
||||
/**
|
||||
* Releases a pane's transport for a restart and returns the PTY its replacement spawn must name.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { waitForTerminalReplayWritesParsed } from '../replay-guard'
|
||||
import {
|
||||
POST_REPLAY_MODE_RESET,
|
||||
@@ -12,7 +13,6 @@ import {
|
||||
shouldSkipAltFrameForWidthMismatch
|
||||
} from '../terminal-snapshot-replay-paint'
|
||||
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { restoredSnapshotPaintsPrintableContent } from '../restored-snapshot-coverage'
|
||||
import { resolveSshReconnectModelPaint } from './resolve-ssh-reconnect-model-paint'
|
||||
import { fitReattachedPaneToGrid, noteReattachAltFrameSkip } from './reattach-grid-fit'
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { useAppStore } from '@/store'
|
||||
import { isRuntimeOwnedSshTargetId } from '../../../../../shared/execution-host'
|
||||
import { resolveSshPaneConnectGate } from '../ssh-pane-connect-gate'
|
||||
|
||||
import { waitForUserInitiatedSshConnect, waitForSshConnection } from './ssh-session-connect'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { isSshSessionGoneError } from './pty-connect-limits'
|
||||
import { toProcessExitStartup } from './process-exit-startup'
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { scheduleRuntimeGraphSync } from '@/runtime/sync-runtime-graph'
|
||||
import { useAppStore } from '@/store'
|
||||
import { isWebTerminalSurfaceTabId } from '@/runtime/web-terminal-surface-id'
|
||||
@@ -9,7 +10,6 @@ import {
|
||||
recordPtyConnectDiagnostic
|
||||
} from './pty-connect-limits'
|
||||
import {
|
||||
isRemoteRuntimePtyId,
|
||||
canRestorePairedParkedTerminal,
|
||||
isSessionOwnedByWorktree
|
||||
} from './paired-parked-terminal-restore'
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { warnTerminalLifecycleAnomaly } from '../terminal-lifecycle-diagnostics'
|
||||
import { isSshSessionGoneError, recordPtyConnectDiagnostic } from './pty-connect-limits'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { toProcessExitStartup } from './process-exit-startup'
|
||||
import { recoverUnverifiableDirectSshReattach } from './direct-ssh-reattach-recovery'
|
||||
import type { ConnectPanePtySession } from './connect-pane-pty-session'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { reportWorkerTerminalUserInput } from '@/lib/worker-terminal-takeover-report'
|
||||
import { useAppStore } from '@/store'
|
||||
import { getLocalProjectExecutionRuntimeContext } from '@/lib/local-preflight-context'
|
||||
@@ -28,7 +29,6 @@ import {
|
||||
} from '../renderer-owned-agent-status-registry'
|
||||
|
||||
import { DIRECT_SSH_PANE_RETRY_SETTLEMENT_TIMEOUT_MS } from './pty-connect-limits'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { resolveLatestAgentDoneStartedAt } from './agent-done-started-at'
|
||||
import { rendererAgentStatusObservations } from '@/lib/renderer-agent-status-observations'
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import type { SleepingAgentSessionRecord } from '../../../../../shared/agent-session-resume'
|
||||
import type { ColdRestoreAgentResumeStartup } from './fresh-spawn-types'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
|
||||
// Why not activationTreatsNoteAsFinished: a live+done note is a finished turn's idle anchor for a running pane, not sleep.
|
||||
export function isHibernationDoneRecord(record: SleepingAgentSessionRecord): boolean {
|
||||
|
||||
+1
-1
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { recordTerminalFreezeBreadcrumb } from '../terminal-freeze-breadcrumbs'
|
||||
import { redactPtyIdForDiagnostics } from '../../../../../shared/pty-delivery-diagnostics'
|
||||
import {
|
||||
@@ -26,7 +27,6 @@ import {
|
||||
shouldWritePtyOutputForeground,
|
||||
consumeInactiveForegroundImmediateBudget
|
||||
} from './foreground-output-scan'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
|
||||
import type { ConnectPanePtySession } from './connect-pane-pty-session'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { useAppStore } from '@/store'
|
||||
import { PROCESS_BOUNDARY_GROUND } from '../../../../../shared/terminal-mode-reset-profiles'
|
||||
import { hasPtySerializer } from '../pty-buffer-serializer'
|
||||
@@ -7,7 +8,6 @@ import { settleSpawnThatLeftPaneUnbound } from './unbound-pane-spawn-recovery'
|
||||
import { STARTUP_CWD_FALLBACK_NOTICE } from './startup-cwd-fallback-notice'
|
||||
import { pendingSpawnByPaneKey, pendingSpawnGenerationByPaneKey } from './pty-connect-limits'
|
||||
import { shouldWritePtyOutputForeground } from './foreground-output-scan'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { toProcessExitStartup } from './process-exit-startup'
|
||||
import type {
|
||||
PendingStartupCommand,
|
||||
|
||||
+1
-1
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { recordTerminalFreezeBreadcrumb } from '../terminal-freeze-breadcrumbs'
|
||||
import { redactPtyIdForDiagnostics } from '../../../../../shared/pty-delivery-diagnostics'
|
||||
import { cancelScheduledHiddenOutputRestore } from '../hidden-output-restore-scheduler'
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
HIDDEN_OUTPUT_RESTORE_REMOTE_REARM_MAX
|
||||
} from './hidden-output-restore-limits'
|
||||
import { shouldWritePtyOutputForeground } from './foreground-output-scan'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
|
||||
import type { ConnectPanePtySession } from './connect-pane-pty-session'
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { safeFit, safeFitAndThen } from '@/lib/pane-manager/pane-tree-ops'
|
||||
import { getFitOverrideForPty } from '@/lib/pane-manager/mobile-fit-overrides'
|
||||
import { waitForTerminalReplayWritesParsed } from '../replay-guard'
|
||||
@@ -24,7 +25,6 @@ import {
|
||||
|
||||
import { HIDDEN_OUTPUT_RESTORE_UNAVAILABLE_WARNING } from './hidden-output-restore-limits'
|
||||
import { shouldWritePtyOutputForeground } from './foreground-output-scan'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { restoredSnapshotPaintsPrintableContent } from '../restored-snapshot-coverage'
|
||||
import { recordTerminalFreezeBreadcrumb } from '../terminal-freeze-breadcrumbs'
|
||||
|
||||
|
||||
+1
-1
@@ -1,9 +1,9 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { isHostAnsweredSnapshotRetryCause } from '@/runtime/remote-runtime-terminal-multiplexer'
|
||||
import { onTerminalScrollIntentFollowOutput } from '@/lib/pane-manager/terminal-scroll-intent'
|
||||
|
||||
import { shouldWritePtyOutputForeground } from './foreground-output-scan'
|
||||
import { readE2eHiddenSnapshotOverride } from './e2e-terminal-pty-harness'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
|
||||
import type { PtyBufferSnapshot } from '../pty-transport'
|
||||
import type { ConnectPanePtySession } from './connect-pane-pty-session'
|
||||
|
||||
-5
@@ -1,16 +1,11 @@
|
||||
import { useAppStore } from '@/store'
|
||||
import { TERMINAL_PAIRED_PARKING_RUNTIME_CAPABILITY } from '../../../../../shared/protocol-version'
|
||||
import { getRemoteRuntimePtyEnvironmentId } from '@/runtime/runtime-terminal-stream'
|
||||
import { REMOTE_PTY_ID_PREFIX } from './pty-connect-limits'
|
||||
import {
|
||||
isRuntimeHostContactRevoked,
|
||||
lastVerifiedRuntimeStatus
|
||||
} from '../../../../../shared/runtime-host-status'
|
||||
|
||||
export function isRemoteRuntimePtyId(ptyId: string | null | undefined): boolean {
|
||||
return typeof ptyId === 'string' && ptyId.startsWith(REMOTE_PTY_ID_PREFIX)
|
||||
}
|
||||
|
||||
export function canRestorePairedParkedTerminal(ptyId: string): boolean {
|
||||
const environmentId = getRemoteRuntimePtyEnvironmentId(ptyId)
|
||||
if (environmentId === null) {
|
||||
|
||||
+1
-1
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { captureNotificationTransportOwner } from '@/attention/notification-subject-owner'
|
||||
import { scheduleRuntimeGraphSync } from '@/runtime/sync-runtime-graph'
|
||||
import { useAppStore } from '@/store'
|
||||
@@ -12,7 +13,6 @@ import {
|
||||
isAgentTaskCompleteNotificationEnabled,
|
||||
isAgentTaskCompleteTrackingEnabled
|
||||
} from './agent-task-complete-settings'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { shouldIgnoreStalePanePtyLayoutBinding } from './pane-pty-layout-binding'
|
||||
|
||||
import type { ConnectPanePtySession } from './connect-pane-pty-session'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { useAppStore } from '@/store'
|
||||
import type { SessionRestoredBannerReason } from '../session-restored-banner-pane-state'
|
||||
import { hasPtySerializer } from '../pty-buffer-serializer'
|
||||
@@ -12,7 +13,6 @@ import { writeTerminalPastePtyInput } from '../terminal-pty-paste-writer'
|
||||
import { createPasteReadinessTimeoutNotice } from '@/lib/launch-agent-paste-timeout-notice'
|
||||
|
||||
import { STARTUP_DRAFT_PASTE_QUIET_MS } from './pty-connect-limits'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
|
||||
import type { ConnectPanePtySession } from './connect-pane-pty-session'
|
||||
|
||||
|
||||
+1
-1
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { RESET_GRAPHIC_RENDITION } from '../../../../../shared/terminal-mode-reset-profiles'
|
||||
import { redactPtyIdForDiagnostics } from '../../../../../shared/pty-delivery-diagnostics'
|
||||
import { writeTerminalOutput } from '@/lib/pane-manager/pane-terminal-output-scheduler'
|
||||
@@ -7,7 +8,6 @@ import type { PtyBufferSnapshot } from '../pty-transport'
|
||||
import type { ConnectPanePtySession } from './connect-pane-pty-session'
|
||||
import { PARK_REVEAL_NO_HOST_IMAGE_WARNING } from './hidden-output-restore-limits'
|
||||
import type { HiddenOutputSnapshotResult } from './hidden-output-snapshot-serialize'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
|
||||
/** Which restore budget an unverifiable probe is charged to; null when the loop keeps its own count (legacy hosts). */
|
||||
export type ParkRevealRetryLedger = 'host' | 'local' | null
|
||||
|
||||
@@ -24,7 +24,6 @@ export function isSshSessionGoneError(error: unknown): boolean {
|
||||
}
|
||||
// Why: relay requests expire at 30s; leave one second for their fallback before re-arming locally.
|
||||
export const DIRECT_SSH_PANE_RETRY_SETTLEMENT_TIMEOUT_MS = 31_000
|
||||
export const REMOTE_PTY_ID_PREFIX = 'remote:'
|
||||
export const PTY_CONNECT_DIAG_LIMIT = 200
|
||||
export const MANUAL_AGENT_COMMAND_MAX_CHARS = 4096
|
||||
export const STARTUP_DRAFT_PASTE_QUIET_MS = 1500
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import type { ManagedPaneInternal } from '@/lib/pane-manager/pane-manager-types'
|
||||
import { subscribeToTerminalInputData } from '../terminal-user-input-signal'
|
||||
import { installTerminalImeCompositionRoute } from '../terminal-ime-composition-route'
|
||||
@@ -20,7 +21,6 @@ import {
|
||||
|
||||
import { FOREGROUND_GRID_DRIFT_CHECK_MIN_MS } from './foreground-output-budgets'
|
||||
import { TERMINAL_FOCUS_IN_SEQUENCE, TERMINAL_FOCUS_OUT_SEQUENCE } from './foreground-output-scan'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { isCodexPaneStale } from './codex-pane-stale'
|
||||
import { installTerminalSelectionFitGuard } from '../terminal-selection-fit-guard'
|
||||
import { initializePaneGeometry, readPaneSize } from './read-pane-size'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { useAppStore } from '@/store'
|
||||
import { createIpcPtyTransport } from '../pty-transport'
|
||||
import { createRemoteRuntimePtyTransport } from '../remote-runtime-pty-transport'
|
||||
@@ -19,7 +20,6 @@ import {
|
||||
installTerminalCapabilityReplyHandlers
|
||||
} from '../terminal-capability-replies'
|
||||
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { TRANSPORT_CONNECT_SETTLE_GRACE_MS } from './pty-connect-limits'
|
||||
import { buildPaneTransportOptions } from './pane-transport-options'
|
||||
import { buffersInputOnlyForSshReattach } from './ssh-reattach-input-buffering'
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import type { ManagedPaneInternal } from '@/lib/pane-manager/pane-manager-types'
|
||||
import { safeFit } from '@/lib/pane-manager/pane-tree-ops'
|
||||
import { requestStablePaneFit } from '@/lib/pane-manager/pane-fit-resize-observer'
|
||||
@@ -8,7 +9,6 @@ import { shouldClaimRemoteDesktopViewport } from '../remote-desktop-viewport-cla
|
||||
import { deferTerminalGeometryMutationDuringRebuild } from '@/lib/pane-manager/terminal-scroll-intent-rebuild'
|
||||
import { waitForStableStartupGrid } from '../terminal-startup-grid-settle'
|
||||
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { isSetupSplitGeometryReady } from './setup-split-geometry'
|
||||
|
||||
import type { ConnectPanePtySession } from './connect-pane-pty-session'
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { safeFitAndThen } from '@/lib/pane-manager/pane-tree-ops'
|
||||
import { getFitOverrideForPty } from '@/lib/pane-manager/mobile-fit-overrides'
|
||||
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import type { ReattachPayloadContext } from './reattach-payload-context'
|
||||
import type { ReattachPayloadSession } from './reattach-payload-session'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { scheduleRuntimeGraphSync } from '@/runtime/sync-runtime-graph'
|
||||
import type { PtyBufferSnapshot, PtyConnectResult } from '../pty-transport'
|
||||
import { warnTerminalLifecycleAnomaly } from '../terminal-lifecycle-diagnostics'
|
||||
@@ -9,7 +10,6 @@ import { parseAppSshPtyId } from '../../../../../shared/ssh-pty-id'
|
||||
import { resolveHiddenRestoreScrollbackRows } from '../terminal-hidden-restore-scrollback'
|
||||
import { shouldIgnoreStalePanePtyLayoutBinding } from './pane-pty-layout-binding'
|
||||
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { hasEmptyReattachRetireEvidence } from './empty-reattach-retire-evidence'
|
||||
import type { ColdRestoreAgentResumeStartup } from './fresh-spawn-types'
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { createTerminalZeroDimensionsMessage } from '../../../../../shared/terminal-zero-dimensions-diagnostic'
|
||||
import { isWorktreeRemovalFenceError } from '../../../../../shared/worktree/removal-fence-error'
|
||||
import { safeFit } from '@/lib/pane-manager/pane-tree-ops'
|
||||
@@ -5,7 +6,6 @@ import { useAppStore } from '@/store'
|
||||
import { createCodexBackfillErrorDetector } from '../codex-backfill-error-detector'
|
||||
import { hasWorktreeSleepIntent, onWorktreeSleepIntentCleared } from '@/lib/worktree-sleep-intent'
|
||||
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { recordPtyConnectDiagnostic } from './pty-connect-limits'
|
||||
import { findTerminalTabForPane } from './terminal-tab-id'
|
||||
|
||||
|
||||
+2
-2
@@ -10,7 +10,7 @@ import { discardTerminalOutput } from '@/lib/pane-manager/pane-terminal-output-s
|
||||
import { getProviderSessionClaimKey } from '@/lib/sleeping-agent-pane-ownership'
|
||||
import { releaseRendererPtyVisibilityClaim } from '../pty-renderer-delivery-claims'
|
||||
|
||||
import { REMOTE_PTY_ID_PREFIX } from './pty-connect-limits'
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { SHIFT_ENTER_RECONFIRM_IDLE_MS } from './foreground-output-scan'
|
||||
import type { PanePtyBinding } from './pane-pty-binding'
|
||||
|
||||
@@ -51,7 +51,7 @@ export function installSessionReconcileDispose(session: ConnectPanePtySession):
|
||||
if (
|
||||
!requestedPtyId ||
|
||||
requestedPtyId === session.handledExitPtyId ||
|
||||
requestedPtyId.startsWith(REMOTE_PTY_ID_PREFIX) ||
|
||||
isRemoteRuntimePtyId(requestedPtyId) ||
|
||||
session.transport.getConnectionId?.() != null
|
||||
) {
|
||||
return
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../../shared/remote-runtime-pty-id'
|
||||
import { makePaneKey } from '../../../../../shared/stable-pane-id'
|
||||
import { useAppStore } from '@/store'
|
||||
import { safeFit } from '@/lib/pane-manager/pane-tree-ops'
|
||||
@@ -12,7 +13,6 @@ import { registerTerminalSideEffectFactConsumer } from '../terminal-side-effect-
|
||||
|
||||
import { isAgentTaskCompleteTrackingEnabled } from './agent-task-complete-settings'
|
||||
import { isAgentProcessInspectionCostly } from '../agent-process-inspection-cost'
|
||||
import { isRemoteRuntimePtyId } from './paired-parked-terminal-restore'
|
||||
import { isRemoteExecutionHostPtyId } from '../remote-execution-host-pty'
|
||||
|
||||
import type { ConnectPanePtySession } from './connect-pane-pty-session'
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import type { ExecutionHostId } from '../../../../shared/execution-host'
|
||||
import { LOCAL_EXECUTION_HOST_ID } from '../../../../shared/execution-host'
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import { getConnectionIdFromState } from '@/lib/connection-context'
|
||||
import {
|
||||
getExecutionHostIdForWorktree,
|
||||
@@ -9,8 +10,6 @@ import { isLocalNativeWindowsConpty } from '@/lib/pane-manager/windows-pty-compa
|
||||
import type { PaneCwdMap } from './resolve-split-cwd'
|
||||
import type { PtyTransport } from './pty-transport-types'
|
||||
|
||||
const REMOTE_RUNTIME_PTY_ID_PREFIX = 'remote:'
|
||||
|
||||
type TerminalTabShellState = {
|
||||
tabsByWorktree: Record<
|
||||
string,
|
||||
@@ -39,10 +38,6 @@ type TerminalCtrlArrowConptyArgs = {
|
||||
transport: TerminalCtrlArrowConptyTransport | null
|
||||
}
|
||||
|
||||
function isRemoteRuntimePtyId(ptyId: string): boolean {
|
||||
return ptyId.startsWith(REMOTE_RUNTIME_PTY_ID_PREFIX)
|
||||
}
|
||||
|
||||
export function isLocalWindowsConptyPaneForCtrlArrow({
|
||||
isWindows,
|
||||
userAgent,
|
||||
@@ -59,7 +54,7 @@ export function isLocalWindowsConptyPaneForCtrlArrow({
|
||||
}
|
||||
|
||||
const ptyId = transport?.getPtyId() ?? null
|
||||
if (ptyId !== null && isRemoteRuntimePtyId(ptyId)) {
|
||||
if (isRemoteRuntimePtyId(ptyId)) {
|
||||
return false
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
// this routes such panes through the same exit teardown an observed exit runs.
|
||||
// See design-docs/terminal-dead-pane-on-bg-exit.md.
|
||||
|
||||
const REMOTE_PTY_ID_PREFIX = 'remote:'
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
|
||||
/**
|
||||
* A pane binding that exposes its bound transport identity plus a reconcile
|
||||
@@ -42,7 +42,7 @@ export function shouldReconcileDeadSession(args: {
|
||||
if (ptyId === null || ptyId === undefined) {
|
||||
return false
|
||||
}
|
||||
if (ptyId.startsWith(REMOTE_PTY_ID_PREFIX)) {
|
||||
if (isRemoteRuntimePtyId(ptyId)) {
|
||||
return false
|
||||
}
|
||||
// Why: only local/daemon-backed ids (connectionId null/undefined) are
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import type { Terminal } from '@xterm/xterm'
|
||||
import type { TerminalLayoutSnapshot, TerminalTab } from '../../../../shared/terminal-tab-types'
|
||||
import type { PtyPaneStartup } from './pty-connection-types'
|
||||
@@ -46,7 +47,7 @@ export function reportActiveRendererPtyForPane(
|
||||
): void {
|
||||
for (const [paneId, transport] of paneTransports) {
|
||||
const ptyId = transport.getPtyId()
|
||||
if (!ptyId || ptyId.startsWith('remote:')) {
|
||||
if (!ptyId || isRemoteRuntimePtyId(ptyId)) {
|
||||
continue
|
||||
}
|
||||
window.api.pty.setActiveRendererPty?.(ptyId, activePaneId === paneId)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { isRemoteRuntimePastePtyId, resolveTerminalPasteRuntime } from './terminal-paste-runtime'
|
||||
import { resolveTerminalPasteRuntime } from './terminal-paste-runtime'
|
||||
|
||||
describe('terminal paste runtime', () => {
|
||||
it('uses the platform-local runtime when no SSH identity is available', () => {
|
||||
@@ -244,10 +244,4 @@ describe('terminal paste runtime', () => {
|
||||
kind: 'remote-runtime'
|
||||
})
|
||||
})
|
||||
|
||||
it('recognizes remote runtime PTY ids', () => {
|
||||
expect(isRemoteRuntimePastePtyId('remote:terminal-1')).toBe(true)
|
||||
expect(isRemoteRuntimePastePtyId('pty-1')).toBe(false)
|
||||
expect(isRemoteRuntimePastePtyId(null)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { TerminalPasteRuntime } from './terminal-paste-model'
|
||||
import { parseWslUncPath } from '../../../../shared/wsl-paths'
|
||||
|
||||
const REMOTE_PTY_ID_PREFIX = 'remote:'
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
|
||||
type TerminalPasteRuntimeTransport = {
|
||||
getConnectionId?: () => string | null | undefined
|
||||
@@ -34,7 +33,7 @@ export function resolveTerminalPasteRuntime({
|
||||
}: ResolveTerminalPasteRuntimeArgs): TerminalPasteRuntime {
|
||||
const windowsConpty = isWindowsConpty === undefined ? {} : { isWindowsConpty }
|
||||
|
||||
if (isRemoteRuntimePastePtyId(ptyId)) {
|
||||
if (isRemoteRuntimePtyId(ptyId)) {
|
||||
return { platform, runtimeKey: `remote:${ptyId}`, kind: 'remote-runtime', ...windowsConpty }
|
||||
}
|
||||
|
||||
@@ -62,10 +61,6 @@ export function resolveTerminalPasteRuntime({
|
||||
return { platform, runtimeKey: `local:${platform}`, kind: 'local', ...windowsConpty }
|
||||
}
|
||||
|
||||
export function isRemoteRuntimePastePtyId(ptyId: string | null | undefined): boolean {
|
||||
return typeof ptyId === 'string' && ptyId.startsWith(REMOTE_PTY_ID_PREFIX)
|
||||
}
|
||||
|
||||
function resolveWslRuntimeKey(
|
||||
metadata:
|
||||
| {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import { useEffect, useRef } from 'react'
|
||||
import {
|
||||
FOCUS_TERMINAL_PANE_EVENT,
|
||||
@@ -52,7 +53,7 @@ function reportRendererPtyVisibility(
|
||||
): void {
|
||||
for (const transport of paneTransports.values()) {
|
||||
const ptyId = transport.getPtyId()
|
||||
if (!ptyId || ptyId.startsWith('remote:')) {
|
||||
if (!ptyId || isRemoteRuntimePtyId(ptyId)) {
|
||||
// Why: remote-runtime PTYs use a relay path outside main's local
|
||||
// renderer-visibility registry, so reporting them here is misleading.
|
||||
continue
|
||||
@@ -190,7 +191,7 @@ export function useTerminalPaneGlobalEffects({
|
||||
|
||||
useEffect(() => {
|
||||
const ptyId = isActive && isVisible && isWorktreeActive ? activeLeafPtyId : null
|
||||
if (!ptyId || ptyId.startsWith('remote:')) {
|
||||
if (!ptyId || isRemoteRuntimePtyId(ptyId)) {
|
||||
return
|
||||
}
|
||||
// Why: main uses this as a scheduler hint only, so the foreground pane's
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../shared/remote-runtime-pty-id'
|
||||
import { parseAppSshPtyId } from '../../../shared/ssh-pty-id'
|
||||
import { parsePaneKey } from '../../../shared/stable-pane-id'
|
||||
import { parseRemoteRuntimePtyId } from '@/runtime/runtime-terminal-stream'
|
||||
@@ -51,7 +52,7 @@ export function resolveAgentStatusConnectionRouting(args: {
|
||||
}
|
||||
return { connectionId: null }
|
||||
}
|
||||
if (ptyId.startsWith('remote:')) {
|
||||
if (isRemoteRuntimePtyId(ptyId)) {
|
||||
return undefined
|
||||
}
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../shared/remote-runtime-pty-id'
|
||||
import { useAppStore } from '@/store'
|
||||
import type { AppState } from '@/store/types'
|
||||
import type { TerminalTab } from '../../../shared/terminal-tab-types'
|
||||
@@ -113,7 +114,7 @@ export function createAutomationTerminalOwnership(
|
||||
consumed = true
|
||||
unsubscribe()
|
||||
observeTakeover()
|
||||
if (args.runtimeKind !== 'desktop' || args.ptyId.startsWith('remote:') || userTookOver) {
|
||||
if (args.runtimeKind !== 'desktop' || isRemoteRuntimePtyId(args.ptyId) || userTookOver) {
|
||||
return false
|
||||
}
|
||||
const state = args.store.getState()
|
||||
|
||||
@@ -24,15 +24,12 @@ import type { TerminalInputKind } from '../../../shared/terminal-input-kind'
|
||||
|
||||
export type RuntimeTerminalProcessInspection = TerminalProcessInspection
|
||||
|
||||
const REMOTE_PTY_ID_PREFIX = 'remote:'
|
||||
const DESKTOP_RUNTIME_CLIENT = { id: 'orca-desktop', type: 'desktop' } as const
|
||||
function isRuntimePtyInputTooLarge(data: string): boolean | Promise<boolean> {
|
||||
return isTerminalInputTooLargeWithDeferredMeasurement(data)
|
||||
}
|
||||
|
||||
export function isRemoteRuntimePtyId(ptyId: string): boolean {
|
||||
return ptyId.startsWith(REMOTE_PTY_ID_PREFIX)
|
||||
}
|
||||
export { isRemoteRuntimePtyId } from '../../../shared/remote-runtime-pty-id'
|
||||
|
||||
function isRemoteInspectionPtyId(ptyId: string): boolean {
|
||||
return getRemoteRuntimePtyEnvironmentId(ptyId) !== null || parseAppSshPtyId(ptyId) !== null
|
||||
|
||||
@@ -4,7 +4,7 @@ import { isClaudeManagementTitle } from '../../../../shared/agent-detection'
|
||||
import { isTerminalLeafId, makePaneKey } from '../../../../shared/stable-pane-id'
|
||||
import { isWebTerminalSurfaceTabId } from '../../../../shared/terminal-surface-id'
|
||||
import type { Tab } from '../../../../shared/tab-types'
|
||||
import { parseRemoteRuntimePtyId } from '../runtime-terminal-stream'
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import {
|
||||
isNativeChatTabWideFallbackSafe,
|
||||
nativeChatLaunchAgentForLeaf,
|
||||
@@ -16,10 +16,6 @@ export function mobileTerminalSurfaceId(parentTabId: string, leafId: string): st
|
||||
return `${parentTabId}::${leafId}`
|
||||
}
|
||||
|
||||
export function isRemoteRuntimePtyId(ptyId: string | null | undefined): boolean {
|
||||
return typeof ptyId === 'string' && parseRemoteRuntimePtyId(ptyId) !== null
|
||||
}
|
||||
|
||||
export function isWebOnlyMirroredTerminalTab(
|
||||
tab: Pick<NonNullable<AppState['tabsByWorktree'][string]>[number], 'id' | 'ptyId'>,
|
||||
layout: AppState['terminalLayoutsByTabId'][string] | undefined
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import type { StateCreator } from 'zustand'
|
||||
import { toast } from 'sonner'
|
||||
import type { AppState } from '../types'
|
||||
@@ -151,7 +152,7 @@ export function createRepoRemovalActions(
|
||||
for (const tab of tabs) {
|
||||
killedTabIds.add(tab.id)
|
||||
for (const ptyId of get().ptyIdsByTabId[tab.id] ?? []) {
|
||||
if (!ptyId.startsWith('remote:')) {
|
||||
if (!isRemoteRuntimePtyId(ptyId)) {
|
||||
window.api.pty.kill(ptyId)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import type { SleepingAgentSessionRecord } from '../../../../shared/agent-session-resume'
|
||||
import type { AppState } from '../types'
|
||||
import {
|
||||
@@ -228,7 +229,7 @@ export function buildTerminalTabRetirementPlans(
|
||||
environmentId: remote.environmentId?.trim() || null,
|
||||
handle: remote.handle
|
||||
})
|
||||
} else if (ptyId.startsWith('remote:')) {
|
||||
} else if (isRemoteRuntimePtyId(ptyId)) {
|
||||
unroutablePtyIds.push(ptyId)
|
||||
} else if (providerOwnership.kind !== 'local-or-ssh') {
|
||||
// Why: HUB-native wake hints are not paired-client PTY ids; wait for pane resolution instead of killing the same-looking local id.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import { parsePaneKey } from '../../../../shared/stable-pane-id'
|
||||
import {
|
||||
restorePtyDataHandlersAfterFailedShutdown,
|
||||
@@ -154,7 +155,7 @@ export function createTerminalPaneHibernationActions(
|
||||
for (const snapshot of unregisterPtyDataHandlers(rendererShutdownPtyIds) ?? []) {
|
||||
snapshot.commit?.()
|
||||
}
|
||||
} else if (!opts.ptyId.startsWith('remote:')) {
|
||||
} else if (!isRemoteRuntimePtyId(opts.ptyId)) {
|
||||
// Why: pty.kill can flush final data before exit; unregister first so stale handlers can't fire phantom notifications during hibernation.
|
||||
const handlerSnapshots = unregisterPtyDataHandlers(rendererShutdownPtyIds) ?? []
|
||||
try {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import { parseAppSshPtyId } from '../../../../shared/ssh-pty-id'
|
||||
import { parseRemoteRuntimePtyId, toRemoteRuntimePtyId } from '@/runtime/runtime-terminal-stream'
|
||||
import { isTerminalTabPresent } from '../slices/terminal-tab-retirement'
|
||||
@@ -5,8 +6,7 @@ import type { TerminalSlice, TerminalStoreGet, TerminalStoreSet } from './termin
|
||||
import {
|
||||
consumePendingActivationSpawn,
|
||||
getPendingActivationSpawnCount,
|
||||
isCurrentDirectSshAuthority,
|
||||
isRemoteRuntimePtyId
|
||||
isCurrentDirectSshAuthority
|
||||
} from './terminal-pty-identities'
|
||||
import { omitUnverifiedPtyLossTabIds } from './terminal-unverified-pty-loss'
|
||||
import { clearWorktreeSleepIntent } from '@/lib/worktree-sleep-intent'
|
||||
|
||||
@@ -2,13 +2,8 @@ import type { AppState } from '../types'
|
||||
import type { TerminalLayoutSnapshot, TerminalTab } from '../../../../shared/terminal-tab-types'
|
||||
import type { DirectSshAuthority } from '../../../../shared/ssh-types'
|
||||
import { parseLegacyNumericPaneKey, parsePaneKey } from '../../../../shared/stable-pane-id'
|
||||
import { parseRemoteRuntimePtyId } from '@/runtime/runtime-terminal-stream'
|
||||
import { resolveDirectSshTerminalWorkspaceKeys } from '../slices/direct-ssh-terminal-workspace-scope'
|
||||
|
||||
export function isRemoteRuntimePtyId(ptyId: string | null | undefined): boolean {
|
||||
return typeof ptyId === 'string' && parseRemoteRuntimePtyId(ptyId) !== null
|
||||
}
|
||||
|
||||
export function isCurrentDirectSshAuthority(
|
||||
state: AppState,
|
||||
authority: DirectSshAuthority
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import { parseAppSshPtyId } from '../../../../shared/ssh-pty-id'
|
||||
import { hasWorktreeSleepIntent } from '@/lib/worktree-sleep-intent'
|
||||
import type { TerminalSlice, TerminalStoreGet, TerminalStoreSet } from './terminal-state'
|
||||
import {
|
||||
consumePendingActivationSpawn,
|
||||
getPendingActivationSpawnCount,
|
||||
isCurrentDirectSshAuthority,
|
||||
isRemoteRuntimePtyId
|
||||
isCurrentDirectSshAuthority
|
||||
} from './terminal-pty-identities'
|
||||
|
||||
export function createTerminalPtyReleaseActions(
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isRemoteRuntimePtyId } from '../../../../shared/remote-runtime-pty-id'
|
||||
import type { TerminalTab } from '../../../../shared/terminal-tab-types'
|
||||
import {
|
||||
restorePtyDataHandlersAfterFailedShutdown,
|
||||
@@ -134,13 +135,13 @@ export function createTerminalShutdownGuardController({
|
||||
stoppedPtyIds: string[]
|
||||
failure?: PromiseRejectedResult
|
||||
}> => {
|
||||
const localPtyIds = rendererShutdownPtyIds.filter((ptyId) => !ptyId.startsWith('remote:'))
|
||||
const localPtyIds = rendererShutdownPtyIds.filter((ptyId) => !isRemoteRuntimePtyId(ptyId))
|
||||
const results = await Promise.allSettled(
|
||||
localPtyIds.map((ptyId) => window.api.pty.kill(ptyId, { keepHistory: keepIdentifiers }))
|
||||
)
|
||||
const stoppedPtyIds = [
|
||||
...(runtimeEnvironmentId
|
||||
? rendererShutdownPtyIds.filter((ptyId) => ptyId.startsWith('remote:'))
|
||||
? rendererShutdownPtyIds.filter((ptyId) => isRemoteRuntimePtyId(ptyId))
|
||||
: []),
|
||||
...localPtyIds.filter((_, index) => results[index]?.status === 'fulfilled')
|
||||
]
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import {
|
||||
isRemoteRuntimePtyId,
|
||||
parseRemoteRuntimePtyId,
|
||||
toRemoteRuntimePtyId
|
||||
} from './remote-runtime-pty-id'
|
||||
import { getPtyExecutionHost } from './terminal-execution-host'
|
||||
|
||||
describe('isRemoteRuntimePtyId', () => {
|
||||
it('recognizes minted paired-runtime ids, with and without an owner', () => {
|
||||
expect(isRemoteRuntimePtyId(toRemoteRuntimePtyId('h1', 'env 1'))).toBe(true)
|
||||
expect(isRemoteRuntimePtyId(toRemoteRuntimePtyId('h1'))).toBe(true)
|
||||
})
|
||||
|
||||
it('rejects local, SSH and missing ids', () => {
|
||||
expect(isRemoteRuntimePtyId('pty-1')).toBe(false)
|
||||
expect(isRemoteRuntimePtyId('ssh:conn@@pty-1')).toBe(false)
|
||||
expect(isRemoteRuntimePtyId('')).toBe(false)
|
||||
expect(isRemoteRuntimePtyId(null)).toBe(false)
|
||||
expect(isRemoteRuntimePtyId(undefined)).toBe(false)
|
||||
})
|
||||
|
||||
// Pins the merge of the prefix-only and parse-based copies: an unparseable `remote:` id still
|
||||
// runs off this host, matching getPtyExecutionHost's 'foreign' verdict.
|
||||
it('treats an unparseable remote id as remote, never local', () => {
|
||||
const malformed = 'remote:%E0%A4%A@@handle'
|
||||
expect(parseRemoteRuntimePtyId(malformed)).toBeNull()
|
||||
expect(isRemoteRuntimePtyId(malformed)).toBe(true)
|
||||
expect(getPtyExecutionHost(malformed)).toBe('foreign')
|
||||
})
|
||||
})
|
||||
@@ -16,8 +16,13 @@ export function toRemoteRuntimePtyId(handle: string, environmentId?: string | nu
|
||||
return `${REMOTE_PTY_ID_PREFIX}${encodeURIComponent(owner)}${REMOTE_PTY_OWNER_SEPARATOR}${encodeURIComponent(handle)}`
|
||||
}
|
||||
|
||||
// Why prefix-only: a `remote:` id that fails to parse still runs off this host, never locally.
|
||||
export function isRemoteRuntimePtyId(ptyId: string | null | undefined): boolean {
|
||||
return typeof ptyId === 'string' && ptyId.startsWith(REMOTE_PTY_ID_PREFIX)
|
||||
}
|
||||
|
||||
export function parseRemoteRuntimePtyId(ptyId: string): RemoteRuntimePtyIdParts | null {
|
||||
if (!ptyId.startsWith(REMOTE_PTY_ID_PREFIX)) {
|
||||
if (!isRemoteRuntimePtyId(ptyId)) {
|
||||
return null
|
||||
}
|
||||
const rest = ptyId.slice(REMOTE_PTY_ID_PREFIX.length)
|
||||
|
||||
@@ -3,7 +3,7 @@ import {
|
||||
toSshExecutionHostId,
|
||||
type ExecutionHostId
|
||||
} from './execution-host'
|
||||
import { parseRemoteRuntimePtyId } from './remote-runtime-pty-id'
|
||||
import { parseRemoteRuntimePtyId, isRemoteRuntimePtyId } from './remote-runtime-pty-id'
|
||||
import { parseAppSshPtyId } from './ssh-pty-id'
|
||||
|
||||
/** 'foreign' = the id proves the PTY runs off this host but cannot name where. */
|
||||
@@ -30,7 +30,7 @@ export function getPtyExecutionHost(ptyId: string | null | undefined): PtyExecut
|
||||
? toRuntimeExecutionHostId(environmentId)
|
||||
: 'foreign'
|
||||
}
|
||||
if (ptyId.startsWith('ssh:') || ptyId.startsWith('remote:')) {
|
||||
if (ptyId.startsWith('ssh:') || isRemoteRuntimePtyId(ptyId)) {
|
||||
return 'foreign'
|
||||
}
|
||||
return null
|
||||
|
||||
Reference in New Issue
Block a user