@team-agent/installer 0.5.1 → 0.5.3
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 +120 -9
- package/Cargo.toml +2 -2
- package/crates/team-agent/Cargo.toml +21 -0
- package/crates/team-agent/src/app_server_test_support.rs +258 -0
- package/crates/team-agent/src/cli/adapters.rs +32 -3
- package/crates/team-agent/src/cli/attach_app_server_leader.rs +16 -0
- package/crates/team-agent/src/cli/diagnose.rs +14 -5
- package/crates/team-agent/src/cli/emit.rs +79 -1
- package/crates/team-agent/src/cli/mod.rs +190 -80
- package/crates/team-agent/src/cli/named_address.rs +111 -0
- package/crates/team-agent/src/cli/send.rs +98 -3
- package/crates/team-agent/src/cli/status_port.rs +44 -6
- package/crates/team-agent/src/cli/tests/named_address.rs +135 -0
- package/crates/team-agent/src/cli/tests/run_delegation.rs +2 -0
- package/crates/team-agent/src/cli/types.rs +17 -0
- package/crates/team-agent/src/codex_app_server.rs +549 -0
- package/crates/team-agent/src/conpty/backend.rs +822 -0
- package/crates/team-agent/src/conpty/mod.rs +32 -0
- package/crates/team-agent/src/coordinator/backoff.rs +132 -7
- package/crates/team-agent/src/coordinator/conpty_shim.rs +730 -0
- package/crates/team-agent/src/coordinator/health.rs +97 -11
- package/crates/team-agent/src/coordinator/mod.rs +8 -0
- package/crates/team-agent/src/coordinator/tests/daemon.rs +2 -1
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +6 -0
- package/crates/team-agent/src/diagnose/orphans.rs +29 -10
- package/crates/team-agent/src/leader/lease.rs +144 -7
- package/crates/team-agent/src/leader/owner_bind.rs +5 -2
- package/crates/team-agent/src/leader/provider_attribution.rs +19 -34
- package/crates/team-agent/src/leader/start.rs +18 -5
- package/crates/team-agent/src/leader/tests/lease_api.rs +179 -0
- package/crates/team-agent/src/lib.rs +36 -0
- package/crates/team-agent/src/lifecycle/launch.rs +207 -94
- package/crates/team-agent/src/lifecycle/lock.rs +40 -33
- package/crates/team-agent/src/lifecycle/restart/agent.rs +10 -18
- package/crates/team-agent/src/lifecycle/restart/common.rs +70 -0
- package/crates/team-agent/src/lifecycle/tests/agent_ops.rs +7 -0
- package/crates/team-agent/src/lifecycle/tests/launch_spawn.rs +7 -0
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +63 -0
- package/crates/team-agent/src/mcp_server/wire.rs +6 -7
- package/crates/team-agent/src/messaging/delivery.rs +329 -9
- package/crates/team-agent/src/messaging/leader_receiver.rs +17 -138
- package/crates/team-agent/src/messaging/mod.rs +2 -2
- package/crates/team-agent/src/messaging/results.rs +78 -21
- package/crates/team-agent/src/messaging/tests/runtime.rs +237 -0
- package/crates/team-agent/src/packaging/tests.rs +41 -6
- package/crates/team-agent/src/packaging/types.rs +31 -3
- package/crates/team-agent/src/platform/argv.rs +324 -0
- package/crates/team-agent/src/platform/errors.rs +95 -0
- package/crates/team-agent/src/platform/file_lock.rs +418 -0
- package/crates/team-agent/src/platform/mod.rs +66 -0
- package/crates/team-agent/src/platform/process.rs +555 -0
- package/crates/team-agent/src/state/persist.rs +205 -25
- package/crates/team-agent/src/tmux_backend.rs +94 -13
- package/crates/team-agent/src/transport_factory.rs +751 -0
- package/package.json +4 -4
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
//! 0.5.x Windows-native transport Phase 1: ConPTY backend.
|
|
2
|
+
//!
|
|
3
|
+
//! Wire protocol + portable shim state machine live in the
|
|
4
|
+
//! `conpty-transport` crate (portable, no team-agent runtime deps —
|
|
5
|
+
//! consumed directly by the Windows `windows-shim` binary). This
|
|
6
|
+
//! module wires that portable machinery into team-agent's `Transport`
|
|
7
|
+
//! trait via `ConPtyBackend`.
|
|
8
|
+
//!
|
|
9
|
+
//! Truth sources (READ-ONLY):
|
|
10
|
+
//! - Design: `.team/artifacts/0.5.x-windows-native-transport-design.md`
|
|
11
|
+
//! - CR verdict: `.team/artifacts/0.5.x-windows-transport-cr-verdict.md`
|
|
12
|
+
//!
|
|
13
|
+
//! CR anchors:
|
|
14
|
+
//! - **C-1 pipe_token**: only lives in `ConPtyBackend.pipe_token`
|
|
15
|
+
//! (Mutex) and the shim's in-memory state; never persisted. Grep
|
|
16
|
+
//! guard test (`conpty_pipe_token_no_persist_guard.rs`) locks this.
|
|
17
|
+
//! - **C-2 kill_server**: forwards to `Op::Shutdown` per-team, not
|
|
18
|
+
//! global. See CR pre-check in the gate report.
|
|
19
|
+
//! - **C-3 stale event**: emitted by the backend when `state.shim_pid`
|
|
20
|
+
//! disagrees with the shim's `hello` response.
|
|
21
|
+
//! - **C-5 pipe_token_mismatch**: `ProtocolError::PipeTokenMismatch`
|
|
22
|
+
//! maps to `TransportError::MuxUnavailable`, NEVER silent retry.
|
|
23
|
+
|
|
24
|
+
pub mod backend;
|
|
25
|
+
|
|
26
|
+
pub use backend::{ConPtyBackend, PipeClientAdapter, PipeClientTrait};
|
|
27
|
+
// Re-export the portable protocol + shim types from the standalone
|
|
28
|
+
// crate so downstream code can `use team_agent::conpty::{Shim,
|
|
29
|
+
// FakePaneRuntime, ...}` without a second dependency.
|
|
30
|
+
pub use conpty_transport::{
|
|
31
|
+
protocol, shim, FakePaneRuntime, LocalShimClient, PaneRuntime, PipeClient, Shim,
|
|
32
|
+
};
|
|
@@ -27,6 +27,14 @@ pub struct DaemonArgs {
|
|
|
27
27
|
pub once: bool,
|
|
28
28
|
/// `--tick-interval`(`__main__.py:29`)。`None` → 读 spec `runtime.tick_interval_sec`。
|
|
29
29
|
pub tick_interval_sec: Option<f64>,
|
|
30
|
+
/// 0.5.x Windows portability Batch 9 F8 (leader msg_2a4cc1fa54c0):
|
|
31
|
+
/// team_key passed via `--team` CLI so the coordinator daemon
|
|
32
|
+
/// doesn't have to derive from state at boot time. That derivation
|
|
33
|
+
/// (Batch 7 F5) got trapped by Batch 8's seed-state pattern
|
|
34
|
+
/// (F8 root cause). Preserving the derivation as a fallback when
|
|
35
|
+
/// `team_key` is None keeps Unix daemons and pre-existing test
|
|
36
|
+
/// harnesses byte-preserving.
|
|
37
|
+
pub team_key: Option<String>,
|
|
30
38
|
}
|
|
31
39
|
|
|
32
40
|
/// daemon 主循环(`main`,`__main__.py:25-98`)。写 pid/meta(source=boot)、装信号→STOP、孤儿自检、
|
|
@@ -36,19 +44,82 @@ pub fn run_daemon(args: DaemonArgs) -> Result<(), DaemonError> {
|
|
|
36
44
|
// CP-1: the daemon's whole tick surface (has_session / capture / inject / list_windows / kill)
|
|
37
45
|
// runs through this backend. Prefer the persisted runtime endpoint so attached explicit-socket
|
|
38
46
|
// teams are checked on the same socket as lifecycle worker operations.
|
|
47
|
+
//
|
|
48
|
+
// 0.5.x Phase 1d Batch 3: coordinator boot now routes through
|
|
49
|
+
// `transport_factory::resolve_transport` so `state.transport.kind =
|
|
50
|
+
// conpty` boots the ConPTY backend, not a tmux backend that would
|
|
51
|
+
// fake `has_session()`. Tmux behavior is byte-equivalent (Layer 3
|
|
52
|
+
// legacy `tmux_endpoint` → same `tmux_backend_for_runtime_state_or_workspace`
|
|
53
|
+
// shape inside the factory's `build_tmux`).
|
|
39
54
|
let state = crate::state::persist::load_runtime_state(args.workspace.as_path()).ok();
|
|
40
|
-
|
|
55
|
+
// 0.5.x Windows portability Batch 9 F8 (leader msg_2a4cc1fa54c0):
|
|
56
|
+
// team_key priority is now `args.team_key` (from `--team` CLI)
|
|
57
|
+
// > `state.active_team_key` fallback. The Batch 7 F5 derivation
|
|
58
|
+
// stays as the fallback so pre-Batch-9 tests + Unix daemons
|
|
59
|
+
// continue byte-preserving. Callers that CAN pass `--team`
|
|
60
|
+
// (Batch 9 quick-start on Windows) do — that avoids the F8
|
|
61
|
+
// seed-state trap where writing active_team_key to state ahead
|
|
62
|
+
// of coord spawn made downstream launch code think "existing
|
|
63
|
+
// runtime, use restart" and skip spec compile.
|
|
64
|
+
let team_key_from_state = args
|
|
65
|
+
.team_key
|
|
66
|
+
.clone()
|
|
67
|
+
.filter(|s| !s.is_empty())
|
|
68
|
+
.or_else(|| {
|
|
69
|
+
state
|
|
70
|
+
.as_ref()
|
|
71
|
+
.and_then(|s| s.get("active_team_key"))
|
|
72
|
+
.and_then(|v| v.as_str())
|
|
73
|
+
.filter(|s| !s.is_empty())
|
|
74
|
+
.map(str::to_string)
|
|
75
|
+
});
|
|
76
|
+
let mut factory_input = crate::transport_factory::TransportFactoryInput::new(
|
|
41
77
|
args.workspace.as_path(),
|
|
42
|
-
|
|
43
|
-
)
|
|
78
|
+
crate::transport_factory::TransportPurpose::Coordinator,
|
|
79
|
+
)
|
|
80
|
+
.with_state(state.as_ref());
|
|
81
|
+
if let Some(ref team_key) = team_key_from_state {
|
|
82
|
+
factory_input = factory_input.with_team_key(Some(team_key));
|
|
83
|
+
}
|
|
84
|
+
let resolved = match crate::transport_factory::resolve_transport(factory_input) {
|
|
85
|
+
Ok(resolved) => resolved,
|
|
86
|
+
Err(e) => {
|
|
87
|
+
// Fail-closed honest degradation: coordinator boots with a
|
|
88
|
+
// tmux-workspace backend so it doesn't crash, but records
|
|
89
|
+
// the assembly refusal in the boot metadata source so the
|
|
90
|
+
// event log shows the reason. This preserves the daemon
|
|
91
|
+
// liveness path while making the failure explicit.
|
|
92
|
+
eprintln!(
|
|
93
|
+
"coordinator: transport_factory refused ({e}); falling back to tmux workspace for daemon liveness"
|
|
94
|
+
);
|
|
95
|
+
let sel = crate::tmux_backend::tmux_backend_for_runtime_state_or_workspace(
|
|
96
|
+
args.workspace.as_path(),
|
|
97
|
+
state.as_ref(),
|
|
98
|
+
);
|
|
99
|
+
let metadata = DaemonTmuxEndpointMetadata {
|
|
100
|
+
tmux_endpoint_used: sel.tmux_endpoint_used.clone(),
|
|
101
|
+
tmux_endpoint_source: "factory_refused_fallback",
|
|
102
|
+
};
|
|
103
|
+
let coord = Coordinator::new(
|
|
104
|
+
args.workspace.clone(),
|
|
105
|
+
Box::new(RealProviderRegistry),
|
|
106
|
+
Box::new(sel.backend),
|
|
107
|
+
);
|
|
108
|
+
return run_daemon_with_coordinator_and_boot_tmux(&args, &coord, Some(metadata));
|
|
109
|
+
}
|
|
110
|
+
};
|
|
111
|
+
// Preserve tmux boot metadata byte-equivalent for tmux teams.
|
|
112
|
+
// ConPTY teams get their source string but no tmux endpoint (design
|
|
113
|
+
// §Behavior Equivalence: same `tmux_endpoint_used` for tmux; None
|
|
114
|
+
// + honest source for conpty).
|
|
44
115
|
let tmux_metadata = DaemonTmuxEndpointMetadata {
|
|
45
|
-
tmux_endpoint_used:
|
|
46
|
-
tmux_endpoint_source:
|
|
116
|
+
tmux_endpoint_used: resolved.tmux_endpoint_used.clone(),
|
|
117
|
+
tmux_endpoint_source: resolved.source,
|
|
47
118
|
};
|
|
48
119
|
let coordinator = Coordinator::new(
|
|
49
120
|
args.workspace.clone(),
|
|
50
121
|
Box::new(RealProviderRegistry),
|
|
51
|
-
|
|
122
|
+
resolved.backend,
|
|
52
123
|
);
|
|
53
124
|
run_daemon_with_coordinator_and_boot_tmux(&args, &coordinator, Some(tmux_metadata))
|
|
54
125
|
}
|
|
@@ -98,6 +169,57 @@ fn run_daemon_with_coordinator_and_boot_tmux(
|
|
|
98
169
|
}
|
|
99
170
|
}
|
|
100
171
|
event_log.write("coordinator.boot", boot_event)?;
|
|
172
|
+
|
|
173
|
+
// 0.5.x Windows portability Batch 8 F7 (leader msg_590b4dce0f68):
|
|
174
|
+
// coordinator takes ownership of shim lifecycle. Idempotent:
|
|
175
|
+
// spawns a fresh shim if none is recorded, reconnects if
|
|
176
|
+
// `state.transport.shim.pid` names a living shim (post-crash
|
|
177
|
+
// restart path). Failures are non-fatal for the coord daemon —
|
|
178
|
+
// the tick loop's transport calls will surface honest
|
|
179
|
+
// MuxUnavailable errors and `mark_transport_unavailable`
|
|
180
|
+
// emits the C-3 stale-family event.
|
|
181
|
+
//
|
|
182
|
+
// On non-Windows this call is cfg'd out (there's no shim
|
|
183
|
+
// concept on Unix).
|
|
184
|
+
#[cfg(windows)]
|
|
185
|
+
{
|
|
186
|
+
let state_for_team_key =
|
|
187
|
+
crate::state::persist::load_runtime_state(args.workspace.as_path()).ok();
|
|
188
|
+
let team_key_opt = state_for_team_key
|
|
189
|
+
.as_ref()
|
|
190
|
+
.and_then(|s| s.get("active_team_key"))
|
|
191
|
+
.and_then(|v| v.as_str())
|
|
192
|
+
.filter(|s| !s.is_empty())
|
|
193
|
+
.map(str::to_string);
|
|
194
|
+
if let Some(team_key) = team_key_opt {
|
|
195
|
+
let workspace_hash =
|
|
196
|
+
crate::tmux_backend::workspace_short_hash_pub(args.workspace.as_path());
|
|
197
|
+
match crate::coordinator::conpty_shim::ensure_shim_running(
|
|
198
|
+
args.workspace.as_path(),
|
|
199
|
+
&team_key,
|
|
200
|
+
&workspace_hash,
|
|
201
|
+
) {
|
|
202
|
+
Ok(handle) => {
|
|
203
|
+
// Detach so the shim survives beyond this
|
|
204
|
+
// coordinator instance (F7 core invariant).
|
|
205
|
+
let _shim_pid = handle.detach();
|
|
206
|
+
eprintln!("coordinator: shim ensured (pid={_shim_pid})");
|
|
207
|
+
}
|
|
208
|
+
Err(e) => {
|
|
209
|
+
// Honest degradation: emit the stale-family
|
|
210
|
+
// event, coord continues without a shim. Tick
|
|
211
|
+
// loop will surface `mux_unavailable` on any
|
|
212
|
+
// transport call.
|
|
213
|
+
eprintln!("coordinator: shim ensure failed: {e}");
|
|
214
|
+
let _ = crate::coordinator::conpty_shim::mark_transport_unavailable(
|
|
215
|
+
args.workspace.as_path(),
|
|
216
|
+
&format!("boot_ensure_failed: {e}"),
|
|
217
|
+
);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
101
223
|
let tick_interval = match args.tick_interval_sec {
|
|
102
224
|
Some(v) if v > 0.0 => v,
|
|
103
225
|
_ => resolve_tick_interval(&args.workspace)?,
|
|
@@ -215,8 +337,11 @@ fn panic_payload_message(payload: &(dyn std::any::Any + Send)) -> String {
|
|
|
215
337
|
}
|
|
216
338
|
|
|
217
339
|
/// 当前 ppid(`os.getppid()`,孤儿自检输入)。
|
|
340
|
+
///
|
|
341
|
+
/// 0.5.x Windows portability Batch 3: uses `platform::process::current_parent_pid`
|
|
342
|
+
/// so Windows sees a real Toolhelp32-derived ppid instead of `0`.
|
|
218
343
|
fn current_ppid() -> u32 {
|
|
219
|
-
|
|
344
|
+
crate::platform::process::current_parent_pid().unwrap_or(0)
|
|
220
345
|
}
|
|
221
346
|
|
|
222
347
|
/// 计算 tick 间隔(`_tick_interval`,`__main__.py:104-115`)。读 spec `runtime.tick_interval_sec`,
|