channels: add open/close RPCs for dynamic pane management
Add open/close to the channel capnp schema. The tmux daemon implements open by finding a pane by name (pane title or window name) and attaching pipe-pane; close detaches and removes from state. Tool handlers channel_open and channel_close added to the tool registry. Co-Authored-By: Proof of Concept <poc@bcachefs.org>
This commit is contained in:
parent
a14e85afe1
commit
e8e9386856
3 changed files with 153 additions and 1 deletions
|
|
@ -218,6 +218,98 @@ impl channel_server::Server for ChannelServerImpl {
|
|||
) -> Promise<(), capnp::Error> {
|
||||
Promise::ok(())
|
||||
}
|
||||
|
||||
fn open(
|
||||
&mut self,
|
||||
params: channel_server::OpenParams,
|
||||
_results: channel_server::OpenResults,
|
||||
) -> Promise<(), capnp::Error> {
|
||||
let params = pry!(params.get());
|
||||
let label = pry!(pry!(params.get_label()).to_str()).to_string();
|
||||
|
||||
// Check if already open
|
||||
{
|
||||
let s = self.state.borrow();
|
||||
if s.pane_labels.contains(&label) {
|
||||
return Promise::ok(());
|
||||
}
|
||||
}
|
||||
|
||||
// Find the tmux pane by name (window or pane title)
|
||||
let pane_id = match find_pane_by_name(&label) {
|
||||
Some(id) => id,
|
||||
None => return Promise::err(capnp::Error::failed(
|
||||
format!("no tmux pane named '{}'", label))),
|
||||
};
|
||||
|
||||
info!("opening channel tmux.{} (pane {})", label, pane_id);
|
||||
|
||||
// Register in state
|
||||
{
|
||||
let mut s = self.state.borrow_mut();
|
||||
s.pane_labels.push(label.clone());
|
||||
}
|
||||
|
||||
// Start pipe-pane reader
|
||||
let pane = PaneConfig { pane_id, label };
|
||||
let reader_state = self.state.clone();
|
||||
tokio::task::spawn_local(async move {
|
||||
pipe_pane_reader(reader_state, pane).await;
|
||||
});
|
||||
|
||||
Promise::ok(())
|
||||
}
|
||||
|
||||
fn close(
|
||||
&mut self,
|
||||
params: channel_server::CloseParams,
|
||||
_results: channel_server::CloseResults,
|
||||
) -> Promise<(), capnp::Error> {
|
||||
let params = pry!(params.get());
|
||||
let channel = pry!(pry!(params.get_channel()).to_str()).to_string();
|
||||
let label = channel.strip_prefix("tmux.").unwrap_or(&channel).to_string();
|
||||
|
||||
let mut s = self.state.borrow_mut();
|
||||
if let Some(pos) = s.pane_labels.iter().position(|l| *l == label) {
|
||||
info!("closing channel tmux.{}", label);
|
||||
s.pane_labels.remove(pos);
|
||||
s.channel_logs.remove(&format!("tmux.{}", label));
|
||||
|
||||
// Disconnect pipe-pane — find the pane ID
|
||||
if let Some(pane_id) = find_pane_by_name(&label) {
|
||||
let _ = std::process::Command::new("tmux")
|
||||
.args(["pipe-pane", "-t", &pane_id])
|
||||
.output();
|
||||
}
|
||||
}
|
||||
|
||||
Promise::ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// ── Pane lookup ──────────────────────────────────────────────
|
||||
|
||||
/// Find a tmux pane by its title/name. Returns the pane ID (e.g. "%5")
|
||||
/// if found. Searches pane titles first, then window names.
|
||||
fn find_pane_by_name(name: &str) -> Option<String> {
|
||||
let output = std::process::Command::new("tmux")
|
||||
.args(["list-panes", "-a", "-F", "#{pane_id}\t#{pane_title}\t#{window_name}"])
|
||||
.output()
|
||||
.ok()?;
|
||||
if !output.status.success() { return None; }
|
||||
|
||||
let stdout = String::from_utf8_lossy(&output.stdout);
|
||||
for line in stdout.lines() {
|
||||
let parts: Vec<&str> = line.splitn(3, '\t').collect();
|
||||
if parts.len() < 3 { continue; }
|
||||
let pane_id = parts[0];
|
||||
let pane_title = parts[1];
|
||||
let window_name = parts[2];
|
||||
if pane_title == name || window_name == name {
|
||||
return Some(pane_id.to_string());
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
// ── Cleanup ───────────────────────────────────────────────────
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue