Skip to main content

agentmux_srv/backend/blockcontroller/shell/
mod.rs

1// Copyright 2025-2026, AgentMux Corp.
2// SPDX-License-Identifier: Apache-2.0
3
4//! ShellController: manages lifecycle of shell and command blocks.
5//! Port of Go's pkg/blockcontroller/shellcontroller.go.
6//!
7//! State machine:
8//!   INIT ─(start)─> RUNNING ─(exit/stop)─> DONE
9//!   DONE ─(resync+force)─> RUNNING
10//!
11//! I/O model (3 async tasks when running):
12//! 1. PTY read loop: process stdout → FileStore + MPS event
13//! 2. Input loop: input channel → process stdin
14//! 3. Wait loop: monitor process exit, update status
15//!
16//! ## Module layout
17//!
18//! This was one 2,394-line file; it is now a submodule directory (pure
19//! reorganization, no logic changes). The `impl Controller for ShellController`
20//! block is kept whole in [`lifecycle`] — a trait impl cannot be split across
21//! files — and it calls the free-function helpers extracted into [`pty`],
22//! [`file_ops`], [`indexing`], and [`translation`]. The struct definitions,
23//! constructor, and small accessor/meta helpers live in [`controller`].
24//!
25//! The `pub use` re-exports below preserve the pre-split flat paths
26//! (`shell::<item>`) that callers elsewhere in the crate already import, so the
27//! reorganization changed no call site.
28
29mod controller;
30mod file_ops;
31mod indexing;
32mod lifecycle;
33mod pty;
34mod spawn_cwd;
35mod ssh_pane;
36pub(crate) mod stamps;
37mod translation;
38
39// Flat re-exports preserving the pre-split `shell::<item>` call sites used
40// elsewhere in the crate (blockcontroller/mod.rs, acp, persistent, subprocess,
41// watchdog, agent_handlers/input, blockfile, app_api).
42pub use controller::ShellController;
43pub use file_ops::{
44    append_output_line, handle_append_block_file, persist_user_line, publish_transcript_changed,
45    with_transcript_order,
46};
47// These are `pub(crate)` at their definition (crate-internal API), so they must
48// be re-exported at the same visibility — `pub use` of a `pub(crate)` item is
49// rejected (E0364).
50pub(crate) use file_ops::resolve_global_output_zone;
51pub(crate) use indexing::{extend_output_idx, output_index, output_now, read_via_index, read_via_stale_index};
52// A full rebuild from byte 0: the readers extend instead (`extend_output_idx`
53// falls back to one itself), so only tests call it directly.
54#[cfg(test)]
55pub(crate) use indexing::rebuild_output_idx;
56
57#[cfg(test)]
58mod tests;
59#[cfg(test)]
60mod tests_transcript;