@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
package/Cargo.lock
CHANGED
|
@@ -91,7 +91,16 @@ 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",
|
|
103
|
+
"windows",
|
|
95
104
|
]
|
|
96
105
|
|
|
97
106
|
[[package]]
|
|
@@ -244,7 +253,7 @@ dependencies = [
|
|
|
244
253
|
"js-sys",
|
|
245
254
|
"log",
|
|
246
255
|
"wasm-bindgen",
|
|
247
|
-
"windows-core",
|
|
256
|
+
"windows-core 0.62.2",
|
|
248
257
|
]
|
|
249
258
|
|
|
250
259
|
[[package]]
|
|
@@ -357,7 +366,7 @@ dependencies = [
|
|
|
357
366
|
"libc",
|
|
358
367
|
"redox_syscall",
|
|
359
368
|
"smallvec",
|
|
360
|
-
"windows-link",
|
|
369
|
+
"windows-link 0.2.1",
|
|
361
370
|
]
|
|
362
371
|
|
|
363
372
|
[[package]]
|
|
@@ -566,10 +575,11 @@ dependencies = [
|
|
|
566
575
|
|
|
567
576
|
[[package]]
|
|
568
577
|
name = "team-agent"
|
|
569
|
-
version = "0.5.
|
|
578
|
+
version = "0.5.3"
|
|
570
579
|
dependencies = [
|
|
571
580
|
"anyhow",
|
|
572
581
|
"chrono",
|
|
582
|
+
"conpty-transport",
|
|
573
583
|
"libc",
|
|
574
584
|
"regex",
|
|
575
585
|
"rusqlite",
|
|
@@ -578,6 +588,7 @@ dependencies = [
|
|
|
578
588
|
"serial_test",
|
|
579
589
|
"sha2",
|
|
580
590
|
"thiserror",
|
|
591
|
+
"windows",
|
|
581
592
|
]
|
|
582
593
|
|
|
583
594
|
[[package]]
|
|
@@ -669,6 +680,17 @@ dependencies = [
|
|
|
669
680
|
"unicode-ident",
|
|
670
681
|
]
|
|
671
682
|
|
|
683
|
+
[[package]]
|
|
684
|
+
name = "win-conpty-phase0"
|
|
685
|
+
version = "0.1.0"
|
|
686
|
+
dependencies = [
|
|
687
|
+
"anyhow",
|
|
688
|
+
"conpty-transport",
|
|
689
|
+
"serde",
|
|
690
|
+
"serde_json",
|
|
691
|
+
"windows",
|
|
692
|
+
]
|
|
693
|
+
|
|
672
694
|
[[package]]
|
|
673
695
|
name = "winapi"
|
|
674
696
|
version = "0.3.9"
|
|
@@ -691,6 +713,41 @@ version = "0.4.0"
|
|
|
691
713
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
692
714
|
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
|
693
715
|
|
|
716
|
+
[[package]]
|
|
717
|
+
name = "windows"
|
|
718
|
+
version = "0.61.3"
|
|
719
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
720
|
+
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
|
|
721
|
+
dependencies = [
|
|
722
|
+
"windows-collections",
|
|
723
|
+
"windows-core 0.61.2",
|
|
724
|
+
"windows-future",
|
|
725
|
+
"windows-link 0.1.3",
|
|
726
|
+
"windows-numerics",
|
|
727
|
+
]
|
|
728
|
+
|
|
729
|
+
[[package]]
|
|
730
|
+
name = "windows-collections"
|
|
731
|
+
version = "0.2.0"
|
|
732
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
733
|
+
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
|
|
734
|
+
dependencies = [
|
|
735
|
+
"windows-core 0.61.2",
|
|
736
|
+
]
|
|
737
|
+
|
|
738
|
+
[[package]]
|
|
739
|
+
name = "windows-core"
|
|
740
|
+
version = "0.61.2"
|
|
741
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
742
|
+
checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3"
|
|
743
|
+
dependencies = [
|
|
744
|
+
"windows-implement",
|
|
745
|
+
"windows-interface",
|
|
746
|
+
"windows-link 0.1.3",
|
|
747
|
+
"windows-result 0.3.4",
|
|
748
|
+
"windows-strings 0.4.2",
|
|
749
|
+
]
|
|
750
|
+
|
|
694
751
|
[[package]]
|
|
695
752
|
name = "windows-core"
|
|
696
753
|
version = "0.62.2"
|
|
@@ -699,9 +756,20 @@ checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
|
|
|
699
756
|
dependencies = [
|
|
700
757
|
"windows-implement",
|
|
701
758
|
"windows-interface",
|
|
702
|
-
"windows-link",
|
|
703
|
-
"windows-result",
|
|
704
|
-
"windows-strings",
|
|
759
|
+
"windows-link 0.2.1",
|
|
760
|
+
"windows-result 0.4.1",
|
|
761
|
+
"windows-strings 0.5.1",
|
|
762
|
+
]
|
|
763
|
+
|
|
764
|
+
[[package]]
|
|
765
|
+
name = "windows-future"
|
|
766
|
+
version = "0.2.1"
|
|
767
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
768
|
+
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
|
|
769
|
+
dependencies = [
|
|
770
|
+
"windows-core 0.61.2",
|
|
771
|
+
"windows-link 0.1.3",
|
|
772
|
+
"windows-threading",
|
|
705
773
|
]
|
|
706
774
|
|
|
707
775
|
[[package]]
|
|
@@ -726,19 +794,53 @@ dependencies = [
|
|
|
726
794
|
"syn",
|
|
727
795
|
]
|
|
728
796
|
|
|
797
|
+
[[package]]
|
|
798
|
+
name = "windows-link"
|
|
799
|
+
version = "0.1.3"
|
|
800
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
801
|
+
checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a"
|
|
802
|
+
|
|
729
803
|
[[package]]
|
|
730
804
|
name = "windows-link"
|
|
731
805
|
version = "0.2.1"
|
|
732
806
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
733
807
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
|
734
808
|
|
|
809
|
+
[[package]]
|
|
810
|
+
name = "windows-numerics"
|
|
811
|
+
version = "0.2.0"
|
|
812
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
813
|
+
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
|
|
814
|
+
dependencies = [
|
|
815
|
+
"windows-core 0.61.2",
|
|
816
|
+
"windows-link 0.1.3",
|
|
817
|
+
]
|
|
818
|
+
|
|
819
|
+
[[package]]
|
|
820
|
+
name = "windows-result"
|
|
821
|
+
version = "0.3.4"
|
|
822
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
823
|
+
checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6"
|
|
824
|
+
dependencies = [
|
|
825
|
+
"windows-link 0.1.3",
|
|
826
|
+
]
|
|
827
|
+
|
|
735
828
|
[[package]]
|
|
736
829
|
name = "windows-result"
|
|
737
830
|
version = "0.4.1"
|
|
738
831
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
739
832
|
checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5"
|
|
740
833
|
dependencies = [
|
|
741
|
-
"windows-link",
|
|
834
|
+
"windows-link 0.2.1",
|
|
835
|
+
]
|
|
836
|
+
|
|
837
|
+
[[package]]
|
|
838
|
+
name = "windows-strings"
|
|
839
|
+
version = "0.4.2"
|
|
840
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
841
|
+
checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57"
|
|
842
|
+
dependencies = [
|
|
843
|
+
"windows-link 0.1.3",
|
|
742
844
|
]
|
|
743
845
|
|
|
744
846
|
[[package]]
|
|
@@ -747,7 +849,16 @@ version = "0.5.1"
|
|
|
747
849
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
748
850
|
checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091"
|
|
749
851
|
dependencies = [
|
|
750
|
-
"windows-link",
|
|
852
|
+
"windows-link 0.2.1",
|
|
853
|
+
]
|
|
854
|
+
|
|
855
|
+
[[package]]
|
|
856
|
+
name = "windows-threading"
|
|
857
|
+
version = "0.1.0"
|
|
858
|
+
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
859
|
+
checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6"
|
|
860
|
+
dependencies = [
|
|
861
|
+
"windows-link 0.1.3",
|
|
751
862
|
]
|
|
752
863
|
|
|
753
864
|
[[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.3"
|
|
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
|
|
@@ -26,6 +27,26 @@ chrono.workspace = true
|
|
|
26
27
|
libc.workspace = true
|
|
27
28
|
rusqlite.workspace = true # step 3 db(bundled SQLite,静态链接)
|
|
28
29
|
|
|
30
|
+
# 0.5.x Windows portability Batch 2/3: uses the same `windows = "0.61"`
|
|
31
|
+
# crate as `win-conpty-phase0` so the workspace shares a single
|
|
32
|
+
# Windows-API crate + already-locked Cargo.lock version.
|
|
33
|
+
#
|
|
34
|
+
# - Batch 2 (`platform::file_lock`): `Win32_Storage_FileSystem`
|
|
35
|
+
# (`LockFileEx`/`UnlockFileEx`) + `Win32_System_IO::OVERLAPPED`.
|
|
36
|
+
# - Batch 3 (`platform::process`): `Win32_System_Threading`
|
|
37
|
+
# (`OpenProcess`, `TerminateProcess`, `GetExitCodeProcess`,
|
|
38
|
+
# `GetCurrentProcessId`) + `Win32_System_Diagnostics_ToolHelp`
|
|
39
|
+
# (Toolhelp snapshot for parent-pid discovery).
|
|
40
|
+
[target.'cfg(windows)'.dependencies.windows]
|
|
41
|
+
version = "0.61"
|
|
42
|
+
features = [
|
|
43
|
+
"Win32_Foundation",
|
|
44
|
+
"Win32_Storage_FileSystem",
|
|
45
|
+
"Win32_System_IO",
|
|
46
|
+
"Win32_System_Threading",
|
|
47
|
+
"Win32_System_Diagnostics_ToolHelp",
|
|
48
|
+
]
|
|
49
|
+
|
|
29
50
|
[dev-dependencies]
|
|
30
51
|
serial_test = { version = "3", features = ["file_locks"] }
|
|
31
52
|
|
|
@@ -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
|
}
|