@team-agent/installer 0.5.17 → 0.5.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/adapters.rs +2 -1
- package/crates/team-agent/src/cli/diagnose.rs +185 -0
- package/crates/team-agent/src/cli/mod.rs +46 -1
- package/crates/team-agent/src/cli/status_port.rs +5 -0
- package/crates/team-agent/src/cli/tests/lane_c.rs +17 -7
- package/crates/team-agent/src/coordinator/backoff.rs +15 -14
- package/crates/team-agent/src/coordinator/health.rs +142 -12
- package/crates/team-agent/src/coordinator/tests/basics.rs +4 -0
- package/crates/team-agent/src/coordinator/tests/mod.rs +3 -0
- package/crates/team-agent/src/coordinator/tick.rs +70 -68
- package/crates/team-agent/src/coordinator/types.rs +47 -0
- package/crates/team-agent/src/diagnose/orphans.rs +35 -13
- package/crates/team-agent/src/lifecycle/restart/agent.rs +4 -2
- package/crates/team-agent/src/lifecycle/restart/common.rs +6 -3
- package/crates/team-agent/src/lifecycle/restart/rebuild.rs +34 -20
- package/crates/team-agent/src/lifecycle/tests/lifecycle_lock.rs +1 -1
- package/crates/team-agent/src/lifecycle/tests/phase_golden.rs +133 -11
- package/crates/team-agent/src/lifecycle/types.rs +54 -0
- package/crates/team-agent/src/messaging/results.rs +7 -0
- package/package.json +4 -4
package/Cargo.lock
CHANGED
package/Cargo.toml
CHANGED
|
@@ -732,7 +732,8 @@ pub fn cmd_diagnose(args: &DiagnoseArgs) -> Result<CmdResult, CliError> {
|
|
|
732
732
|
&selected.run_workspace,
|
|
733
733
|
)),
|
|
734
734
|
};
|
|
735
|
-
let (issues, suggested_repairs) =
|
|
735
|
+
let (issues, suggested_repairs) =
|
|
736
|
+
diagnose_runtime_for_workspace(&selected.run_workspace, &state, backend.as_ref());
|
|
736
737
|
let ok = issues.as_array().is_some_and(Vec::is_empty);
|
|
737
738
|
Ok(CmdResult::from_json(
|
|
738
739
|
json!({
|
|
@@ -165,6 +165,125 @@ pub(crate) fn diagnose_runtime(state: &Value, backend: &dyn Transport) -> (Value
|
|
|
165
165
|
(Value::Array(issues), Value::Array(repairs))
|
|
166
166
|
}
|
|
167
167
|
|
|
168
|
+
pub(crate) fn diagnose_runtime_for_workspace(
|
|
169
|
+
workspace: &std::path::Path,
|
|
170
|
+
state: &Value,
|
|
171
|
+
backend: &dyn Transport,
|
|
172
|
+
) -> (Value, Value) {
|
|
173
|
+
let (mut issues, mut repairs) = diagnose_runtime(state, backend);
|
|
174
|
+
append_coordinator_health_issue(workspace, state, &mut issues, &mut repairs);
|
|
175
|
+
(issues, repairs)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
fn append_coordinator_health_issue(
|
|
179
|
+
workspace: &std::path::Path,
|
|
180
|
+
state: &Value,
|
|
181
|
+
issues: &mut Value,
|
|
182
|
+
repairs: &mut Value,
|
|
183
|
+
) {
|
|
184
|
+
let workspace = crate::coordinator::WorkspacePath::new(workspace.to_path_buf());
|
|
185
|
+
let health = crate::coordinator::coordinator_health(&workspace);
|
|
186
|
+
let Some(id) = coordinator_issue_id(state, &health) else {
|
|
187
|
+
return;
|
|
188
|
+
};
|
|
189
|
+
if let Some(items) = issues.as_array_mut() {
|
|
190
|
+
items.push(coordinator_issue_value(id, &health, workspace.as_path()));
|
|
191
|
+
}
|
|
192
|
+
if let Some(items) = repairs.as_array_mut() {
|
|
193
|
+
items.push(coordinator_repair_hint(id, &health));
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
fn coordinator_issue_id(
|
|
198
|
+
state: &Value,
|
|
199
|
+
health: &crate::coordinator::HealthReport,
|
|
200
|
+
) -> Option<&'static str> {
|
|
201
|
+
match health.status {
|
|
202
|
+
crate::coordinator::CoordinatorHealthStatus::Stale
|
|
203
|
+
| crate::coordinator::CoordinatorHealthStatus::InvalidPid => {
|
|
204
|
+
Some("coordinator_unavailable")
|
|
205
|
+
}
|
|
206
|
+
crate::coordinator::CoordinatorHealthStatus::Running => {
|
|
207
|
+
if !health.metadata_ok {
|
|
208
|
+
return match health.metadata_mismatch_reason.as_deref() {
|
|
209
|
+
Some(
|
|
210
|
+
"binary_identity_missing"
|
|
211
|
+
| "binary_version_mismatch"
|
|
212
|
+
| "binary_path_mismatch",
|
|
213
|
+
) => Some("coordinator_stale_identity"),
|
|
214
|
+
Some(_) | None => Some("coordinator_unavailable"),
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
if !health.schema.ok {
|
|
218
|
+
Some("coordinator_schema_incompatible")
|
|
219
|
+
} else {
|
|
220
|
+
None
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
crate::coordinator::CoordinatorHealthStatus::Missing => {
|
|
224
|
+
coordinator_expected(state).then_some("coordinator_unavailable")
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
fn coordinator_issue_value(
|
|
230
|
+
id: &str,
|
|
231
|
+
health: &crate::coordinator::HealthReport,
|
|
232
|
+
workspace: &std::path::Path,
|
|
233
|
+
) -> Value {
|
|
234
|
+
json!({
|
|
235
|
+
"id": id,
|
|
236
|
+
"status": coordinator_status_wire(health.status),
|
|
237
|
+
"pid": health.pid.map(|pid| pid.get()),
|
|
238
|
+
"metadata_ok": health.metadata_ok,
|
|
239
|
+
"metadata_mismatch_reason": health.metadata_mismatch_reason.clone(),
|
|
240
|
+
"binary_path": health.current_binary_identity.binary_path.clone(),
|
|
241
|
+
"binary_version": health.current_binary_identity.binary_version.clone(),
|
|
242
|
+
"schema_ok": health.schema.ok,
|
|
243
|
+
"coordinator_log": crate::coordinator::coordinator_log_path(
|
|
244
|
+
&crate::coordinator::WorkspacePath::new(workspace.to_path_buf())
|
|
245
|
+
)
|
|
246
|
+
.to_string_lossy()
|
|
247
|
+
.to_string(),
|
|
248
|
+
})
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
fn coordinator_repair_hint(id: &str, _health: &crate::coordinator::HealthReport) -> Value {
|
|
252
|
+
let hint_action = match id {
|
|
253
|
+
"coordinator_schema_incompatible" => "team-agent repair-state --schema",
|
|
254
|
+
_ => "team-agent restart",
|
|
255
|
+
};
|
|
256
|
+
json!({
|
|
257
|
+
"issue": id,
|
|
258
|
+
"action_required": true,
|
|
259
|
+
"advisory": true,
|
|
260
|
+
"broken_class": id,
|
|
261
|
+
"hint_action": hint_action,
|
|
262
|
+
"dedupe_key": id,
|
|
263
|
+
"action": format!("{hint_action} # diagnose only reports coordinator health; it does not start, stop, or rotate the coordinator"),
|
|
264
|
+
})
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
fn coordinator_expected(state: &Value) -> bool {
|
|
268
|
+
state
|
|
269
|
+
.get("session_name")
|
|
270
|
+
.and_then(Value::as_str)
|
|
271
|
+
.is_some_and(|session| !session.is_empty())
|
|
272
|
+
|| state
|
|
273
|
+
.get("agents")
|
|
274
|
+
.and_then(Value::as_object)
|
|
275
|
+
.is_some_and(|agents| !agents.is_empty())
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
fn coordinator_status_wire(status: crate::coordinator::CoordinatorHealthStatus) -> &'static str {
|
|
279
|
+
match status {
|
|
280
|
+
crate::coordinator::CoordinatorHealthStatus::Missing => "missing",
|
|
281
|
+
crate::coordinator::CoordinatorHealthStatus::InvalidPid => "invalid_pid",
|
|
282
|
+
crate::coordinator::CoordinatorHealthStatus::Running => "running",
|
|
283
|
+
crate::coordinator::CoordinatorHealthStatus::Stale => "stale",
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
168
287
|
fn topology_repair_hint(issue: &str) -> Value {
|
|
169
288
|
json!({
|
|
170
289
|
"issue": issue,
|
|
@@ -246,6 +365,72 @@ mod tests {
|
|
|
246
365
|
path
|
|
247
366
|
}
|
|
248
367
|
|
|
368
|
+
fn coordinator_health_fixture(
|
|
369
|
+
status: crate::coordinator::CoordinatorHealthStatus,
|
|
370
|
+
metadata_mismatch_reason: Option<&str>,
|
|
371
|
+
schema_ok: bool,
|
|
372
|
+
) -> crate::coordinator::HealthReport {
|
|
373
|
+
crate::coordinator::HealthReport {
|
|
374
|
+
ok: matches!(status, crate::coordinator::CoordinatorHealthStatus::Running)
|
|
375
|
+
&& metadata_mismatch_reason.is_none()
|
|
376
|
+
&& schema_ok,
|
|
377
|
+
status,
|
|
378
|
+
pid: Some(crate::coordinator::Pid::new(std::process::id())),
|
|
379
|
+
metadata: None,
|
|
380
|
+
metadata_ok: metadata_mismatch_reason.is_none(),
|
|
381
|
+
metadata_mismatch_reason: metadata_mismatch_reason.map(ToString::to_string),
|
|
382
|
+
current_binary_identity: crate::coordinator::CoordinatorBinaryIdentity {
|
|
383
|
+
binary_path: "/current/team-agent".to_string(),
|
|
384
|
+
binary_version: env!("CARGO_PKG_VERSION").to_string(),
|
|
385
|
+
},
|
|
386
|
+
schema: crate::coordinator::SchemaHealth {
|
|
387
|
+
ok: schema_ok,
|
|
388
|
+
schema_version: crate::db::schema::SCHEMA_VERSION,
|
|
389
|
+
error: None,
|
|
390
|
+
action: None,
|
|
391
|
+
},
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
#[test]
|
|
396
|
+
fn coordinator_issue_id_maps_stale_pid_to_unavailable() {
|
|
397
|
+
let health = coordinator_health_fixture(
|
|
398
|
+
crate::coordinator::CoordinatorHealthStatus::Stale,
|
|
399
|
+
None,
|
|
400
|
+
true,
|
|
401
|
+
);
|
|
402
|
+
assert_eq!(
|
|
403
|
+
coordinator_issue_id(&json!({"session_name": "team-fixture"}), &health),
|
|
404
|
+
Some("coordinator_unavailable")
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
#[test]
|
|
409
|
+
fn coordinator_issue_id_maps_binary_identity_to_stale_identity() {
|
|
410
|
+
let health = coordinator_health_fixture(
|
|
411
|
+
crate::coordinator::CoordinatorHealthStatus::Running,
|
|
412
|
+
Some("binary_version_mismatch"),
|
|
413
|
+
true,
|
|
414
|
+
);
|
|
415
|
+
assert_eq!(
|
|
416
|
+
coordinator_issue_id(&json!({"session_name": "team-fixture"}), &health),
|
|
417
|
+
Some("coordinator_stale_identity")
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
#[test]
|
|
422
|
+
fn coordinator_issue_id_maps_schema_failure_to_schema_incompatible() {
|
|
423
|
+
let health = coordinator_health_fixture(
|
|
424
|
+
crate::coordinator::CoordinatorHealthStatus::Running,
|
|
425
|
+
None,
|
|
426
|
+
false,
|
|
427
|
+
);
|
|
428
|
+
assert_eq!(
|
|
429
|
+
coordinator_issue_id(&json!({"session_name": "team-fixture"}), &health),
|
|
430
|
+
Some("coordinator_schema_incompatible")
|
|
431
|
+
);
|
|
432
|
+
}
|
|
433
|
+
|
|
249
434
|
#[test]
|
|
250
435
|
fn diagnose_surfaces_codex_session_identity_mismatch() {
|
|
251
436
|
let workspace =
|
|
@@ -821,7 +821,11 @@ pub mod lifecycle_port {
|
|
|
821
821
|
let mut coordinator_timeout = false;
|
|
822
822
|
let mut coordinator_post_stop = CoordinatorStopObservation::NotNeeded;
|
|
823
823
|
let mut coordinator_pid_for_report = None;
|
|
824
|
-
let
|
|
824
|
+
let coordinator_stop_reason =
|
|
825
|
+
coordinator_stop_reason_for_shutdown(&run_workspace, team, &state)?;
|
|
826
|
+
let should_stop_coordinator =
|
|
827
|
+
matches!(coordinator_stop_reason, "bare_shutdown" | "scoped_last_live_team");
|
|
828
|
+
let stopped = if should_stop_coordinator {
|
|
825
829
|
let wp = crate::coordinator::WorkspacePath::new(run_workspace.clone());
|
|
826
830
|
let coordinator_pid_before_stop = crate::coordinator::coordinator_health(&wp).pid;
|
|
827
831
|
coordinator_pid_for_report = coordinator_pid_before_stop.map(|pid| pid.get());
|
|
@@ -929,6 +933,7 @@ pub mod lifecycle_port {
|
|
|
929
933
|
"killed_sessions": killed_sessions.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
|
|
930
934
|
"spared_sessions": spared_sessions.iter().map(|s| s.as_str()).collect::<Vec<_>>(),
|
|
931
935
|
"coordinator_status": coordinator_status,
|
|
936
|
+
"coordinator_stop_reason": coordinator_stop_reason,
|
|
932
937
|
"status": status,
|
|
933
938
|
"phase": phase,
|
|
934
939
|
"verification_degraded": verification_degraded,
|
|
@@ -971,9 +976,11 @@ pub mod lifecycle_port {
|
|
|
971
976
|
"owned_files": owned_file_residuals,
|
|
972
977
|
},
|
|
973
978
|
"error": kill_error,
|
|
979
|
+
"coordinator_stop_reason": coordinator_stop_reason,
|
|
974
980
|
"coordinator": {
|
|
975
981
|
"status": coordinator_status,
|
|
976
982
|
"pid": coordinator_pid,
|
|
983
|
+
"stop_reason": coordinator_stop_reason,
|
|
977
984
|
},
|
|
978
985
|
});
|
|
979
986
|
#[cfg(windows)]
|
|
@@ -2921,6 +2928,7 @@ pub mod lifecycle_port {
|
|
|
2921
2928
|
session_name,
|
|
2922
2929
|
agents,
|
|
2923
2930
|
coordinator_started,
|
|
2931
|
+
coordinator,
|
|
2924
2932
|
next_actions,
|
|
2925
2933
|
attach_commands,
|
|
2926
2934
|
} => json!({
|
|
@@ -2929,6 +2937,7 @@ pub mod lifecycle_port {
|
|
|
2929
2937
|
"session_name": session_name.as_str(),
|
|
2930
2938
|
"agents": agents.iter().map(|a| a.agent_id.as_str()).collect::<Vec<_>>(),
|
|
2931
2939
|
"coordinator_started": coordinator_started,
|
|
2940
|
+
"coordinator": crate::lifecycle::coordinator_start_summary_value(&coordinator),
|
|
2932
2941
|
"next_actions": next_actions,
|
|
2933
2942
|
"attach_commands": attach_commands,
|
|
2934
2943
|
"reminder": crate::cli::QUICK_START_REMINDER,
|
|
@@ -2938,6 +2947,7 @@ pub mod lifecycle_port {
|
|
|
2938
2947
|
agents,
|
|
2939
2948
|
failed_agents,
|
|
2940
2949
|
coordinator_started,
|
|
2950
|
+
coordinator,
|
|
2941
2951
|
next_actions,
|
|
2942
2952
|
attach_commands,
|
|
2943
2953
|
} => json!({
|
|
@@ -2964,6 +2974,7 @@ pub mod lifecycle_port {
|
|
|
2964
2974
|
),
|
|
2965
2975
|
})).collect::<Vec<_>>(),
|
|
2966
2976
|
"coordinator_started": coordinator_started,
|
|
2977
|
+
"coordinator": crate::lifecycle::coordinator_start_summary_value(&coordinator),
|
|
2967
2978
|
"next_actions": next_actions,
|
|
2968
2979
|
"attach_commands": attach_commands,
|
|
2969
2980
|
"reminder": crate::cli::QUICK_START_REMINDER,
|
|
@@ -3370,6 +3381,35 @@ pub mod lifecycle_port {
|
|
|
3370
3381
|
}
|
|
3371
3382
|
}
|
|
3372
3383
|
|
|
3384
|
+
fn coordinator_stop_reason_for_shutdown(
|
|
3385
|
+
workspace: &Path,
|
|
3386
|
+
team: Option<&str>,
|
|
3387
|
+
selected_state: &Value,
|
|
3388
|
+
) -> Result<&'static str, CliError> {
|
|
3389
|
+
let Some(requested_team) = team.filter(|team| !team.is_empty()) else {
|
|
3390
|
+
return Ok("bare_shutdown");
|
|
3391
|
+
};
|
|
3392
|
+
let stopped_key = selected_state
|
|
3393
|
+
.get("active_team_key")
|
|
3394
|
+
.and_then(Value::as_str)
|
|
3395
|
+
.filter(|key| !key.is_empty())
|
|
3396
|
+
.unwrap_or(requested_team);
|
|
3397
|
+
let raw = crate::state::persist::load_runtime_state(workspace)?;
|
|
3398
|
+
let live_sibling = raw
|
|
3399
|
+
.get("teams")
|
|
3400
|
+
.and_then(Value::as_object)
|
|
3401
|
+
.is_some_and(|teams| {
|
|
3402
|
+
teams
|
|
3403
|
+
.iter()
|
|
3404
|
+
.any(|(key, team)| key.as_str() != stopped_key && team_has_running_agent(team))
|
|
3405
|
+
});
|
|
3406
|
+
if live_sibling {
|
|
3407
|
+
Ok("scoped_live_sibling_present")
|
|
3408
|
+
} else {
|
|
3409
|
+
Ok("scoped_last_live_team")
|
|
3410
|
+
}
|
|
3411
|
+
}
|
|
3412
|
+
|
|
3373
3413
|
fn mark_matching_session_teams_stopped(
|
|
3374
3414
|
state: &mut Value,
|
|
3375
3415
|
session_name: Option<&crate::transport::SessionName>,
|
|
@@ -3915,10 +3955,15 @@ pub mod diagnose_port {
|
|
|
3915
3955
|
"pid": m.pid.get(),
|
|
3916
3956
|
"protocol_version": m.protocol_version,
|
|
3917
3957
|
"message_store_schema_version": m.message_store_schema_version,
|
|
3958
|
+
"binary_path": m.binary_path,
|
|
3959
|
+
"binary_version": m.binary_version,
|
|
3918
3960
|
"source": m.source,
|
|
3919
3961
|
"updated_at": m.updated_at,
|
|
3920
3962
|
})),
|
|
3921
3963
|
"metadata_ok": health.metadata_ok,
|
|
3964
|
+
"metadata_mismatch_reason": health.metadata_mismatch_reason,
|
|
3965
|
+
"binary_path": health.current_binary_identity.binary_path,
|
|
3966
|
+
"binary_version": health.current_binary_identity.binary_version,
|
|
3922
3967
|
"schema_ok": health.schema.ok,
|
|
3923
3968
|
"schema_error": health.schema.error.map(|e| format!("{e:?}")),
|
|
3924
3969
|
"schema": {
|
|
@@ -1151,10 +1151,15 @@ use rusqlite::params;
|
|
|
1151
1151
|
"pid": m.pid.get(),
|
|
1152
1152
|
"protocol_version": m.protocol_version,
|
|
1153
1153
|
"message_store_schema_version": m.message_store_schema_version,
|
|
1154
|
+
"binary_path": m.binary_path,
|
|
1155
|
+
"binary_version": m.binary_version,
|
|
1154
1156
|
"source": m.source,
|
|
1155
1157
|
"updated_at": m.updated_at,
|
|
1156
1158
|
})),
|
|
1157
1159
|
"metadata_ok": health.metadata_ok,
|
|
1160
|
+
"metadata_mismatch_reason": health.metadata_mismatch_reason,
|
|
1161
|
+
"binary_path": health.current_binary_identity.binary_path,
|
|
1162
|
+
"binary_version": health.current_binary_identity.binary_version,
|
|
1158
1163
|
"schema_ok": health.schema.ok,
|
|
1159
1164
|
"schema_error": health.schema.error.map(|e| format!("{e:?}")),
|
|
1160
1165
|
"schema": {
|
|
@@ -57,20 +57,30 @@ use super::*;
|
|
|
57
57
|
let _ = std::fs::remove_dir_all(&ws);
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
-
// ── status #3: coordinator = coordinator_health(ws) FULL
|
|
61
|
-
//
|
|
62
|
-
// RUST mod.rs:105-112 emits a 6-key subset {status,ok,pid,metadata_ok,schema_ok,schema_version}
|
|
63
|
-
// (no metadata/schema_error; flat schema_version instead of nested schema). RED on key-order. ──────
|
|
60
|
+
// ── status #3: coordinator = coordinator_health(ws) FULL shape in golden order ──────────────────
|
|
61
|
+
// 0.5.18 adds binary identity diagnostics to expose stale coordinator rotation reasons.
|
|
64
62
|
#[test]
|
|
65
|
-
fn
|
|
63
|
+
fn status_coordinator_is_full_health_identity_shape() {
|
|
66
64
|
let ws = tmp_workspace();
|
|
67
65
|
let v = status_port::status(&ws, false, true).expect("status");
|
|
68
66
|
let coord = v["coordinator"].as_object().expect("coordinator dict");
|
|
69
67
|
let order: Vec<&str> = coord.keys().map(String::as_str).collect();
|
|
70
68
|
assert_eq!(
|
|
71
69
|
order,
|
|
72
|
-
vec![
|
|
73
|
-
|
|
70
|
+
vec![
|
|
71
|
+
"ok",
|
|
72
|
+
"status",
|
|
73
|
+
"pid",
|
|
74
|
+
"metadata",
|
|
75
|
+
"metadata_ok",
|
|
76
|
+
"metadata_mismatch_reason",
|
|
77
|
+
"binary_path",
|
|
78
|
+
"binary_version",
|
|
79
|
+
"schema_ok",
|
|
80
|
+
"schema_error",
|
|
81
|
+
"schema"
|
|
82
|
+
],
|
|
83
|
+
"golden coordinator_health insertion order with 0.5.18 identity fields; got {order:?}"
|
|
74
84
|
);
|
|
75
85
|
assert!(
|
|
76
86
|
coord.get("schema_version").is_none(),
|
|
@@ -61,18 +61,14 @@ pub fn run_daemon(args: DaemonArgs) -> Result<(), DaemonError> {
|
|
|
61
61
|
// seed-state trap where writing active_team_key to state ahead
|
|
62
62
|
// of coord spawn made downstream launch code think "existing
|
|
63
63
|
// runtime, use restart" and skip spec compile.
|
|
64
|
-
let team_key_from_state = args
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
.and_then(|v| v.as_str())
|
|
73
|
-
.filter(|s| !s.is_empty())
|
|
74
|
-
.map(str::to_string)
|
|
75
|
-
});
|
|
64
|
+
let team_key_from_state = args.team_key.clone().filter(|s| !s.is_empty()).or_else(|| {
|
|
65
|
+
state
|
|
66
|
+
.as_ref()
|
|
67
|
+
.and_then(|s| s.get("active_team_key"))
|
|
68
|
+
.and_then(|v| v.as_str())
|
|
69
|
+
.filter(|s| !s.is_empty())
|
|
70
|
+
.map(str::to_string)
|
|
71
|
+
});
|
|
76
72
|
let mut factory_input = crate::transport_factory::TransportFactoryInput::new(
|
|
77
73
|
args.workspace.as_path(),
|
|
78
74
|
crate::transport_factory::TransportPurpose::Coordinator,
|
|
@@ -104,7 +100,8 @@ pub fn run_daemon(args: DaemonArgs) -> Result<(), DaemonError> {
|
|
|
104
100
|
args.workspace.clone(),
|
|
105
101
|
Box::new(RealProviderRegistry),
|
|
106
102
|
Box::new(sel.backend),
|
|
107
|
-
)
|
|
103
|
+
)
|
|
104
|
+
.with_team_key(team_key_from_state.clone());
|
|
108
105
|
return run_daemon_with_coordinator_and_boot_tmux(&args, &coord, Some(metadata));
|
|
109
106
|
}
|
|
110
107
|
};
|
|
@@ -120,7 +117,8 @@ pub fn run_daemon(args: DaemonArgs) -> Result<(), DaemonError> {
|
|
|
120
117
|
args.workspace.clone(),
|
|
121
118
|
Box::new(RealProviderRegistry),
|
|
122
119
|
resolved.backend,
|
|
123
|
-
)
|
|
120
|
+
)
|
|
121
|
+
.with_team_key(team_key_from_state.clone());
|
|
124
122
|
run_daemon_with_coordinator_and_boot_tmux(&args, &coordinator, Some(tmux_metadata))
|
|
125
123
|
}
|
|
126
124
|
|
|
@@ -147,11 +145,14 @@ fn run_daemon_with_coordinator_and_boot_tmux(
|
|
|
147
145
|
let pid = Pid::new(std::process::id());
|
|
148
146
|
std::fs::write(coordinator_pid_path(&args.workspace), pid.to_string())?;
|
|
149
147
|
write_coordinator_metadata(&args.workspace, pid, MetadataSource::Boot)?;
|
|
148
|
+
let binary_identity = crate::coordinator::current_coordinator_binary_identity();
|
|
150
149
|
|
|
151
150
|
let event_log = EventLog::new(args.workspace.as_path());
|
|
152
151
|
let mut boot_event = serde_json::json!({
|
|
153
152
|
"workspace": args.workspace.as_path().to_string_lossy(),
|
|
154
153
|
"once": args.once,
|
|
154
|
+
"binary_path": binary_identity.binary_path,
|
|
155
|
+
"binary_version": binary_identity.binary_version,
|
|
155
156
|
});
|
|
156
157
|
if let Some(metadata) = tmux_metadata {
|
|
157
158
|
if let Some(object) = boot_event.as_object_mut() {
|