@team-agent/installer 0.5.116 → 0.5.117
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.lock +1 -1
- package/Cargo.toml +1 -1
- package/crates/team-agent/src/leader/lease.rs +1 -18
- package/crates/team-agent/src/lifecycle/pane_input_lock.rs +86 -8
- package/crates/team-agent/src/messaging/delivery.rs +132 -27
- package/crates/team-agent/src/messaging/enter_rescue.rs +334 -0
- package/crates/team-agent/src/messaging/enter_rescue_tests.rs +566 -0
- package/crates/team-agent/src/messaging/helpers.rs +1 -0
- package/crates/team-agent/src/messaging/mod.rs +1 -0
- package/crates/team-agent/src/messaging/scheduler.rs +8 -0
- package/crates/team-agent/src/messaging/tests/basic.rs +2 -0
- package/crates/team-agent/src/messaging/types.rs +1 -0
- package/crates/team-agent/src/state/repository.rs +47 -0
- package/crates/team-agent/src/tmux_backend/native_input.rs +494 -0
- package/crates/team-agent/src/tmux_backend/native_input_tests.rs +781 -0
- package/crates/team-agent/src/tmux_backend.rs +49 -6
- package/crates/team-agent/src/transport/native_input.rs +594 -0
- package/crates/team-agent/src/transport.rs +19 -0
- package/package.json +4 -4
|
@@ -73,6 +73,8 @@ use crate::transport::{
|
|
|
73
73
|
SubmitVerification, Target, Transport, TransportError, TurnVerification, WindowName,
|
|
74
74
|
};
|
|
75
75
|
|
|
76
|
+
mod native_input;
|
|
77
|
+
|
|
76
78
|
pub const PANE_BINDING_NONCE_METADATA_KEY: &str = "TEAM_AGENT_PANE_BINDING_NONCE";
|
|
77
79
|
pub(crate) const KEEPALIVE_WINDOW_NAME: &str = "[team-agent keepalive]";
|
|
78
80
|
const TMUX_PANE_BINDING_NONCE_OPTION: &str = "@team_agent_pane_binding_nonce";
|
|
@@ -80,6 +82,29 @@ const TMUX_SESSION_OWNER_WORKSPACE_OPTION: &str = "@team_agent_owner_workspace";
|
|
|
80
82
|
const TMUX_SESSION_OWNER_TEAM_OPTION: &str = "@team_agent_owner_team";
|
|
81
83
|
const TMUX_SESSION_OWNER_GENERATION_OPTION: &str = "@team_agent_owner_generation";
|
|
82
84
|
|
|
85
|
+
/// Shared existing pane nonce generator; transport owns the physical nonce.
|
|
86
|
+
/// Same material/algorithm as the original Leader claim producer, no new dependency.
|
|
87
|
+
pub(crate) fn next_pane_binding_nonce(workspace: &Path, endpoint: &str, pane: &PaneId) -> String {
|
|
88
|
+
use sha2::{Digest, Sha256};
|
|
89
|
+
use std::sync::atomic::{AtomicU64, Ordering};
|
|
90
|
+
use std::time::{SystemTime, UNIX_EPOCH};
|
|
91
|
+
|
|
92
|
+
static COUNTER: AtomicU64 = AtomicU64::new(0);
|
|
93
|
+
let nanos = SystemTime::now()
|
|
94
|
+
.duration_since(UNIX_EPOCH)
|
|
95
|
+
.map_or(0, |duration| duration.as_nanos());
|
|
96
|
+
let counter = COUNTER.fetch_add(1, Ordering::Relaxed);
|
|
97
|
+
let canonical = workspace.canonicalize().unwrap_or_else(|_| workspace.to_path_buf());
|
|
98
|
+
let material = format!(
|
|
99
|
+
"{}\0{endpoint}\0{}\0{}\0{nanos}\0{counter}",
|
|
100
|
+
canonical.display(),
|
|
101
|
+
pane.as_str(),
|
|
102
|
+
std::process::id(),
|
|
103
|
+
);
|
|
104
|
+
let digest = Sha256::digest(material.as_bytes());
|
|
105
|
+
digest[..16].iter().map(|byte| format!("{byte:02x}")).collect()
|
|
106
|
+
}
|
|
107
|
+
|
|
83
108
|
/// Result of running an external command — the typed output of the OS edge.
|
|
84
109
|
#[derive(Debug, Clone)]
|
|
85
110
|
pub struct CommandOutput {
|
|
@@ -687,7 +712,7 @@ pub(crate) fn tmux_backend_for_runtime_state_or_workspace(
|
|
|
687
712
|
workspace: &Path,
|
|
688
713
|
state: Option<&serde_json::Value>,
|
|
689
714
|
) -> RuntimeTmuxBackendSelection {
|
|
690
|
-
let (backend, source) =
|
|
715
|
+
let (mut backend, source) =
|
|
691
716
|
if let Some((endpoint, source)) = runtime_tmux_endpoint_from_state(state) {
|
|
692
717
|
(TmuxBackend::for_tmux_endpoint(endpoint), source)
|
|
693
718
|
} else {
|
|
@@ -696,6 +721,8 @@ pub(crate) fn tmux_backend_for_runtime_state_or_workspace(
|
|
|
696
721
|
RuntimeTmuxEndpointSource::WorkspaceFallback,
|
|
697
722
|
)
|
|
698
723
|
};
|
|
724
|
+
// A persisted endpoint changes addressing, not the owning input-lock scope.
|
|
725
|
+
backend.event_workspace = Some(workspace.to_path_buf());
|
|
699
726
|
RuntimeTmuxBackendSelection {
|
|
700
727
|
tmux_endpoint_used: backend.tmux_endpoint(),
|
|
701
728
|
backend,
|
|
@@ -719,7 +746,7 @@ pub(crate) fn tmux_backend_with_runner_for_runtime_state_or_workspace(
|
|
|
719
746
|
workspace: &Path,
|
|
720
747
|
state: Option<&serde_json::Value>,
|
|
721
748
|
) -> RuntimeTmuxBackendSelection {
|
|
722
|
-
let (backend, source) =
|
|
749
|
+
let (mut backend, source) =
|
|
723
750
|
if let Some((endpoint, source)) = runtime_tmux_endpoint_from_state(state) {
|
|
724
751
|
(
|
|
725
752
|
TmuxBackend::with_runner_for_tmux_endpoint(runner, endpoint),
|
|
@@ -731,6 +758,7 @@ pub(crate) fn tmux_backend_with_runner_for_runtime_state_or_workspace(
|
|
|
731
758
|
RuntimeTmuxEndpointSource::WorkspaceFallback,
|
|
732
759
|
)
|
|
733
760
|
};
|
|
761
|
+
backend.event_workspace = Some(workspace.to_path_buf());
|
|
734
762
|
RuntimeTmuxBackendSelection {
|
|
735
763
|
tmux_endpoint_used: backend.tmux_endpoint(),
|
|
736
764
|
backend,
|
|
@@ -3069,6 +3097,10 @@ impl Transport for TmuxBackend {
|
|
|
3069
3097
|
true
|
|
3070
3098
|
}
|
|
3071
3099
|
|
|
3100
|
+
fn supports_owned_native_input(&self) -> bool {
|
|
3101
|
+
true
|
|
3102
|
+
}
|
|
3103
|
+
|
|
3072
3104
|
fn tmux_endpoint(&self) -> Option<String> {
|
|
3073
3105
|
self.socket
|
|
3074
3106
|
.as_ref()
|
|
@@ -3249,14 +3281,19 @@ impl Transport for TmuxBackend {
|
|
|
3249
3281
|
bracketed: bool,
|
|
3250
3282
|
observer: Option<&dyn SubmitObserver>,
|
|
3251
3283
|
) -> Result<InjectReport, TransportError> {
|
|
3284
|
+
if let (Some(request), InjectPayload::Text(text), Key::Enter) =
|
|
3285
|
+
(crate::transport::native_input::current_request(), payload, submit)
|
|
3286
|
+
{
|
|
3287
|
+
return self.inject_native_input(target, text, bracketed, request, observer);
|
|
3288
|
+
}
|
|
3252
3289
|
let pane = pane_from_target(target);
|
|
3253
|
-
let _pane_input = crate::lifecycle::pane_input_lock::
|
|
3290
|
+
let _pane_input = crate::lifecycle::pane_input_lock::acquire_for_write(
|
|
3254
3291
|
crate::lifecycle::pane_input_lock::PaneInputLockRequest {
|
|
3255
3292
|
workspace: self.event_workspace.as_deref(),
|
|
3256
3293
|
target_key: pane.as_str(),
|
|
3257
3294
|
operation: "inject",
|
|
3258
3295
|
},
|
|
3259
|
-
)
|
|
3296
|
+
).map_err(|_| TransportError::NativeInputNotReady { reason: "required_guard_active".into() })?;
|
|
3260
3297
|
// U1 #7: pane readback signal for the non-pasted-prompt text path.
|
|
3261
3298
|
let mut token_visible_for_report: Option<bool> = None;
|
|
3262
3299
|
match payload {
|
|
@@ -3811,15 +3848,21 @@ impl Transport for TmuxBackend {
|
|
|
3811
3848
|
})
|
|
3812
3849
|
}
|
|
3813
3850
|
|
|
3851
|
+
fn rescue_enter(&self, evidence: &crate::transport::native_input::NativeInputEvidence, authorize: &dyn Fn() -> bool, observer: Option<&dyn SubmitObserver>)
|
|
3852
|
+
-> Result<crate::transport::native_input::EnterRescueOutcome, TransportError>
|
|
3853
|
+
{
|
|
3854
|
+
self.rescue_native_input(evidence, authorize, observer)
|
|
3855
|
+
}
|
|
3856
|
+
|
|
3814
3857
|
fn send_keys(&self, target: &Target, keys: &[Key]) -> Result<(), TransportError> {
|
|
3815
3858
|
let pane = pane_from_target(target);
|
|
3816
|
-
let _pane_input = crate::lifecycle::pane_input_lock::
|
|
3859
|
+
let _pane_input = crate::lifecycle::pane_input_lock::acquire_for_write(
|
|
3817
3860
|
crate::lifecycle::pane_input_lock::PaneInputLockRequest {
|
|
3818
3861
|
workspace: self.event_workspace.as_deref(),
|
|
3819
3862
|
target_key: pane.as_str(),
|
|
3820
3863
|
operation: "send_keys",
|
|
3821
3864
|
},
|
|
3822
|
-
)
|
|
3865
|
+
).map_err(|_| TransportError::NativeInputNotReady { reason: "required_guard_active".into() })?;
|
|
3823
3866
|
if keys.contains(&Key::CancelMode) {
|
|
3824
3867
|
if let Some(mode) = pane_mode_from_raw(self.query(target, PaneField::PaneMode)?) {
|
|
3825
3868
|
let argv = crate::transport::tmux_cancel_mode_argv(&pane, mode);
|