mirror of
https://github.com/stablyai/orca.git
synced 2026-10-03 00:02:19 +00:00
fix: publish relay-local SSH PTY IDs for stable workspace synchronization
This commit is contained in:
@@ -1,5 +1,8 @@
|
||||
import { getDefaultWorkspaceSession } from './constants'
|
||||
import { projectRemoteWorkspaceSshPtyOwner } from './remote-workspace-ssh-pty-owner'
|
||||
import {
|
||||
projectRemoteWorkspaceSshPtyOwner,
|
||||
stripRemoteWorkspaceSshPtyOwners
|
||||
} from './remote-workspace-ssh-pty-owner'
|
||||
import type { RemoteWorkspaceSession, RemoteWorkspaceTerminalTab } from './remote-workspace-types'
|
||||
import type { TerminalTab } from './terminal-tab-types'
|
||||
import type { WorkspaceSessionState } from './workspace-session-state-types'
|
||||
@@ -136,7 +139,7 @@ export function exportRemoteWorkspaceSession(
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
return stripRemoteWorkspaceSshPtyOwners({
|
||||
activeWorktreePath,
|
||||
activeTabId,
|
||||
tabsByWorktreePath,
|
||||
@@ -159,7 +162,7 @@ export function exportRemoteWorkspaceSession(
|
||||
: undefined,
|
||||
lastVisitedAtByWorktreePath,
|
||||
defaultTerminalTabsAppliedByWorktreePath
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function importRemoteWorkspaceSession(
|
||||
|
||||
@@ -37,6 +37,10 @@ it('imports a retained host session under the receiving client SSH target', () =
|
||||
remoteSessionIdsByTabId: { tab: oldPtyId }
|
||||
}
|
||||
const snapshot = exportRemoteWorkspaceSession(oldSession, { isTargetWorktree: () => true })
|
||||
// Previously published snapshots contain the publishing client's target IDs.
|
||||
snapshot.tabsByWorktreePath['/srv/project'][0].ptyId = oldPtyId
|
||||
snapshot.terminalLayoutsByTabId.tab.ptyIdsByLeafId = { leaf: oldPtyId }
|
||||
snapshot.remoteSessionIdsByTabId = { tab: oldPtyId }
|
||||
const imported = importRemoteWorkspaceSession(snapshot, {
|
||||
executionHostId: 'ssh:new-client-target',
|
||||
resolveWorktreeId: () => 'new-repo::/srv/project'
|
||||
@@ -86,3 +90,35 @@ it('preserves legacy, runtime, and malformed IDs while rebasing encoded SSH owne
|
||||
})
|
||||
expect(snapshot.remoteSessionIdsByTabId.prior).toBe(toAppSshPtyId('old-client', 'pty2:leaf:4'))
|
||||
})
|
||||
|
||||
it('publishes the same host session after either client imports it', () => {
|
||||
const hostSession = {
|
||||
activeWorktreePath: null,
|
||||
activeTabId: null,
|
||||
tabsByWorktreePath: {
|
||||
'/srv/project': [
|
||||
{
|
||||
id: 'tab',
|
||||
ptyId: toAppSshPtyId('client-a', 'pty-1'),
|
||||
worktreePath: '/srv/project',
|
||||
title: 'Shell',
|
||||
customTitle: null,
|
||||
color: null,
|
||||
sortOrder: 0,
|
||||
createdAt: 1
|
||||
}
|
||||
]
|
||||
},
|
||||
terminalLayoutsByTabId: {},
|
||||
remoteSessionIdsByTabId: { tab: toAppSshPtyId('client-a', 'pty-1') }
|
||||
}
|
||||
const republish = (target: string) =>
|
||||
exportRemoteWorkspaceSession(
|
||||
importRemoteWorkspaceSession(hostSession, {
|
||||
executionHostId: `ssh:${target}`,
|
||||
resolveWorktreeId: () => `${target}::/srv/project`
|
||||
}),
|
||||
{ isTargetWorktree: () => true }
|
||||
)
|
||||
expect(republish('client-a')).toEqual(republish('client-b'))
|
||||
})
|
||||
|
||||
@@ -11,10 +11,22 @@ export function projectRemoteWorkspaceSshPtyOwner(
|
||||
return session
|
||||
}
|
||||
// The authenticated host owns the snapshot; target IDs belong to each importing client.
|
||||
const project = (id: string): string => {
|
||||
return mapSshPtyIds(session, (id) => {
|
||||
const parsed = parseAppSshPtyId(id)
|
||||
return parsed ? toAppSshPtyId(host.targetId, parsed.relayPtyId) : id
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
export function stripRemoteWorkspaceSshPtyOwners(
|
||||
session: RemoteWorkspaceSession
|
||||
): RemoteWorkspaceSession {
|
||||
return mapSshPtyIds(session, (id) => parseAppSshPtyId(id)?.relayPtyId ?? id)
|
||||
}
|
||||
|
||||
function mapSshPtyIds(
|
||||
session: RemoteWorkspaceSession,
|
||||
project: (id: string) => string
|
||||
): RemoteWorkspaceSession {
|
||||
const projectIds = (ids: Record<string, string>): Record<string, string> =>
|
||||
Object.fromEntries(Object.entries(ids).map(([key, id]) => [key, project(id)]))
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user