@team-agent/installer 0.5.1 → 0.5.2
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 +118 -9
- package/Cargo.toml +2 -2
- package/crates/team-agent/Cargo.toml +1 -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 +74 -1
- package/crates/team-agent/src/cli/mod.rs +70 -25
- 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 +131 -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 +488 -0
- package/crates/team-agent/src/conpty/backend.rs +710 -0
- package/crates/team-agent/src/conpty/mod.rs +32 -0
- package/crates/team-agent/src/coordinator/backoff.rs +45 -6
- package/crates/team-agent/src/diagnose/orphans.rs +11 -3
- 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/start.rs +18 -5
- package/crates/team-agent/src/leader/tests/lease_api.rs +172 -0
- package/crates/team-agent/src/lib.rs +23 -0
- package/crates/team-agent/src/lifecycle/launch.rs +127 -3
- package/crates/team-agent/src/lifecycle/restart/common.rs +70 -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/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 +232 -0
- package/crates/team-agent/src/tmux_backend.rs +31 -0
- package/crates/team-agent/src/transport_factory.rs +632 -0
- package/package.json +4 -4
package/Cargo.lock
CHANGED
|
@@ -91,7 +91,15 @@ dependencies = [
|
|
|
91
91
|
"js-sys",
|
|
92
92
|
"num-traits",
|
|
93
93
|
"wasm-bindgen",
|
|
94
|
-
"windows-link",
|
|
94
|
+
"windows-link 0.2.1",
|
|
95
|
+
]
|
|
96
|
+
|
|
97
|
+
[[package]]
|
|
98
|
+
name = "conpty-transport"
|
|
99
|
+
version = "0.1.0"
|
|
100
|
+
dependencies = [
|
|
101
|
+
"serde",
|
|
102
|
+
"serde_json",
|
|
95
103
|
]
|
|
96
104
|
|
|
97
105
|
[[package]]
|
|
@@ -244,7 +252,7 @@ dependencies = [
|
|
|
244
252
|
"js-sys",
|
|
245
253
|
"log",
|
|
246
254
|
"wasm-bindgen",
|
|
247
|
-
"windows-core",
|
|
255
|
+
"windows-core 0.62.2",
|
|
248
256
|
]
|
|
249
257
|
|
|
250
258
|
[[package]]
|
|
@@ -357,7 +365,7 @@ dependencies = [
|
|
|
357
365
|
"libc",
|
|
358
366
|
"redox_syscall",
|
|
359
367
|
"smallvec",
|
|
360
|
-
"windows-link",
|
|
368
|
+
"windows-link 0.2.1",
|
|
361
369
|
]
|
|
362
370
|
|
|
363
371
|
[[package]]
|
|
@@ -566,10 +574,11 @@ dependencies = [
|
|
|
566
574
|
|
|
567
575
|
[[package]]
|
|
568
576
|
name = "team-agent"
|
|
569
|
-
version = "0.5.
|
|
577
|
+
version = "0.5.2"
|
|
570
578
|
dependencies = [
|
|
571
579
|
"anyhow",
|
|
572
580
|
"chrono",
|
|
581
|
+
"conpty-transport",
|
|
573
582
|
"libc",
|
|
574
583
|
"regex",
|
|
575
584
|
"rusqlite",
|
|
@@ -669,6 +678,17 @@ dependencies = [
|
|
|
669
678
|
"unicode-ident",
|
|
670
679
|
]
|
|
671
680
|
|
|
681
|
+
[[package]]
|
|
682
|
+
name = "win-conpty-phase0"
|
|
683
|
+
version = "0.1.0"
|
|
684
|
+
dependencies = [
|
|
685
|
+
"anyhow",
|
|
686
|
+
"conpty-transport",
|
|
687
|
+
"serde",
|
|
688
|
+
"serde_json",
|
|
689
|
+
"windows",
|
|
690
|
+
]
|
|
691
|
+
|
|
672
692
|
[[package]]
|
|
673
693
|
name = "winapi"
|
|
674
694
|
version = "0.3.9"
|
|
@@ -691,6 +711,41 @@ version = "0.4.0"
|
|
|
691
711
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
692
712
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
693
713
|
|
|
714
|
+
[[package]]
|
|
715
|
+
name = "windows"
|
|
716
|
+
version = "0.61.3"
|
|
717
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
718
|
+
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
|
719
|
+
dependencies = [
|
|
720
|
+
"windows-collections",
|
|
721
|
+
"windows-core 0.61.2",
|
|
722
|
+
"windows-future",
|
|
723
|
+
"windows-link 0.1.3",
|
|
724
|
+
"windows-numerics",
|
|
725
|
+
]
|
|
726
|
+
|
|
727
|
+
[[package]]
|
|
728
|
+
name = "windows-collections"
|
|
729
|
+
version = "0.2.0"
|
|
730
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
731
|
+
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
|
732
|
+
dependencies = [
|
|
733
|
+
"windows-core 0.61.2",
|
|
734
|
+
]
|
|
735
|
+
|
|
736
|
+
[[package]]
|
|
737
|
+
name = "windows-core"
|
|
738
|
+
version = "0.61.2"
|
|
739
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
740
|
+
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
|
741
|
+
dependencies = [
|
|
742
|
+
"windows-implement",
|
|
743
|
+
"windows-interface",
|
|
744
|
+
"windows-link 0.1.3",
|
|
745
|
+
"windows-result 0.3.4",
|
|
746
|
+
"windows-strings 0.4.2",
|
|
747
|
+
]
|
|
748
|
+
|
|
694
749
|
[[package]]
|
|
695
750
|
name = "windows-core"
|
|
696
751
|
version = "0.62.2"
|
|
@@ -699,9 +754,20 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
|
|
699
754
|
dependencies = [
|
|
700
755
|
"windows-implement",
|
|
701
756
|
"windows-interface",
|
|
702
|
-
"windows-link",
|
|
703
|
-
"windows-result",
|
|
704
|
-
"windows-strings",
|
|
757
|
+
"windows-link 0.2.1",
|
|
758
|
+
"windows-result 0.4.1",
|
|
759
|
+
"windows-strings 0.5.1",
|
|
760
|
+
]
|
|
761
|
+
|
|
762
|
+
[[package]]
|
|
763
|
+
name = "windows-future"
|
|
764
|
+
version = "0.2.1"
|
|
765
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
766
|
+
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
|
767
|
+
dependencies = [
|
|
768
|
+
"windows-core 0.61.2",
|
|
769
|
+
"windows-link 0.1.3",
|
|
770
|
+
"windows-threading",
|
|
705
771
|
]
|
|
706
772
|
|
|
707
773
|
[[package]]
|
|
@@ -726,19 +792,53 @@ dependencies = [
|
|
|
726
792
|
"syn",
|
|
727
793
|
]
|
|
728
794
|
|
|
795
|
+
[[package]]
|
|
796
|
+
name = "windows-link"
|
|
797
|
+
version = "0.1.3"
|
|
798
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
799
|
+
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
|
800
|
+
|
|
729
801
|
[[package]]
|
|
730
802
|
name = "windows-link"
|
|
731
803
|
version = "0.2.1"
|
|
732
804
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
733
805
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
734
806
|
|
|
807
|
+
[[package]]
|
|
808
|
+
name = "windows-numerics"
|
|
809
|
+
version = "0.2.0"
|
|
810
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
811
|
+
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
|
812
|
+
dependencies = [
|
|
813
|
+
"windows-core 0.61.2",
|
|
814
|
+
"windows-link 0.1.3",
|
|
815
|
+
]
|
|
816
|
+
|
|
817
|
+
[[package]]
|
|
818
|
+
name = "windows-result"
|
|
819
|
+
version = "0.3.4"
|
|
820
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
821
|
+
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
|
822
|
+
dependencies = [
|
|
823
|
+
"windows-link 0.1.3",
|
|
824
|
+
]
|
|
825
|
+
|
|
735
826
|
[[package]]
|
|
736
827
|
name = "windows-result"
|
|
737
828
|
version = "0.4.1"
|
|
738
829
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
739
830
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
|
740
831
|
dependencies = [
|
|
741
|
-
"windows-link",
|
|
832
|
+
"windows-link 0.2.1",
|
|
833
|
+
]
|
|
834
|
+
|
|
835
|
+
[[package]]
|
|
836
|
+
name = "windows-strings"
|
|
837
|
+
version = "0.4.2"
|
|
838
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
839
|
+
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
|
840
|
+
dependencies = [
|
|
841
|
+
"windows-link 0.1.3",
|
|
742
842
|
]
|
|
743
843
|
|
|
744
844
|
[[package]]
|
|
@@ -747,7 +847,16 @@ version = "0.5.1"
|
|
|
747
847
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
748
848
|
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
|
749
849
|
dependencies = [
|
|
750
|
-
"windows-link",
|
|
850
|
+
"windows-link 0.2.1",
|
|
851
|
+
]
|
|
852
|
+
|
|
853
|
+
[[package]]
|
|
854
|
+
name = "windows-threading"
|
|
855
|
+
version = "0.1.0"
|
|
856
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
857
|
+
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
|
858
|
+
dependencies = [
|
|
859
|
+
"windows-link 0.1.3",
|
|
751
860
|
]
|
|
752
861
|
|
|
753
862
|
[[package]]
|
package/Cargo.toml
CHANGED
|
@@ -5,11 +5,11 @@
|
|
|
5
5
|
# 等抽成独立成员 crate(届时各自顶上 §10 的 deny 锁 + cargo-deny ban)。
|
|
6
6
|
[workspace]
|
|
7
7
|
resolver = "2"
|
|
8
|
-
members = ["crates/team-agent"]
|
|
8
|
+
members = ["crates/team-agent", "crates/win-conpty-phase0", "crates/conpty-transport"]
|
|
9
9
|
|
|
10
10
|
[workspace.package]
|
|
11
11
|
edition = "2021"
|
|
12
|
-
version = "0.5.
|
|
12
|
+
version = "0.5.2"
|
|
13
13
|
license = "AGPL-3.0"
|
|
14
14
|
rust-version = "1.95"
|
|
15
15
|
|
|
@@ -16,6 +16,7 @@ path = "src/main.rs"
|
|
|
16
16
|
# step 2 (model) 已落地: serde/serde_json/thiserror/sha2。
|
|
17
17
|
# rusqlite 仍留 workspace 调色板,step 3 (db) 再加(且需联网先 fetch)。
|
|
18
18
|
[dependencies]
|
|
19
|
+
conpty-transport = { path = "../conpty-transport" }
|
|
19
20
|
anyhow.workspace = true
|
|
20
21
|
thiserror.workspace = true
|
|
21
22
|
serde.workspace = true
|
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
use serde_json::{json, Value};
|
|
2
|
+
use std::io::{Read, Write};
|
|
3
|
+
use std::os::unix::net::{UnixListener, UnixStream};
|
|
4
|
+
use std::path::PathBuf;
|
|
5
|
+
use std::sync::{
|
|
6
|
+
atomic::{AtomicBool, Ordering},
|
|
7
|
+
Arc, Mutex,
|
|
8
|
+
};
|
|
9
|
+
use std::thread::JoinHandle;
|
|
10
|
+
use std::time::Duration;
|
|
11
|
+
|
|
12
|
+
#[derive(Clone)]
|
|
13
|
+
pub(crate) struct FakeAppServerScript {
|
|
14
|
+
pub user_agent: Option<String>,
|
|
15
|
+
pub thread_id: String,
|
|
16
|
+
pub session_id: String,
|
|
17
|
+
pub cwd: String,
|
|
18
|
+
pub resume_error: Option<String>,
|
|
19
|
+
pub turn_error: Option<String>,
|
|
20
|
+
pub turn_status: String,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
impl FakeAppServerScript {
|
|
24
|
+
pub(crate) fn happy(thread_id: &str, session_id: &str, cwd: &str) -> Self {
|
|
25
|
+
Self {
|
|
26
|
+
user_agent: Some("codex-appserver-team-agent-test/0.139.0".to_string()),
|
|
27
|
+
thread_id: thread_id.to_string(),
|
|
28
|
+
session_id: session_id.to_string(),
|
|
29
|
+
cwd: cwd.to_string(),
|
|
30
|
+
resume_error: None,
|
|
31
|
+
turn_error: None,
|
|
32
|
+
turn_status: "inProgress".to_string(),
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
pub(crate) struct FakeAppServer {
|
|
38
|
+
endpoint: String,
|
|
39
|
+
path: PathBuf,
|
|
40
|
+
received_turns: Arc<Mutex<Vec<Value>>>,
|
|
41
|
+
running: Arc<AtomicBool>,
|
|
42
|
+
handle: Option<JoinHandle<()>>,
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
impl FakeAppServer {
|
|
46
|
+
pub(crate) fn start(tag: &str, script: FakeAppServerScript) -> Self {
|
|
47
|
+
static NEXT_ID: std::sync::atomic::AtomicU64 = std::sync::atomic::AtomicU64::new(0);
|
|
48
|
+
let id = NEXT_ID.fetch_add(1, Ordering::Relaxed);
|
|
49
|
+
let path = PathBuf::from(format!("/tmp/taas-{}-{id}-{tag}.sock", std::process::id()));
|
|
50
|
+
let _ = std::fs::remove_file(&path);
|
|
51
|
+
let listener = UnixListener::bind(&path).unwrap();
|
|
52
|
+
listener.set_nonblocking(true).unwrap();
|
|
53
|
+
let received_turns = Arc::new(Mutex::new(Vec::new()));
|
|
54
|
+
let running = Arc::new(AtomicBool::new(true));
|
|
55
|
+
let thread_turns = Arc::clone(&received_turns);
|
|
56
|
+
let thread_running = Arc::clone(&running);
|
|
57
|
+
let thread_path = path.clone();
|
|
58
|
+
let handle = std::thread::spawn(move || {
|
|
59
|
+
while thread_running.load(Ordering::Relaxed) {
|
|
60
|
+
match listener.accept() {
|
|
61
|
+
Ok((mut stream, _)) => {
|
|
62
|
+
let _ = handle_connection(&mut stream, &script, &thread_turns);
|
|
63
|
+
}
|
|
64
|
+
Err(err) if err.kind() == std::io::ErrorKind::WouldBlock => {
|
|
65
|
+
std::thread::sleep(Duration::from_millis(10));
|
|
66
|
+
}
|
|
67
|
+
Err(_) => break,
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
let _ = std::fs::remove_file(thread_path);
|
|
71
|
+
});
|
|
72
|
+
Self {
|
|
73
|
+
endpoint: format!("unix://{}", path.display()),
|
|
74
|
+
path,
|
|
75
|
+
received_turns,
|
|
76
|
+
running,
|
|
77
|
+
handle: Some(handle),
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
pub(crate) fn endpoint(&self) -> &str {
|
|
82
|
+
&self.endpoint
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
pub(crate) fn path(&self) -> &PathBuf {
|
|
86
|
+
&self.path
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
pub(crate) fn received_turns(&self) -> Vec<Value> {
|
|
90
|
+
self.received_turns.lock().unwrap().clone()
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
impl Drop for FakeAppServer {
|
|
95
|
+
fn drop(&mut self) {
|
|
96
|
+
self.running.store(false, Ordering::Relaxed);
|
|
97
|
+
let _ = UnixStream::connect(&self.path);
|
|
98
|
+
if let Some(handle) = self.handle.take() {
|
|
99
|
+
let _ = handle.join();
|
|
100
|
+
}
|
|
101
|
+
let _ = std::fs::remove_file(&self.path);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
fn handle_connection(
|
|
106
|
+
stream: &mut UnixStream,
|
|
107
|
+
script: &FakeAppServerScript,
|
|
108
|
+
turns: &Arc<Mutex<Vec<Value>>>,
|
|
109
|
+
) -> std::io::Result<()> {
|
|
110
|
+
stream.set_nonblocking(false)?;
|
|
111
|
+
stream.set_read_timeout(Some(Duration::from_secs(3)))?;
|
|
112
|
+
stream.set_write_timeout(Some(Duration::from_secs(3)))?;
|
|
113
|
+
read_http_upgrade(stream)?;
|
|
114
|
+
stream.write_all(
|
|
115
|
+
b"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: test\r\n\r\n",
|
|
116
|
+
)?;
|
|
117
|
+
while let Some(frame) = read_ws_text(stream)? {
|
|
118
|
+
let value: Value = match serde_json::from_str(&frame) {
|
|
119
|
+
Ok(value) => value,
|
|
120
|
+
Err(_) => continue,
|
|
121
|
+
};
|
|
122
|
+
let method = value.get("method").and_then(Value::as_str).unwrap_or("");
|
|
123
|
+
let id = value.get("id").cloned().unwrap_or(Value::Null);
|
|
124
|
+
match method {
|
|
125
|
+
"initialize" => {
|
|
126
|
+
let mut result = serde_json::Map::new();
|
|
127
|
+
result.insert("codexHome".to_string(), json!("/tmp/codex-home"));
|
|
128
|
+
result.insert("platformFamily".to_string(), json!("unix"));
|
|
129
|
+
result.insert("platformOs".to_string(), json!("macos"));
|
|
130
|
+
if let Some(user_agent) = &script.user_agent {
|
|
131
|
+
result.insert("userAgent".to_string(), json!(user_agent));
|
|
132
|
+
}
|
|
133
|
+
write_ws_text(
|
|
134
|
+
stream,
|
|
135
|
+
&json!({"id": id, "result": Value::Object(result)}).to_string(),
|
|
136
|
+
)?;
|
|
137
|
+
}
|
|
138
|
+
"initialized" => {}
|
|
139
|
+
"thread/resume" => {
|
|
140
|
+
if let Some(message) = &script.resume_error {
|
|
141
|
+
write_ws_text(
|
|
142
|
+
stream,
|
|
143
|
+
&json!({"id": id, "error": {"code": -32600, "message": message}})
|
|
144
|
+
.to_string(),
|
|
145
|
+
)?;
|
|
146
|
+
} else {
|
|
147
|
+
write_ws_text(
|
|
148
|
+
stream,
|
|
149
|
+
&json!({
|
|
150
|
+
"id": id,
|
|
151
|
+
"result": {
|
|
152
|
+
"cwd": script.cwd,
|
|
153
|
+
"thread": {
|
|
154
|
+
"id": script.thread_id,
|
|
155
|
+
"sessionId": script.session_id,
|
|
156
|
+
"cwd": script.cwd,
|
|
157
|
+
"ephemeral": false
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
})
|
|
161
|
+
.to_string(),
|
|
162
|
+
)?;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
"turn/start" => {
|
|
166
|
+
turns.lock().unwrap().push(value.clone());
|
|
167
|
+
if let Some(message) = &script.turn_error {
|
|
168
|
+
write_ws_text(
|
|
169
|
+
stream,
|
|
170
|
+
&json!({"id": id, "error": {"code": -32600, "message": message}})
|
|
171
|
+
.to_string(),
|
|
172
|
+
)?;
|
|
173
|
+
} else {
|
|
174
|
+
write_ws_text(
|
|
175
|
+
stream,
|
|
176
|
+
&json!({
|
|
177
|
+
"id": id,
|
|
178
|
+
"result": {
|
|
179
|
+
"turn": {"id": "turn-test", "status": script.turn_status}
|
|
180
|
+
}
|
|
181
|
+
})
|
|
182
|
+
.to_string(),
|
|
183
|
+
)?;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
_ => {}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
Ok(())
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
fn read_http_upgrade(stream: &mut UnixStream) -> std::io::Result<()> {
|
|
193
|
+
let mut data = Vec::new();
|
|
194
|
+
let mut buf = [0u8; 1];
|
|
195
|
+
while !data.ends_with(b"\r\n\r\n") {
|
|
196
|
+
stream.read_exact(&mut buf)?;
|
|
197
|
+
data.push(buf[0]);
|
|
198
|
+
}
|
|
199
|
+
Ok(())
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
fn read_ws_text(stream: &mut UnixStream) -> std::io::Result<Option<String>> {
|
|
203
|
+
let mut header = [0u8; 2];
|
|
204
|
+
match stream.read_exact(&mut header) {
|
|
205
|
+
Ok(()) => {}
|
|
206
|
+
Err(err)
|
|
207
|
+
if matches!(
|
|
208
|
+
err.kind(),
|
|
209
|
+
std::io::ErrorKind::UnexpectedEof
|
|
210
|
+
| std::io::ErrorKind::WouldBlock
|
|
211
|
+
| std::io::ErrorKind::TimedOut
|
|
212
|
+
) =>
|
|
213
|
+
{
|
|
214
|
+
return Ok(None);
|
|
215
|
+
}
|
|
216
|
+
Err(err) => return Err(err),
|
|
217
|
+
}
|
|
218
|
+
let opcode = header[0] & 0x0f;
|
|
219
|
+
if opcode == 0x8 {
|
|
220
|
+
return Ok(None);
|
|
221
|
+
}
|
|
222
|
+
let masked = header[1] & 0x80 != 0;
|
|
223
|
+
let mut len = u64::from(header[1] & 0x7f);
|
|
224
|
+
if len == 126 {
|
|
225
|
+
let mut ext = [0u8; 2];
|
|
226
|
+
stream.read_exact(&mut ext)?;
|
|
227
|
+
len = u64::from(u16::from_be_bytes(ext));
|
|
228
|
+
} else if len == 127 {
|
|
229
|
+
let mut ext = [0u8; 8];
|
|
230
|
+
stream.read_exact(&mut ext)?;
|
|
231
|
+
len = u64::from_be_bytes(ext);
|
|
232
|
+
}
|
|
233
|
+
let mut mask = [0u8; 4];
|
|
234
|
+
if masked {
|
|
235
|
+
stream.read_exact(&mut mask)?;
|
|
236
|
+
}
|
|
237
|
+
let mut payload = vec![0u8; usize::try_from(len).unwrap_or(0)];
|
|
238
|
+
stream.read_exact(&mut payload)?;
|
|
239
|
+
if masked {
|
|
240
|
+
for (idx, byte) in payload.iter_mut().enumerate() {
|
|
241
|
+
*byte ^= mask[idx % 4];
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
Ok(Some(String::from_utf8_lossy(&payload).to_string()))
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
fn write_ws_text(stream: &mut UnixStream, text: &str) -> std::io::Result<()> {
|
|
248
|
+
let payload = text.as_bytes();
|
|
249
|
+
let mut frame = vec![0x81];
|
|
250
|
+
if payload.len() < 126 {
|
|
251
|
+
frame.push(payload.len() as u8);
|
|
252
|
+
} else {
|
|
253
|
+
frame.push(126);
|
|
254
|
+
frame.extend_from_slice(&(payload.len() as u16).to_be_bytes());
|
|
255
|
+
}
|
|
256
|
+
frame.extend_from_slice(payload);
|
|
257
|
+
stream.write_all(&frame)
|
|
258
|
+
}
|
|
@@ -107,6 +107,7 @@ pub fn cmd_quick_start(args: &QuickStartArgs) -> Result<CmdResult, CliError> {
|
|
|
107
107
|
args.team_id.as_deref(),
|
|
108
108
|
args.yes,
|
|
109
109
|
!args.no_display,
|
|
110
|
+
args.backend.as_deref(),
|
|
110
111
|
)?;
|
|
111
112
|
let readiness = value.get("readiness").and_then(Value::as_object);
|
|
112
113
|
let all_resumable_have_session = readiness
|
|
@@ -716,8 +717,22 @@ pub fn cmd_diagnose(args: &DiagnoseArgs) -> Result<CmdResult, CliError> {
|
|
|
716
717
|
.map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
717
718
|
let state = selected.state;
|
|
718
719
|
let event_log = selected.run_workspace.join(".team").join("logs").join("events.jsonl");
|
|
719
|
-
|
|
720
|
-
|
|
720
|
+
// 0.5.x Phase 1d Batch 3: factory-routed diagnose backend so
|
|
721
|
+
// conpty teams get accurate `has_session` / capture from the shim
|
|
722
|
+
// rather than a workspace tmux fallback (which would always miss
|
|
723
|
+
// the ConPTY pane universe).
|
|
724
|
+
let backend: Box<dyn crate::transport::Transport> =
|
|
725
|
+
match crate::transport_factory::resolve_read_only_transport(
|
|
726
|
+
&selected.run_workspace,
|
|
727
|
+
Some(&state),
|
|
728
|
+
crate::transport_factory::TransportPurpose::Diagnose,
|
|
729
|
+
) {
|
|
730
|
+
Ok(r) => r.backend,
|
|
731
|
+
Err(_) => Box::new(crate::tmux_backend::TmuxBackend::for_workspace(
|
|
732
|
+
&selected.run_workspace,
|
|
733
|
+
)),
|
|
734
|
+
};
|
|
735
|
+
let (issues, suggested_repairs) = diagnose_runtime(&state, backend.as_ref());
|
|
721
736
|
let ok = issues.as_array().is_some_and(Vec::is_empty);
|
|
722
737
|
Ok(CmdResult::from_json(
|
|
723
738
|
json!({
|
|
@@ -801,7 +816,21 @@ pub fn cmd_peek(args: &PeekArgs) -> Result<CmdResult, CliError> {
|
|
|
801
816
|
let Some((session, window, target)) = agent_pane_id(&state, &args.agent, agent_state) else {
|
|
802
817
|
return Ok(peek_unavailable(&args.agent, args.json));
|
|
803
818
|
};
|
|
804
|
-
|
|
819
|
+
// 0.5.x Phase 1d Batch 3: factory-routed peek backend so conpty
|
|
820
|
+
// teams peek from the shim scrollback rather than a workspace
|
|
821
|
+
// tmux capture that would always be empty. Tmux teams keep
|
|
822
|
+
// byte-equivalent behavior.
|
|
823
|
+
let backend: Box<dyn crate::transport::Transport> =
|
|
824
|
+
match crate::transport_factory::resolve_read_only_transport(
|
|
825
|
+
&args.workspace,
|
|
826
|
+
Some(&state),
|
|
827
|
+
crate::transport_factory::TransportPurpose::Status,
|
|
828
|
+
) {
|
|
829
|
+
Ok(r) => r.backend,
|
|
830
|
+
Err(_) => Box::new(crate::tmux_backend::TmuxBackend::for_workspace(
|
|
831
|
+
&args.workspace,
|
|
832
|
+
)),
|
|
833
|
+
};
|
|
805
834
|
let windows = backend
|
|
806
835
|
.list_windows(&crate::transport::SessionName::new(session.clone()))
|
|
807
836
|
.map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
use super::*;
|
|
2
|
+
|
|
3
|
+
/// `team-agent attach-app-server-leader` writes a typed codex_app_server
|
|
4
|
+
/// leader_receiver through the leader lease primitive.
|
|
5
|
+
pub fn cmd_attach_app_server_leader(
|
|
6
|
+
args: &AttachAppServerLeaderArgs,
|
|
7
|
+
) -> Result<CmdResult, CliError> {
|
|
8
|
+
let value = crate::leader::attach_app_server_leader(
|
|
9
|
+
&args.workspace,
|
|
10
|
+
args.team.as_deref(),
|
|
11
|
+
&args.socket,
|
|
12
|
+
&args.thread_id,
|
|
13
|
+
)
|
|
14
|
+
.map_err(|error| CliError::Runtime(error.to_string()))?;
|
|
15
|
+
Ok(CmdResult::from_json(value, args.json))
|
|
16
|
+
}
|
|
@@ -483,14 +483,23 @@ fn inject_tmux_session_present(workspace: &std::path::Path, state: &mut Value) {
|
|
|
483
483
|
return;
|
|
484
484
|
};
|
|
485
485
|
let session_name_owned = session_name.to_string();
|
|
486
|
-
|
|
486
|
+
// 0.5.x Phase 1d Batch 3: factory-routed read path. conpty state
|
|
487
|
+
// hits the shim; tmux state hits the persisted endpoint (byte-
|
|
488
|
+
// equivalent). Diagnose no longer reports `tmux_session_missing`
|
|
489
|
+
// for a conpty team just because it can't find a tmux session on
|
|
490
|
+
// the workspace socket (design §Batch 3 Verification anchor).
|
|
491
|
+
let resolved = crate::transport_factory::resolve_read_only_transport(
|
|
487
492
|
workspace,
|
|
488
493
|
Some(state),
|
|
494
|
+
crate::transport_factory::TransportPurpose::Diagnose,
|
|
489
495
|
);
|
|
490
|
-
let present =
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
496
|
+
let present = match resolved {
|
|
497
|
+
Ok(r) => r
|
|
498
|
+
.backend
|
|
499
|
+
.has_session(&crate::transport::SessionName::new(session_name_owned))
|
|
500
|
+
.unwrap_or(false),
|
|
501
|
+
Err(_) => false,
|
|
502
|
+
};
|
|
494
503
|
if let Value::Object(map) = state {
|
|
495
504
|
map.insert("tmux_session_present".to_string(), Value::Bool(present));
|
|
496
505
|
}
|