feat(sandokan-remote): B1.4 — RemoteEngine vía SSH socket-forward

Opción B: RemoteEngine orquesta en un host remoto tunelando el wire
del daemon sobre un canal SSH direct-streamlocal hacia el sandokan.sock
remoto. El protocolo es idéntico al de DaemonEngine (postcard
length-prefixed) — sólo cambia el transporte, así que read_frame/
write_frame se reusan tal cual.

- brahman-ssh-multiplex: + SshSession::forward_unix — abre un canal
  direct-streamlocal y devuelve su ChannelStream (AsyncRead+AsyncWrite).
- sandokan-daemon: protocol ahora pub, exporta read_frame/write_frame.
- sandokan-remote: RemoteEngine { SshSession + remote_socket }.
  connect() o with_session(); cada operación abre un canal nuevo
  (multiplexado sobre la conexión maestra).
- sandokan umbrella re-exporta RemoteEngine.

Completa Fase B: sandokan tiene Local + Daemon + Remote + auto().
cargo check --workspace verde. RemoteEngine necesita un host remoto
con `sandokan daemon` para validación runtime (sin unit test).

Opción A (text-parse del CLI por compat) queda pendiente por decisión
del usuario.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
sergio
2026-05-20 15:37:11 +00:00
parent 0e13c35f3e
commit 05886022e0
9 changed files with 178 additions and 2 deletions
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "sandokan-remote"
version.workspace = true
edition.workspace = true
license.workspace = true
authors.workspace = true
publish.workspace = true
description = "sandokan RemoteEngine — orquesta en un host remoto tunelando el wire del daemon sobre un canal SSH direct-streamlocal."
[dependencies]
sandokan-core = { path = "../sandokan-core" }
sandokan-daemon = { path = "../sandokan-daemon" }
sandokan-lifecycle = { path = "../sandokan-lifecycle" }
brahman-ssh-multiplex = { path = "../../protocol/brahman-ssh-multiplex" }
async-trait = { workspace = true }
ulid = { workspace = true }