mirror of
https://github.com/stablyai/orca.git
synced 2026-10-19 00:03:20 +00:00
* Sweep all persisted carriers of a removed SSH target id on re-adoption reassignSshTargetId re-pointed repos and worktree metas but left the old target id embedded in persisted session pty ids (ssh:<id>@@pty-N in tabs, layouts, remoteSessionIdsByTabId), the startup reconnect list (activeConnectionIdsAtShutdown, replayed via ssh.connect at boot — the exact 'SSH target not found' in STA-1468), sleeping-agent resume records, provisioned project host setups, sidebar host-scope arrays, and relay pty leases. Any survivor resurfaces later as a failing connect or reattach. New ssh-target-id-migration module re-points every carrier in one pass, wired into reassignSshTargetId with per-carrier unit and store-level round-trip tests. Co-authored-by: Orca <help@stably.ai> * Bridge SSH connection state to paired remote clients The SSH surface was desktop-only: ssh:state-changed went to the host's own BrowserWindow and the web client's ssh API was a no-op stub, so a paired client's reconnect overlay never learned the host connected and its target labels stayed empty (STA-1468 — overlay stuck on 'please connect' over a live terminal). - New sshStateChanged runtime client event, emitted from broadcastSshState through OrcaRuntimeService onto the existing clientEvents stream. - New ssh.listTargets / ssh.listRemovedTargetLabels RPC methods next to the previously unused ssh.getState / ssh.connect. - Web preload now routes listTargets / listRemovedTargetLabels / getState / connect to the paired host's runtime RPC instead of stubbing them. - useIpcEvents applies sshStateChanged on paired web clients through the same guarded path as desktop ssh.onStateChanged; desktop clients ignore the event since a foreign runtime's targets would pollute their local SSH store. Co-authored-by: Orca <help@stably.ai> * Harden the SSH reconnect overlay against stale or unknown target state - Only present the destructive 'SSH host removed' state on positive evidence (a removal tombstone label, or a hydrated non-empty target list lacking the id). A client whose SSH state never hydrated has an empty labels map for every id and must not offer workspace removal. - After a failed Connect, resync target metadata so a stale overlay converges to the ghost/re-adopted state instead of offering the same failing Connect forever (the repeated 'SSH target not found' toast loop in STA-1468). Co-authored-by: Orca <help@stably.ai> * Address CodeRabbit review on #7767 - Re-key workspaceSessionsByHostId partitions stored under a removed SSH host id during re-adoption (no writer keys partitions by ssh host today, but the schema tolerates it — re-key instead of stranding; live partition wins when both keys exist). - Track SSH target-list hydration explicitly (sshTargetsHydrated) instead of inferring it from a non-empty label map, so a legitimately empty target list still counts as removal evidence and a never-hydrated client still never offers destructive removal. - Apply the refreshed target list before the best-effort removed-labels fetch in the overlay resync, so a labels failure can't discard it. Co-authored-by: Orca <help@stably.ai> --------- Co-authored-by: Orca <help@stably.ai>
36 lines
950 B
TypeScript
36 lines
950 B
TypeScript
import { z } from 'zod'
|
|
import {
|
|
connectRegisteredSshTarget,
|
|
getRegisteredSshState,
|
|
listRegisteredRemovedSshTargetLabels,
|
|
listRegisteredSshTargets
|
|
} from '../../../ipc/ssh'
|
|
import { defineMethod, type RpcMethod } from '../core'
|
|
|
|
const SshTarget = z.object({
|
|
targetId: z.string().min(1)
|
|
})
|
|
|
|
export const SSH_METHODS: RpcMethod[] = [
|
|
defineMethod({
|
|
name: 'ssh.getState',
|
|
params: SshTarget,
|
|
handler: (params) => ({ state: getRegisteredSshState(params.targetId) ?? null })
|
|
}),
|
|
defineMethod({
|
|
name: 'ssh.connect',
|
|
params: SshTarget,
|
|
handler: async (params) => ({ state: await connectRegisteredSshTarget(params.targetId) })
|
|
}),
|
|
defineMethod({
|
|
name: 'ssh.listTargets',
|
|
params: null,
|
|
handler: () => ({ targets: listRegisteredSshTargets() })
|
|
}),
|
|
defineMethod({
|
|
name: 'ssh.listRemovedTargetLabels',
|
|
params: null,
|
|
handler: () => ({ labels: listRegisteredRemovedSshTargetLabels() })
|
|
})
|
|
]
|