@team-agent/installer 0.3.18 → 0.3.19
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/cli/emit.rs +4 -3
- package/crates/team-agent/src/cli/leader.rs +15 -1
- package/crates/team-agent/src/cli/tests/base.rs +15 -14
- package/crates/team-agent/src/cli/tests/status_send.rs +42 -0
- package/crates/team-agent/src/cli/types.rs +6 -0
- package/crates/team-agent/src/leader/lease.rs +3 -4
- package/crates/team-agent/src/leader/start.rs +70 -7
- package/crates/team-agent/src/leader/tests/identity.rs +10 -2
- package/crates/team-agent/src/tmux_backend/tests.rs +21 -0
- package/crates/team-agent/src/tmux_backend.rs +31 -9
- package/package.json +4 -4
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
|
@@ -32,9 +32,10 @@ pub fn run(argv: &[String], cwd: &Path) -> ExitCode {
|
|
|
32
32
|
return emit_missing_subcommand_usage();
|
|
33
33
|
};
|
|
34
34
|
if command == "codex" || command == "claude" || command == "copilot" {
|
|
35
|
-
return cmd_leader_passthrough(command, &argv[1..], cwd)
|
|
36
|
-
|
|
37
|
-
|
|
35
|
+
return match cmd_leader_passthrough(command, &argv[1..], cwd) {
|
|
36
|
+
Ok(result) => emit_result(result),
|
|
37
|
+
Err(error) => emit_cli_error(command, &argv[1..], cwd, &error),
|
|
38
|
+
};
|
|
38
39
|
}
|
|
39
40
|
if matches!(command, "-h" | "--help" | "help") {
|
|
40
41
|
println!("{}", command_help(None));
|
|
@@ -23,7 +23,14 @@ pub fn leader_launcher_args(values: &[String]) -> Result<LeaderLauncherArgs, Cli
|
|
|
23
23
|
while idx < values.len() {
|
|
24
24
|
let token = &values[idx];
|
|
25
25
|
if token == "--" {
|
|
26
|
-
|
|
26
|
+
for value in values.iter().skip(idx + 1) {
|
|
27
|
+
if is_leader_launcher_flag(value) {
|
|
28
|
+
return Err(CliError::Runtime(format!(
|
|
29
|
+
"Team Agent launcher flag {value} must appear before --"
|
|
30
|
+
)));
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
out.provider_args.extend(values.iter().skip(idx + 1).cloned());
|
|
27
34
|
break;
|
|
28
35
|
} else if token == "--attach" || token == "--attach-existing" {
|
|
29
36
|
out.attach_existing = true;
|
|
@@ -49,6 +56,13 @@ pub fn leader_launcher_args(values: &[String]) -> Result<LeaderLauncherArgs, Cli
|
|
|
49
56
|
Ok(out)
|
|
50
57
|
}
|
|
51
58
|
|
|
59
|
+
fn is_leader_launcher_flag(value: &str) -> bool {
|
|
60
|
+
matches!(
|
|
61
|
+
value,
|
|
62
|
+
"--external-leader" | "--attach" | "--attach-existing" | "--confirm" | "--attach-session"
|
|
63
|
+
) || value.starts_with("--attach-session=")
|
|
64
|
+
}
|
|
65
|
+
|
|
52
66
|
fn leader_launcher_json(values: &[String]) -> bool {
|
|
53
67
|
values
|
|
54
68
|
.iter()
|
|
@@ -86,17 +86,18 @@ use super::*;
|
|
|
86
86
|
assert!(!got.attach_existing);
|
|
87
87
|
assert_eq!(
|
|
88
88
|
got.provider_args,
|
|
89
|
-
vec!["--
|
|
89
|
+
vec!["--model".to_string(), "opus".to_string()]
|
|
90
90
|
);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
#[test]
|
|
94
|
-
fn
|
|
95
|
-
let
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
94
|
+
fn leader_launcher_args_external_leader_after_dashdash_errors() {
|
|
95
|
+
let err = leader_launcher_args(&["--".into(), "--external-leader".into()])
|
|
96
|
+
.expect_err("Team Agent flags after -- must not be silently passed to provider");
|
|
97
|
+
assert!(
|
|
98
|
+
err.to_string()
|
|
99
|
+
.contains("Team Agent launcher flag --external-leader must appear before --"),
|
|
100
|
+
"unexpected error: {err}"
|
|
100
101
|
);
|
|
101
102
|
}
|
|
102
103
|
|
|
@@ -116,22 +117,22 @@ use super::*;
|
|
|
116
117
|
}
|
|
117
118
|
|
|
118
119
|
#[test]
|
|
119
|
-
fn
|
|
120
|
-
//
|
|
121
|
-
// provider_args=["
|
|
122
|
-
//
|
|
120
|
+
fn leader_launcher_args_dashdash_passthrough_strips_separator() {
|
|
121
|
+
// ["--attach","--","-x","--provider-confirm"] ->
|
|
122
|
+
// provider_args=["-x","--provider-confirm"], attach_existing=True, confirm_attach=False
|
|
123
|
+
// Known Team Agent launcher flags after `--` are rejected by a separate guard.
|
|
123
124
|
let got = leader_launcher_args(&[
|
|
124
125
|
"--attach".into(),
|
|
125
126
|
"--".into(),
|
|
126
127
|
"-x".into(),
|
|
127
|
-
"--confirm".into(),
|
|
128
|
+
"--provider-confirm".into(),
|
|
128
129
|
])
|
|
129
130
|
.unwrap();
|
|
130
131
|
assert!(got.attach_existing);
|
|
131
|
-
assert!(!got.confirm_attach
|
|
132
|
+
assert!(!got.confirm_attach);
|
|
132
133
|
assert_eq!(
|
|
133
134
|
got.provider_args,
|
|
134
|
-
vec!["
|
|
135
|
+
vec!["-x".to_string(), "--provider-confirm".to_string()]
|
|
135
136
|
);
|
|
136
137
|
}
|
|
137
138
|
|
|
@@ -415,6 +415,48 @@ fn run_send_unknown_task_renders_error_not_silent_swallow() {
|
|
|
415
415
|
let _ = std::fs::remove_dir_all(&ws);
|
|
416
416
|
}
|
|
417
417
|
|
|
418
|
+
#[test]
|
|
419
|
+
fn run_leader_passthrough_flag_after_dashdash_renders_error_not_silent_swallow() {
|
|
420
|
+
let ws = std::env::temp_dir().join(format!(
|
|
421
|
+
"ta-run-leaderflag-{}-{}",
|
|
422
|
+
std::process::id(),
|
|
423
|
+
std::time::SystemTime::now()
|
|
424
|
+
.duration_since(std::time::UNIX_EPOCH)
|
|
425
|
+
.unwrap()
|
|
426
|
+
.as_nanos()
|
|
427
|
+
));
|
|
428
|
+
std::fs::create_dir_all(&ws).unwrap();
|
|
429
|
+
let argv: Vec<String> = ["codex", "--", "--external-leader"]
|
|
430
|
+
.iter()
|
|
431
|
+
.map(ToString::to_string)
|
|
432
|
+
.collect();
|
|
433
|
+
|
|
434
|
+
let code = run(&argv, &ws);
|
|
435
|
+
|
|
436
|
+
assert_eq!(
|
|
437
|
+
code,
|
|
438
|
+
ExitCode::Error,
|
|
439
|
+
"misplaced leader flag must fail before provider exec"
|
|
440
|
+
);
|
|
441
|
+
let logs_dir = ws.join(".team").join("logs");
|
|
442
|
+
let mut found = String::new();
|
|
443
|
+
if let Ok(entries) = std::fs::read_dir(&logs_dir) {
|
|
444
|
+
for entry in entries.flatten() {
|
|
445
|
+
let name = entry.file_name().to_string_lossy().to_string();
|
|
446
|
+
if name.starts_with("cli-error-") {
|
|
447
|
+
found = std::fs::read_to_string(entry.path()).unwrap_or_default();
|
|
448
|
+
break;
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
assert!(
|
|
453
|
+
found.contains("Team Agent launcher flag --external-leader must appear before --"),
|
|
454
|
+
"leader passthrough run() must render the misplaced flag error; a silent swallow leaves no \
|
|
455
|
+
cli-error log. got log body: {found:?}"
|
|
456
|
+
);
|
|
457
|
+
let _ = std::fs::remove_dir_all(&ws);
|
|
458
|
+
}
|
|
459
|
+
|
|
418
460
|
// R8 D6 (c-lite offline byte-lock): the CLI requeued_exhausted_watchers return projection, extracted
|
|
419
461
|
// into a pure helper, must project the golden event's watcher_ids STRING list (leader/__init__.py:56) —
|
|
420
462
|
// NOT the Rust `requeued` Vec<WatcherNotice> objects.
|
|
@@ -101,6 +101,12 @@ impl CliError {
|
|
|
101
101
|
.to_string(),
|
|
102
102
|
]);
|
|
103
103
|
}
|
|
104
|
+
} else if error.contains("Team Agent launcher flag")
|
|
105
|
+
&& error.contains("must appear before --")
|
|
106
|
+
{
|
|
107
|
+
payload.action = String::from(
|
|
108
|
+
"move the Team Agent launcher flag before `--`; only provider flags belong after `--`",
|
|
109
|
+
);
|
|
104
110
|
}
|
|
105
111
|
payload
|
|
106
112
|
}
|
|
@@ -144,6 +144,7 @@ fn attach_leader_targets(workspace: &Path, state: &Value) -> Vec<AttachLeaderTar
|
|
|
144
144
|
let mut targets = Vec::new();
|
|
145
145
|
for endpoint in state_recorded_tmux_endpoints(state) {
|
|
146
146
|
let backend = tmux_backend_for_endpoint(&endpoint);
|
|
147
|
+
let resolved_endpoint = backend.tmux_endpoint();
|
|
147
148
|
targets.extend(
|
|
148
149
|
backend
|
|
149
150
|
.list_targets()
|
|
@@ -151,7 +152,7 @@ fn attach_leader_targets(workspace: &Path, state: &Value) -> Vec<AttachLeaderTar
|
|
|
151
152
|
.into_iter()
|
|
152
153
|
.map(|info| AttachLeaderTarget {
|
|
153
154
|
info,
|
|
154
|
-
endpoint:
|
|
155
|
+
endpoint: resolved_endpoint.clone(),
|
|
155
156
|
}),
|
|
156
157
|
);
|
|
157
158
|
}
|
|
@@ -168,10 +169,8 @@ fn attach_leader_targets(workspace: &Path, state: &Value) -> Vec<AttachLeaderTar
|
|
|
168
169
|
fn tmux_backend_for_endpoint(endpoint: &str) -> crate::tmux_backend::TmuxBackend {
|
|
169
170
|
if endpoint.is_empty() || endpoint == "default" {
|
|
170
171
|
crate::tmux_backend::TmuxBackend::new()
|
|
171
|
-
} else if Path::new(endpoint).is_absolute() {
|
|
172
|
-
crate::tmux_backend::TmuxBackend::for_tmux_endpoint(endpoint)
|
|
173
172
|
} else {
|
|
174
|
-
crate::tmux_backend::TmuxBackend::
|
|
173
|
+
crate::tmux_backend::TmuxBackend::for_tmux_endpoint(endpoint)
|
|
175
174
|
}
|
|
176
175
|
}
|
|
177
176
|
|
|
@@ -196,6 +196,9 @@ pub fn execute_leader_plan(
|
|
|
196
196
|
let detached = plan.mode == LeaderStartMode::NewTmuxSession
|
|
197
197
|
&& !std::io::stdin().is_terminal()
|
|
198
198
|
&& insert_detach_flag(&mut argv);
|
|
199
|
+
if plan.is_external_leader {
|
|
200
|
+
persist_external_leader_topology_marker(plan, workspace)?;
|
|
201
|
+
}
|
|
199
202
|
let status = run_leader_argv(&argv, &plan.leader_env, plan, workspace)?;
|
|
200
203
|
let code = status.code();
|
|
201
204
|
if !status.success() {
|
|
@@ -205,9 +208,6 @@ pub fn execute_leader_plan(
|
|
|
205
208
|
.unwrap_or_else(|| "signal".to_string())
|
|
206
209
|
)));
|
|
207
210
|
}
|
|
208
|
-
if plan.is_external_leader {
|
|
209
|
-
persist_external_leader_topology_marker(plan, workspace)?;
|
|
210
|
-
}
|
|
211
211
|
if detached {
|
|
212
212
|
Ok(LeaderLaunchOutcome {
|
|
213
213
|
status: LeaderLaunchStatus::Detached,
|
|
@@ -258,7 +258,7 @@ fn start_argv(
|
|
|
258
258
|
match mode {
|
|
259
259
|
LeaderStartMode::ExecProvider => {
|
|
260
260
|
let mut argv = vec![provider_cmd];
|
|
261
|
-
argv.extend(provider_args
|
|
261
|
+
argv.extend(normalized_provider_args(provider_args));
|
|
262
262
|
Ok(argv)
|
|
263
263
|
}
|
|
264
264
|
LeaderStartMode::ManagedTmuxClient => {
|
|
@@ -289,7 +289,7 @@ fn start_argv(
|
|
|
289
289
|
exports.push(shlex_quote(&format!("PATH={path}")));
|
|
290
290
|
}
|
|
291
291
|
let mut provider_argv = vec![provider_cmd];
|
|
292
|
-
provider_argv.extend(provider_args
|
|
292
|
+
provider_argv.extend(normalized_provider_args(provider_args));
|
|
293
293
|
let shell = format!(
|
|
294
294
|
"cd {} && export {} && exec {}",
|
|
295
295
|
shlex_quote(&resolved_workspace.to_string_lossy()),
|
|
@@ -316,10 +316,17 @@ fn start_argv(
|
|
|
316
316
|
|
|
317
317
|
fn provider_command_argv(provider: Provider, provider_args: &[String]) -> Vec<String> {
|
|
318
318
|
let mut argv = vec![provider_command_name(provider).to_string()];
|
|
319
|
-
argv.extend(provider_args
|
|
319
|
+
argv.extend(normalized_provider_args(provider_args));
|
|
320
320
|
argv
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
+
fn normalized_provider_args(provider_args: &[String]) -> impl Iterator<Item = String> + '_ {
|
|
324
|
+
provider_args
|
|
325
|
+
.iter()
|
|
326
|
+
.skip(usize::from(provider_args.first().is_some_and(|arg| arg == "--")))
|
|
327
|
+
.cloned()
|
|
328
|
+
}
|
|
329
|
+
|
|
323
330
|
fn managed_team_session_name(identity: &LeaderIdentity) -> SessionName {
|
|
324
331
|
let team = identity.team_id.as_str();
|
|
325
332
|
if team.starts_with("team-") {
|
|
@@ -777,7 +784,12 @@ mod tests {
|
|
|
777
784
|
use std::path::Path;
|
|
778
785
|
use std::sync::Mutex;
|
|
779
786
|
|
|
787
|
+
use crate::leader::{
|
|
788
|
+
LeaderIdentity, LeaderLaunchSocket, LeaderSessionUuidSource, LeaderStartMode,
|
|
789
|
+
LeaderStartPlan,
|
|
790
|
+
};
|
|
780
791
|
use crate::model::enums::PaneLiveness;
|
|
792
|
+
use crate::model::ids::{LeaderSessionUuid, TeamKey};
|
|
781
793
|
use crate::provider::{Provider, COPILOT_READY_MARKER, COPILOT_TRUST_PROMPT_MARKER};
|
|
782
794
|
use crate::transport::{
|
|
783
795
|
AttachOutcome, BackendKind, CaptureRange, CapturedText, InjectPayload, InjectReport,
|
|
@@ -786,7 +798,7 @@ mod tests {
|
|
|
786
798
|
TurnVerification, WindowName,
|
|
787
799
|
};
|
|
788
800
|
|
|
789
|
-
use super::handle_exec_provider_startup_prompts;
|
|
801
|
+
use super::{execute_leader_plan, handle_exec_provider_startup_prompts, shlex_quote};
|
|
790
802
|
|
|
791
803
|
struct ScriptedTransport {
|
|
792
804
|
screens: Mutex<Vec<String>>,
|
|
@@ -937,6 +949,57 @@ mod tests {
|
|
|
937
949
|
}
|
|
938
950
|
}
|
|
939
951
|
|
|
952
|
+
#[test]
|
|
953
|
+
fn external_exec_provider_persists_topology_before_provider_exec() {
|
|
954
|
+
let workspace = std::env::temp_dir().join(format!(
|
|
955
|
+
"ta-external-pre-exec-{}",
|
|
956
|
+
std::process::id()
|
|
957
|
+
));
|
|
958
|
+
std::fs::create_dir_all(&workspace).unwrap();
|
|
959
|
+
let state_path = crate::state::persist::runtime_state_path(&workspace);
|
|
960
|
+
let command = format!(
|
|
961
|
+
"test -f {path} && grep -q is_external_leader {path}",
|
|
962
|
+
path = shlex_quote(&state_path.to_string_lossy())
|
|
963
|
+
);
|
|
964
|
+
let identity = LeaderIdentity {
|
|
965
|
+
leader_session_uuid: LeaderSessionUuid::derive(
|
|
966
|
+
"fp",
|
|
967
|
+
&workspace.to_string_lossy(),
|
|
968
|
+
"tester",
|
|
969
|
+
"current",
|
|
970
|
+
)
|
|
971
|
+
.unwrap(),
|
|
972
|
+
leader_session_uuid_source: LeaderSessionUuidSource::Derived,
|
|
973
|
+
machine_fingerprint: "fp".to_string(),
|
|
974
|
+
workspace_abspath: workspace.clone(),
|
|
975
|
+
os_user: "tester".to_string(),
|
|
976
|
+
team_id: TeamKey::new("current"),
|
|
977
|
+
};
|
|
978
|
+
let plan = LeaderStartPlan {
|
|
979
|
+
mode: LeaderStartMode::ExecProvider,
|
|
980
|
+
provider: Provider::Codex,
|
|
981
|
+
workspace: workspace.clone(),
|
|
982
|
+
socket: LeaderLaunchSocket::Workspace,
|
|
983
|
+
session_name: None,
|
|
984
|
+
argv: vec!["sh".to_string(), "-c".to_string(), command],
|
|
985
|
+
provider_argv: vec!["codex".to_string()],
|
|
986
|
+
leader_window: None,
|
|
987
|
+
is_external_leader: true,
|
|
988
|
+
leader_env: BTreeMap::new(),
|
|
989
|
+
identity: Some(identity),
|
|
990
|
+
detached: false,
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
let outcome = execute_leader_plan(&plan, &workspace)
|
|
994
|
+
.expect("external marker must be present before provider argv runs");
|
|
995
|
+
|
|
996
|
+
assert_eq!(outcome.status, crate::leader::LeaderLaunchStatus::Exited);
|
|
997
|
+
let state = crate::state::persist::load_runtime_state(&workspace).unwrap();
|
|
998
|
+
assert_eq!(state["is_external_leader"], serde_json::json!(true));
|
|
999
|
+
assert_eq!(state["teams"]["current"]["is_external_leader"], serde_json::json!(true));
|
|
1000
|
+
let _ = std::fs::remove_dir_all(&workspace);
|
|
1001
|
+
}
|
|
1002
|
+
|
|
940
1003
|
#[test]
|
|
941
1004
|
fn exec_provider_leader_startup_prompt_handler_reuses_copilot_adapter() {
|
|
942
1005
|
let transport = ScriptedTransport::new(vec![
|
|
@@ -108,12 +108,20 @@ use super::*;
|
|
|
108
108
|
let ws = std::env::temp_dir().join(format!("ta_rs_lsp_external_{}", std::process::id()));
|
|
109
109
|
std::fs::create_dir_all(&ws).unwrap();
|
|
110
110
|
|
|
111
|
-
let
|
|
111
|
+
let provider_args = vec!["--".to_string(), "--model".to_string(), "opus".to_string()];
|
|
112
|
+
let plan = leader_start_plan(Provider::Fake, &provider_args, &ws, false, false, None, true).unwrap();
|
|
112
113
|
|
|
113
114
|
assert_eq!(plan.mode, LeaderStartMode::ExecProvider);
|
|
114
115
|
assert!(plan.is_external_leader);
|
|
115
116
|
assert_eq!(plan.leader_window, None);
|
|
116
|
-
assert_eq!(
|
|
117
|
+
assert_eq!(
|
|
118
|
+
plan.argv,
|
|
119
|
+
vec!["fake".to_string(), "--model".to_string(), "opus".to_string()]
|
|
120
|
+
);
|
|
121
|
+
assert_eq!(
|
|
122
|
+
plan.provider_argv,
|
|
123
|
+
vec!["fake".to_string(), "--model".to_string(), "opus".to_string()]
|
|
124
|
+
);
|
|
117
125
|
}
|
|
118
126
|
|
|
119
127
|
#[test]
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
|
|
9
9
|
|
|
10
10
|
use std::collections::{BTreeMap, VecDeque};
|
|
11
|
+
use std::os::unix::net::UnixListener;
|
|
11
12
|
use std::path::Path;
|
|
12
13
|
use std::sync::{Arc, Mutex};
|
|
13
14
|
|
|
@@ -251,8 +252,20 @@
|
|
|
251
252
|
}
|
|
252
253
|
|
|
253
254
|
#[test]
|
|
255
|
+
#[serial_test::serial(env)]
|
|
254
256
|
fn leader_receiver_short_endpoint_must_not_reconstruct_tmux_l_socket() {
|
|
255
257
|
let endpoint = "dl9aa40c88";
|
|
258
|
+
let uid = unsafe { libc::geteuid() };
|
|
259
|
+
let tmp = std::env::temp_dir().join(format!(
|
|
260
|
+
"ta-tmux-short-endpoint-{}",
|
|
261
|
+
std::process::id()
|
|
262
|
+
));
|
|
263
|
+
let root = tmp.join(format!("tmux-{uid}"));
|
|
264
|
+
std::fs::create_dir_all(&root).unwrap();
|
|
265
|
+
let socket_path = root.join(endpoint);
|
|
266
|
+
let _listener = UnixListener::bind(&socket_path).unwrap();
|
|
267
|
+
let socket_path = socket_path.canonicalize().unwrap();
|
|
268
|
+
let _env = EnvGuard::apply(&[("TMPDIR", Some(tmp.to_str().unwrap()))]);
|
|
256
269
|
let (be, rec) = {
|
|
257
270
|
let recorded = Arc::new(Mutex::new(Vec::new()));
|
|
258
271
|
let runner = MockCommandRunner {
|
|
@@ -275,6 +288,14 @@
|
|
|
275
288
|
"non-canonical leader endpoints must be rejected or left unbound, never reconstructed as \
|
|
276
289
|
tmux -L <short> under the coordinator socket root; calls={calls:?}"
|
|
277
290
|
);
|
|
291
|
+
assert!(
|
|
292
|
+
calls.iter().all(|call| call.starts_with(&[
|
|
293
|
+
"tmux".to_string(),
|
|
294
|
+
"-S".to_string(),
|
|
295
|
+
socket_path.to_string_lossy().to_string()
|
|
296
|
+
])),
|
|
297
|
+
"short leader endpoints must resolve to the existing physical socket path with -S; calls={calls:?}"
|
|
298
|
+
);
|
|
278
299
|
}
|
|
279
300
|
|
|
280
301
|
// ── 1. has_session: exit 0 -> true, exit 1 -> false; argv = `tmux has-session -t <s>` ──────────
|
|
@@ -233,6 +233,12 @@ impl TmuxBackend {
|
|
|
233
233
|
socket: Some(TmuxSocketEndpoint::Path(endpoint.to_string())),
|
|
234
234
|
event_workspace: None,
|
|
235
235
|
}
|
|
236
|
+
} else if let Some(path) = socket_path_for_name(endpoint) {
|
|
237
|
+
Self {
|
|
238
|
+
runner: Box::new(RealCommandRunner),
|
|
239
|
+
socket: Some(TmuxSocketEndpoint::Path(path.to_string_lossy().into_owned())),
|
|
240
|
+
event_workspace: None,
|
|
241
|
+
}
|
|
236
242
|
} else {
|
|
237
243
|
Self::new()
|
|
238
244
|
}
|
|
@@ -275,6 +281,12 @@ impl TmuxBackend {
|
|
|
275
281
|
socket: None,
|
|
276
282
|
event_workspace: None,
|
|
277
283
|
}
|
|
284
|
+
} else if let Some(path) = socket_path_for_name(endpoint) {
|
|
285
|
+
Self {
|
|
286
|
+
runner,
|
|
287
|
+
socket: Some(TmuxSocketEndpoint::Path(path.to_string_lossy().into_owned())),
|
|
288
|
+
event_workspace: None,
|
|
289
|
+
}
|
|
278
290
|
} else {
|
|
279
291
|
Self {
|
|
280
292
|
runner,
|
|
@@ -340,13 +352,7 @@ pub(crate) fn socket_name_for_workspace(workspace: &Path) -> String {
|
|
|
340
352
|
}
|
|
341
353
|
|
|
342
354
|
pub(crate) fn socket_path_for_workspace(workspace: &Path) -> Option<PathBuf> {
|
|
343
|
-
|
|
344
|
-
return Some(existing);
|
|
345
|
-
}
|
|
346
|
-
let uid = unsafe { libc::geteuid() };
|
|
347
|
-
let default_root = PathBuf::from(format!("/tmp/tmux-{uid}"));
|
|
348
|
-
let default_root = default_root.canonicalize().unwrap_or(default_root);
|
|
349
|
-
Some(default_root.join(socket_name_for_workspace(workspace)))
|
|
355
|
+
socket_path_for_name(&socket_name_for_workspace(workspace))
|
|
350
356
|
}
|
|
351
357
|
|
|
352
358
|
pub(crate) fn socket_probe_missing_for_workspace(workspace: &Path) -> bool {
|
|
@@ -354,11 +360,27 @@ pub(crate) fn socket_probe_missing_for_workspace(workspace: &Path) -> bool {
|
|
|
354
360
|
}
|
|
355
361
|
|
|
356
362
|
fn existing_socket_path_for_workspace(workspace: &Path) -> Option<PathBuf> {
|
|
357
|
-
|
|
363
|
+
existing_socket_path_for_name(&socket_name_for_workspace(workspace))
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
pub(crate) fn socket_path_for_name(socket_name: &str) -> Option<PathBuf> {
|
|
367
|
+
if socket_name.is_empty() || socket_name == "default" || Path::new(socket_name).is_absolute() {
|
|
368
|
+
return None;
|
|
369
|
+
}
|
|
370
|
+
if let Some(existing) = existing_socket_path_for_name(socket_name) {
|
|
371
|
+
return Some(existing);
|
|
372
|
+
}
|
|
373
|
+
let uid = unsafe { libc::geteuid() };
|
|
374
|
+
let default_root = PathBuf::from(format!("/tmp/tmux-{uid}"));
|
|
375
|
+
let default_root = default_root.canonicalize().unwrap_or(default_root);
|
|
376
|
+
Some(default_root.join(socket_name))
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
fn existing_socket_path_for_name(socket_name: &str) -> Option<PathBuf> {
|
|
358
380
|
let roots = tmux_socket_roots();
|
|
359
381
|
for root in &roots {
|
|
360
382
|
let root = root.canonicalize().unwrap_or_else(|_| root.clone());
|
|
361
|
-
let candidate = root.join(
|
|
383
|
+
let candidate = root.join(socket_name);
|
|
362
384
|
if candidate.exists() {
|
|
363
385
|
return Some(candidate.canonicalize().unwrap_or(candidate));
|
|
364
386
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@team-agent/installer",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.19",
|
|
4
4
|
"description": "npx installer for Team Agent",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"codex",
|
|
@@ -20,9 +20,9 @@
|
|
|
20
20
|
"team-agent-installer": "npm/install.mjs"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
|
-
"@team-agent/cli-darwin-arm64": "0.3.
|
|
24
|
-
"@team-agent/cli-darwin-x64": "0.3.
|
|
25
|
-
"@team-agent/cli-linux-x64": "0.3.
|
|
23
|
+
"@team-agent/cli-darwin-arm64": "0.3.19",
|
|
24
|
+
"@team-agent/cli-darwin-x64": "0.3.19",
|
|
25
|
+
"@team-agent/cli-linux-x64": "0.3.19"
|
|
26
26
|
},
|
|
27
27
|
"scripts": {
|
|
28
28
|
"postinstall": "node npm/bincheck.mjs",
|