@team-agent/installer 0.5.17 → 0.5.18

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.
@@ -478,7 +478,6 @@ impl EnvVarGuard {
478
478
  }
479
479
  Self { key, previous }
480
480
  }
481
-
482
481
  }
483
482
 
484
483
  impl Drop for EnvVarGuard {
@@ -581,6 +580,9 @@ fn normalize_string(text: String, ctx: &mut NormalizeCtx, key: Option<&str>) ->
581
580
  if key_is_timestamp(key) {
582
581
  return json!("<TS>");
583
582
  }
583
+ if text == crate::packaging::Version::current().as_str() {
584
+ return json!("<VERSION>");
585
+ }
584
586
  if key.contains("endpoint") && value_looks_like_endpoint_path(&text) {
585
587
  return json!("<SOCKET>");
586
588
  }
@@ -602,6 +604,7 @@ fn normalize_string(text: String, ctx: &mut NormalizeCtx, key: Option<&str>) ->
602
604
 
603
605
  fn normalize_path_string(text: &str, ctx: &NormalizeCtx) -> String {
604
606
  let mut out = text.to_string();
607
+ out = normalize_team_agent_binary_path(&out);
605
608
  for alias in &ctx.workspace_aliases {
606
609
  out = out.replace(alias, "<WORKSPACE>");
607
610
  }
@@ -609,7 +612,6 @@ fn normalize_path_string(text: &str, ctx: &NormalizeCtx) -> String {
609
612
  for alias in &ctx.temp_aliases {
610
613
  out = out.replace(alias, "<TMP>");
611
614
  }
612
- out = normalize_team_agent_binary_path(&out);
613
615
  out = normalize_tmux_socket_dir(&out);
614
616
  normalize_socket_token(&out)
615
617
  }
@@ -622,7 +624,7 @@ fn value_looks_like_endpoint_path(text: &str) -> bool {
622
624
  }
623
625
 
624
626
  fn normalize_team_agent_binary_path(text: &str) -> String {
625
- let mut out = text.to_string();
627
+ let mut out = replace_known_team_agent_binary_paths(text);
626
628
  // 0.5.0 hermetic 教训「环境路径类 token 化」的直接延伸
627
629
  // (leader msg_6ee04cf5aee8):归一化改为结构判据,不绑路径前缀。
628
630
  // 用户 CARGO_TARGET_DIR 可以指到任意目录(默认 `<repo>/target`,
@@ -642,6 +644,30 @@ fn normalize_team_agent_binary_path(text: &str) -> String {
642
644
  out
643
645
  }
644
646
 
647
+ fn replace_known_team_agent_binary_paths(text: &str) -> String {
648
+ let mut out = text.to_string();
649
+ for alias in known_team_agent_binary_path_aliases() {
650
+ out = out.replace(&alias, "<TEAM_AGENT_BIN>");
651
+ }
652
+ out
653
+ }
654
+
655
+ fn known_team_agent_binary_path_aliases() -> Vec<String> {
656
+ let mut aliases = Vec::new();
657
+ if let Some(path) = option_env!("CARGO_BIN_EXE_team-agent") {
658
+ push_path_alias(&mut aliases, PathBuf::from(path));
659
+ }
660
+ if let Some(path) = std::env::var_os("CARGO_BIN_EXE_team-agent") {
661
+ push_path_alias(&mut aliases, PathBuf::from(path));
662
+ }
663
+ if let Ok(path) = std::env::current_exe() {
664
+ push_path_alias(&mut aliases, path);
665
+ }
666
+ aliases.sort_by(|a, b| b.len().cmp(&a.len()).then_with(|| a.cmp(b)));
667
+ aliases.dedup();
668
+ aliases
669
+ }
670
+
645
671
  /// Structural (path-prefix-independent) normalization for
646
672
  /// `/…/deps/team_agent-<hex>(.exe)?` occurrences. Scans the input for
647
673
  /// every `/deps/team_agent-` marker and rewrites the containing
@@ -750,17 +776,31 @@ fn normalize_tmux_uid_with_prefix(mut text: String, prefix: &str) -> String {
750
776
  }
751
777
 
752
778
  fn normalize_socket_token(text: &str) -> String {
753
- let mut out = text.to_string();
754
- while let Some(idx) = out.find("ta-") {
755
- let end = out[idx..]
779
+ const CONTEXT: &str = "/tmux-<UID>/";
780
+ let mut out = String::with_capacity(text.len());
781
+ let mut search_from = 0;
782
+ while let Some(offset) = text[search_from..].find(CONTEXT) {
783
+ let context_start = search_from + offset;
784
+ let token_start = context_start + CONTEXT.len();
785
+ if !text[token_start..].starts_with("ta-") {
786
+ out.push_str(&text[search_from..token_start]);
787
+ search_from = token_start;
788
+ continue;
789
+ }
790
+ let end = text[token_start..]
756
791
  .find(|c: char| !(c.is_ascii_alphanumeric() || c == '-'))
757
- .map(|offset| idx + offset)
758
- .unwrap_or_else(|| out.len());
759
- if end == idx + 3 {
760
- break;
792
+ .map(|offset| token_start + offset)
793
+ .unwrap_or_else(|| text.len());
794
+ if end == token_start + 3 {
795
+ out.push_str(&text[search_from..end]);
796
+ search_from = end;
797
+ continue;
761
798
  }
762
- out.replace_range(idx..end, "ta-<SOCKET>");
799
+ out.push_str(&text[search_from..token_start]);
800
+ out.push_str("ta-<SOCKET>");
801
+ search_from = end;
763
802
  }
803
+ out.push_str(&text[search_from..]);
764
804
  out
765
805
  }
766
806
 
@@ -801,3 +841,85 @@ fn pretty(value: &Value) -> String {
801
841
  text.push('\n');
802
842
  text
803
843
  }
844
+
845
+ #[test]
846
+ fn phase_golden_normalizer_maps_custom_cargo_target_deps_binary_before_tmp_aliasing() {
847
+ let ctx = NormalizeCtx {
848
+ workspace_aliases: Vec::new(),
849
+ temp_aliases: vec!["/Volumes/nvme/tmp".to_string()],
850
+ pane_ids: BTreeMap::new(),
851
+ };
852
+ let input = concat!(
853
+ "mcp_servers.team_orchestrator.command=\"",
854
+ "/Volumes/nvme/tmp/ta-0517-target/debug/deps/team_agent-c924abc123",
855
+ "\""
856
+ );
857
+
858
+ assert_eq!(
859
+ normalize_path_string(input, &ctx),
860
+ "mcp_servers.team_orchestrator.command=\"<TEAM_AGENT_BIN>\"",
861
+ "custom CARGO_TARGET_DIR binary paths must normalize to <TEAM_AGENT_BIN> before tmp/socket tokenization"
862
+ );
863
+ }
864
+
865
+ #[test]
866
+ fn phase_golden_normalizer_maps_current_test_binary_to_team_agent_marker() {
867
+ let ctx = NormalizeCtx {
868
+ workspace_aliases: Vec::new(),
869
+ temp_aliases: vec!["/Volumes/nvme/tmp".to_string()],
870
+ pane_ids: BTreeMap::new(),
871
+ };
872
+ let input = format!(
873
+ "mcp_servers.team_orchestrator.command=\"{}\"",
874
+ std::env::current_exe()
875
+ .expect("current test binary path")
876
+ .display()
877
+ );
878
+
879
+ assert_eq!(
880
+ normalize_path_string(&input, &ctx),
881
+ "mcp_servers.team_orchestrator.command=\"<TEAM_AGENT_BIN>\"",
882
+ "actual test argv0/current_exe path must normalize to <TEAM_AGENT_BIN>"
883
+ );
884
+ }
885
+
886
+ #[test]
887
+ fn phase_golden_normalizer_does_not_treat_cargo_target_dir_as_socket_token() {
888
+ let ctx = NormalizeCtx {
889
+ workspace_aliases: Vec::new(),
890
+ temp_aliases: vec!["/Volumes/nvme/tmp".to_string()],
891
+ pane_ids: BTreeMap::new(),
892
+ };
893
+
894
+ assert_eq!(
895
+ normalize_path_string("/Volumes/nvme/tmp/ta-0517-target/build.log", &ctx),
896
+ "<TMP>/ta-0517-target/build.log",
897
+ "ta-* directory names outside tmux socket context must not be rewritten as ta-<SOCKET>"
898
+ );
899
+ assert_eq!(
900
+ normalize_path_string("tmux -S /tmp/tmux-501/ta-a60d10b25edd attach", &ctx),
901
+ "tmux -S <TMP>/tmux-<UID>/ta-<SOCKET> attach",
902
+ "real tmux socket paths still need stable socket tokenization"
903
+ );
904
+ }
905
+
906
+ #[test]
907
+ fn phase_golden_normalizer_maps_current_version_by_value_only() {
908
+ let mut ctx = NormalizeCtx {
909
+ workspace_aliases: Vec::new(),
910
+ temp_aliases: Vec::new(),
911
+ pane_ids: BTreeMap::new(),
912
+ };
913
+ let current = crate::packaging::Version::current().as_str().to_string();
914
+
915
+ assert_eq!(
916
+ normalize_string(current.clone(), &mut ctx, Some("arbitrary_field")),
917
+ json!("<VERSION>"),
918
+ "current binary version values must normalize without depending on field names"
919
+ );
920
+ assert_eq!(
921
+ normalize_string(format!("version={current}"), &mut ctx, Some("binary_version")),
922
+ json!(format!("version={current}")),
923
+ "version normalization must be exact-value based, not a broad substring or field-name rewrite"
924
+ );
925
+ }
@@ -83,6 +83,52 @@ impl PlanId {
83
83
  }
84
84
  }
85
85
 
86
+ #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
87
+ pub struct CoordinatorStartSummary {
88
+ pub ok: bool,
89
+ pub status: String,
90
+ pub pid: Option<u32>,
91
+ pub binary_path: Option<String>,
92
+ pub binary_version: Option<String>,
93
+ pub rotation_reason: Option<String>,
94
+ }
95
+
96
+ impl CoordinatorStartSummary {
97
+ pub fn from_start_report(report: &crate::coordinator::StartReport) -> Self {
98
+ Self {
99
+ ok: report.ok,
100
+ status: coordinator_start_status_wire(report.status).to_string(),
101
+ pid: report.pid.map(|pid| pid.get()),
102
+ binary_path: report.binary_path.clone(),
103
+ binary_version: report.binary_version.clone(),
104
+ rotation_reason: report.rotation_reason.clone(),
105
+ }
106
+ }
107
+ }
108
+
109
+ fn coordinator_start_status_wire(status: crate::coordinator::StartOutcome) -> &'static str {
110
+ match status {
111
+ crate::coordinator::StartOutcome::AlreadyRunning => "already_running",
112
+ crate::coordinator::StartOutcome::RestartIncompatibleStopFailed => {
113
+ "restart_incompatible_stop_failed"
114
+ }
115
+ crate::coordinator::StartOutcome::SchemaIncompatible => "schema_incompatible",
116
+ crate::coordinator::StartOutcome::Started => "started",
117
+ crate::coordinator::StartOutcome::StartedAfterRotation => "started_after_rotation",
118
+ }
119
+ }
120
+
121
+ pub fn coordinator_start_summary_value(summary: &CoordinatorStartSummary) -> serde_json::Value {
122
+ serde_json::json!({
123
+ "ok": summary.ok,
124
+ "status": summary.status,
125
+ "pid": summary.pid,
126
+ "binary_path": summary.binary_path,
127
+ "binary_version": summary.binary_version,
128
+ "rotation_reason": summary.rotation_reason,
129
+ })
130
+ }
131
+
86
132
  impl std::fmt::Display for PlanId {
87
133
  fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
88
134
  f.write_str(&self.0)
@@ -650,6 +696,7 @@ pub enum RestartReport {
650
696
  session_name: SessionName,
651
697
  agents: Vec<RestartedAgent>,
652
698
  coordinator_started: bool,
699
+ coordinator: CoordinatorStartSummary,
653
700
  next_actions: Vec<String>,
654
701
  attach_commands: Vec<String>,
655
702
  },
@@ -660,6 +707,7 @@ pub enum RestartReport {
660
707
  agents: Vec<RestartedAgent>,
661
708
  failed_agents: Vec<RestartFailedAgent>,
662
709
  coordinator_started: bool,
710
+ coordinator: CoordinatorStartSummary,
663
711
  next_actions: Vec<String>,
664
712
  attach_commands: Vec<String>,
665
713
  },
@@ -301,12 +301,19 @@ fn start_report_value(report: &crate::coordinator::StartReport) -> serde_json::V
301
301
  }
302
302
  crate::coordinator::StartOutcome::SchemaIncompatible => "schema_incompatible",
303
303
  crate::coordinator::StartOutcome::Started => "started",
304
+ crate::coordinator::StartOutcome::StartedAfterRotation => "started_after_rotation",
304
305
  };
305
306
  let mut value = serde_json::json!({
306
307
  "ok": report.ok,
307
308
  "pid": report.pid.map(|p| p.get()),
308
309
  "status": status,
310
+ "binary_path": report.binary_path.clone(),
311
+ "binary_version": report.binary_version.clone(),
312
+ "rotation_reason": report.rotation_reason.clone(),
309
313
  });
314
+ if let Some(previous_pid) = report.previous_pid {
315
+ value["previous_pid"] = serde_json::json!(previous_pid.get());
316
+ }
310
317
  if let Some(log) = &report.log {
311
318
  value["log"] = serde_json::json!(log.to_string_lossy().to_string());
312
319
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@team-agent/installer",
3
- "version": "0.5.17",
3
+ "version": "0.5.18",
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.5.17",
24
- "@team-agent/cli-darwin-x64": "0.5.17",
25
- "@team-agent/cli-linux-x64": "0.5.17"
23
+ "@team-agent/cli-darwin-arm64": "0.5.18",
24
+ "@team-agent/cli-darwin-x64": "0.5.18",
25
+ "@team-agent/cli-linux-x64": "0.5.18"
26
26
  },
27
27
  "scripts": {
28
28
  "postinstall": "node npm/bincheck.mjs",