Skip to content

Commit 3db84fa

Browse files
committed
Address PR review feedback (#5360)
- FileExplorerError.sshCommandFailed: surface only the first non-empty line of the SSH stderr in the user-visible message so multi-line SSH banners (host-key warnings, connection notices) don't appear verbatim in the Files panel error label. Full stderr is still passed in via the associated value for log/telemetry callers. - restoreSessionSnapshot: never trust a persisted .remoteReport. The semantic of .remoteReport is "confirmed by a live remote shell", but a relaunched workspace has not re-confirmed anything yet — the remote shell may have started in a different cwd, or the persisted path may no longer exist. Coerce restored .remoteReport to .localSeed so the SSH file explorer falls back to remote $HOME via resolveRemoteHome on reconnect; the first .liveReport from the reconnected shell flips the origin back to .remoteReport. - bash/zsh integration: cache _CMUX_PWD_LAST_PWD only after the relay dispatch succeeds. The previous unconditional cache suppressed retries when the relay CLI path or remote-relay socket detection transiently fails on the first prompt of a new shell. - FileExplorerSSHRootSeedingTests: rename testLegacySnapshotMissingOriginDecodesAsLocalSeed to ...DecodesAsNil to accurately describe what's asserted (the decoder contract — Workspace.restoreSessionSnapshot's nil → .localSeed coercion is exercised end-to-end via the manual restore path).
1 parent c7276a1 commit 3db84fa

5 files changed

Lines changed: 40 additions & 10 deletions

File tree

Resources/shell-integration/cmux-bash-integration.bash

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1476,10 +1476,14 @@ _cmux_prompt_command() {
14761476
fi
14771477
# CWD report — relay path only sends via JSON-RPC (issue #5360);
14781478
# the rest of _cmux_prompt_command is gated on a Unix socket.
1479+
# Cache _CMUX_PWD_LAST_PWD only after the dispatch succeeds so a
1480+
# transient failure (relay CLI missing, socket not ready, etc.) is
1481+
# retried on the next prompt instead of being silently suppressed.
14791482
local relay_pwd="$PWD"
14801483
if [[ "$relay_pwd" != "$_CMUX_PWD_LAST_PWD" ]]; then
1481-
_CMUX_PWD_LAST_PWD="$relay_pwd"
1482-
_cmux_report_pwd_via_relay "$relay_pwd" || true
1484+
if _cmux_report_pwd_via_relay "$relay_pwd"; then
1485+
_CMUX_PWD_LAST_PWD="$relay_pwd"
1486+
fi
14831487
fi
14841488
return 0
14851489
fi

Resources/shell-integration/cmux-zsh-integration.zsh

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1668,10 +1668,14 @@ _cmux_precmd() {
16681668
fi
16691669
# CWD report — relay path only sends via JSON-RPC (issue #5360);
16701670
# the rest of _cmux_prompt_command is gated on a Unix socket.
1671+
# Cache _CMUX_PWD_LAST_PWD only after the dispatch succeeds so a
1672+
# transient failure (relay CLI missing, socket not ready, etc.) is
1673+
# retried on the next prompt instead of being silently suppressed.
16711674
local relay_pwd="$PWD"
16721675
if [[ "$relay_pwd" != "$_CMUX_PWD_LAST_PWD" ]]; then
1673-
_CMUX_PWD_LAST_PWD="$relay_pwd"
1674-
_cmux_report_pwd_via_relay "$relay_pwd" || true
1676+
if _cmux_report_pwd_via_relay "$relay_pwd"; then
1677+
_CMUX_PWD_LAST_PWD="$relay_pwd"
1678+
fi
16751679
fi
16761680
return 0
16771681
fi

Sources/FileExplorerStore.swift

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -718,8 +718,16 @@ enum FileExplorerError: LocalizedError {
718718
case .providerUnavailable:
719719
return String(localized: "fileExplorer.error.unavailable", defaultValue: "File explorer is not available")
720720
case .sshCommandFailed(let detail):
721-
let trimmed = detail.trimmingCharacters(in: .whitespacesAndNewlines)
722-
if trimmed.isEmpty {
721+
// Surface only the first non-empty line so multi-line SSH banners
722+
// (host-key notices, connection warnings) don't appear verbatim in
723+
// the Files panel; the full stderr is still passed in via the
724+
// associated value for log/telemetry callers.
725+
let firstLine = detail
726+
.components(separatedBy: .newlines)
727+
.lazy
728+
.map { $0.trimmingCharacters(in: .whitespaces) }
729+
.first(where: { !$0.isEmpty }) ?? ""
730+
if firstLine.isEmpty {
723731
return String(
724732
localized: "fileExplorer.error.sshFailed",
725733
defaultValue: "SSH command failed"
@@ -729,7 +737,7 @@ enum FileExplorerError: LocalizedError {
729737
localized: "fileExplorer.error.sshFailedWithDetail",
730738
defaultValue: "SSH command failed: %@"
731739
)
732-
return String(format: format, trimmed)
740+
return String(format: format, firstLine)
733741
}
734742
}
735743
}

Sources/Workspace.swift

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,19 @@ extension Workspace {
295295
if !normalizedCurrentDirectory.isEmpty {
296296
currentDirectory = normalizedCurrentDirectory
297297
}
298-
currentDirectoryOrigin = snapshot.currentDirectoryOrigin ?? .localSeed
298+
// Restored origin: never trust a persisted .remoteReport. The semantic of
299+
// .remoteReport is "confirmed by a live remote shell", but a relaunched
300+
// workspace has not re-confirmed anything yet — the remote shell may have
301+
// started in a different cwd, or the persisted path may no longer exist.
302+
// Treat .remoteReport as .localSeed so the SSH file explorer falls back
303+
// to remote $HOME via resolveRemoteHome; the first .liveReport from the
304+
// reconnected shell will flip the origin back to .remoteReport.
305+
switch snapshot.currentDirectoryOrigin {
306+
case nil, .remoteReport:
307+
currentDirectoryOrigin = .localSeed
308+
case .localSeed?, .localKnown?:
309+
currentDirectoryOrigin = snapshot.currentDirectoryOrigin ?? .localSeed
310+
}
299311

300312
let panelSnapshotsById = Dictionary(uniqueKeysWithValues: snapshot.panels.map { ($0.id, $0) })
301313
let leafEntries: [SessionPaneRestoreEntry] = {

cmuxTests/FileExplorerSSHRootSeedingTests.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,9 +59,11 @@ final class FileExplorerSSHRootSeedingTests: XCTestCase {
5959
XCTAssertEqual(workspace.fileExplorerRemoteRootPath, "/home/dev/proj")
6060
}
6161

62-
func testLegacySnapshotMissingOriginDecodesAsLocalSeed() throws {
62+
func testLegacySnapshotMissingOriginDecodesAsNil() throws {
6363
// Old session-com.cmuxterm.app.json files do not carry currentDirectoryOrigin.
64-
// They must decode to .localSeed for back-compat.
64+
// The decode contract is: missing field → nil; restoreSessionSnapshot then
65+
// coerces nil → .localSeed via `?? .localSeed` so the SSH file explorer
66+
// falls back to remote $HOME on legacy snapshots.
6567
let json = """
6668
{
6769
"processTitle": "Terminal",

0 commit comments

Comments
 (0)