@team-agent/installer 0.5.110 → 0.5.112
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 +231 -812
- package/crates/team-agent/src/cli/diagnose.rs +2 -424
- package/crates/team-agent/src/cli/emit.rs +493 -451
- package/crates/team-agent/src/cli/leader.rs +4 -9
- package/crates/team-agent/src/cli/leaders.rs +90 -29
- package/crates/team-agent/src/cli/mod.rs +348 -169
- package/crates/team-agent/src/cli/models.rs +80 -24
- package/crates/team-agent/src/cli/named_address.rs +114 -25
- package/crates/team-agent/src/cli/route.rs +10 -5
- package/crates/team-agent/src/cli/send/presentation.rs +60 -41
- package/crates/team-agent/src/cli/send/resolve.rs +25 -18
- package/crates/team-agent/src/cli/send.rs +4 -4
- package/crates/team-agent/src/cli/spec.rs +53 -109
- package/crates/team-agent/src/cli/tests/base.rs +189 -82
- package/crates/team-agent/src/cli/tests/lane_c.rs +1 -2
- package/crates/team-agent/src/cli/tests/leader_watch.rs +2 -86
- package/crates/team-agent/src/cli/tests/main_preserved.rs +15 -140
- package/crates/team-agent/src/cli/tests/missing_subcommands.rs +1 -258
- package/crates/team-agent/src/cli/tests/mod.rs +0 -2
- package/crates/team-agent/src/cli/tests/named_address.rs +19 -22
- package/crates/team-agent/src/cli/tests/status_send.rs +15 -10
- package/crates/team-agent/src/cli/tests/verb_install_skill.rs +2 -2
- package/crates/team-agent/src/cli/types.rs +26 -121
- package/crates/team-agent/src/coordinator/health.rs +3 -283
- package/crates/team-agent/src/coordinator/mod.rs +0 -5
- package/crates/team-agent/src/coordinator/tests/basics.rs +0 -9
- package/crates/team-agent/src/coordinator/tests/mod.rs +0 -1
- package/crates/team-agent/src/coordinator/tests/tick_core.rs +0 -17
- package/crates/team-agent/src/coordinator/types.rs +1 -21
- package/crates/team-agent/src/db/message_store.rs +0 -42
- package/crates/team-agent/src/db/migration.rs +49 -9
- package/crates/team-agent/src/db/schema.rs +27 -20
- package/crates/team-agent/src/leader/owner_bind.rs +0 -24
- package/crates/team-agent/src/leader/registry.rs +3 -3
- package/crates/team-agent/src/leader/tests/byte_findings.rs +0 -20
- package/crates/team-agent/src/leader/tests/identity.rs +0 -32
- package/crates/team-agent/src/leader/tests/lease_claim.rs +0 -55
- package/crates/team-agent/src/lifecycle/tests/pi_dual_entry_red.rs +4 -9
- package/crates/team-agent/src/lifecycle/tests/restart_session_capture_red.rs +2 -51
- package/crates/team-agent/src/lifecycle/tests/stale_team_saveconflict_contract.rs +3 -1
- package/crates/team-agent/src/lifecycle/tests/status_credential_redaction_contract.rs +23 -23
- package/crates/team-agent/src/lifecycle/tests/subprep_codex_trust_roundtrip_red.rs +29 -55
- package/crates/team-agent/src/messaging/mod.rs +0 -2
- package/crates/team-agent/src/messaging/results.rs +0 -58
- package/crates/team-agent/src/messaging/tests/runtime.rs +0 -46
- package/crates/team-agent/src/state/repository.rs +1 -5
- package/package.json +4 -4
- package/templates/team_state.md +1 -1
- package/crates/team-agent/src/cli/tests/peer_allow.rs +0 -164
- package/crates/team-agent/src/cli/tests/verb_validate.rs +0 -195
- package/crates/team-agent/src/coordinator/tests/watch.rs +0 -223
- package/crates/team-agent/src/messaging/peers.rs +0 -34
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//!
|
|
2
|
-
//! diagnose/
|
|
2
|
+
//! diagnose/doctor CLI helpers.
|
|
3
3
|
use super::*;
|
|
4
4
|
use crate::model::pane_authority_refusal::{
|
|
5
5
|
PaneAuthorityRecoveryAction, PaneAuthorityRecoveryHint, PaneAuthorityRefusal,
|
|
@@ -1159,7 +1159,6 @@ mod tests {
|
|
|
1159
1159
|
);
|
|
1160
1160
|
}
|
|
1161
1161
|
|
|
1162
|
-
|
|
1163
1162
|
fn write_codex_identity_rollout(
|
|
1164
1163
|
workspace: &std::path::Path,
|
|
1165
1164
|
session_id: &str,
|
|
@@ -1305,123 +1304,6 @@ mod tests {
|
|
|
1305
1304
|
}
|
|
1306
1305
|
}
|
|
1307
1306
|
|
|
1308
|
-
pub(crate) fn build_preflight_report(team: &std::path::Path) -> Result<Value, CliError> {
|
|
1309
|
-
let mut checks = Vec::new();
|
|
1310
|
-
let mut next_actions = Vec::new();
|
|
1311
|
-
|
|
1312
|
-
let compiled = match crate::compiler::compile_team(team) {
|
|
1313
|
-
Ok(spec) => {
|
|
1314
|
-
checks.push(json!({
|
|
1315
|
-
"name": "compile",
|
|
1316
|
-
"ok": true,
|
|
1317
|
-
"agents": compiled_agent_ids(&spec),
|
|
1318
|
-
}));
|
|
1319
|
-
Some(spec)
|
|
1320
|
-
}
|
|
1321
|
-
Err(error) => {
|
|
1322
|
-
checks.push(json!({
|
|
1323
|
-
"name": "compile",
|
|
1324
|
-
"ok": false,
|
|
1325
|
-
"error": error.to_string(),
|
|
1326
|
-
}));
|
|
1327
|
-
next_actions.push(json!(
|
|
1328
|
-
"fix TEAM.md and role front matter, then run preflight again"
|
|
1329
|
-
));
|
|
1330
|
-
None
|
|
1331
|
-
}
|
|
1332
|
-
};
|
|
1333
|
-
|
|
1334
|
-
let tmux_path = command_path("tmux");
|
|
1335
|
-
checks.push(json!({
|
|
1336
|
-
"name": "tmux",
|
|
1337
|
-
"ok": tmux_path.is_some(),
|
|
1338
|
-
"path": tmux_path,
|
|
1339
|
-
}));
|
|
1340
|
-
if tmux_path.is_none() {
|
|
1341
|
-
next_actions.push(json!("install tmux or add it to PATH"));
|
|
1342
|
-
}
|
|
1343
|
-
|
|
1344
|
-
let workspace = crate::model::paths::team_workspace(team)
|
|
1345
|
-
.map_err(|error| CliError::Runtime(error.to_string()))?;
|
|
1346
|
-
let profile_dir_exists = workspace
|
|
1347
|
-
.join(".team")
|
|
1348
|
-
.join("current")
|
|
1349
|
-
.join("profiles")
|
|
1350
|
-
.exists()
|
|
1351
|
-
|| team.join("profiles").exists();
|
|
1352
|
-
let profile_dir_check = json!({
|
|
1353
|
-
"name": "profile_dir",
|
|
1354
|
-
"ok": true,
|
|
1355
|
-
"status": if profile_dir_exists { "present" } else { "not_required" },
|
|
1356
|
-
});
|
|
1357
|
-
let profile_smoke_check = build_profile_smoke_check_for_team(team)?;
|
|
1358
|
-
if profile_smoke_check.get("ok").and_then(Value::as_bool) == Some(false) {
|
|
1359
|
-
let reason = profile_smoke_check
|
|
1360
|
-
.get("reason")
|
|
1361
|
-
.or_else(|| profile_smoke_check.get("error"))
|
|
1362
|
-
.and_then(Value::as_str)
|
|
1363
|
-
.unwrap_or("profile smoke failed");
|
|
1364
|
-
next_actions.push(json!(format!("fix compatible_api profile smoke: {reason}")));
|
|
1365
|
-
}
|
|
1366
|
-
checks.push(json!({
|
|
1367
|
-
"name": "profiles",
|
|
1368
|
-
"ok": true,
|
|
1369
|
-
"checks": compact_profile_checks(team),
|
|
1370
|
-
}));
|
|
1371
|
-
checks.push(profile_smoke_check);
|
|
1372
|
-
checks.push(if compiled.is_some() {
|
|
1373
|
-
json!({
|
|
1374
|
-
"name": "models",
|
|
1375
|
-
"ok": true,
|
|
1376
|
-
})
|
|
1377
|
-
} else {
|
|
1378
|
-
json!({
|
|
1379
|
-
"name": "models",
|
|
1380
|
-
"ok": true,
|
|
1381
|
-
"status": "skipped",
|
|
1382
|
-
})
|
|
1383
|
-
});
|
|
1384
|
-
checks.push(json!({
|
|
1385
|
-
"name": "core_runtime",
|
|
1386
|
-
"ok": true,
|
|
1387
|
-
"status": core_runtime_status(),
|
|
1388
|
-
}));
|
|
1389
|
-
checks.push(profile_dir_check);
|
|
1390
|
-
|
|
1391
|
-
let blockers = preflight_blockers(&checks);
|
|
1392
|
-
let ok = blockers.is_empty();
|
|
1393
|
-
let summary = if ok {
|
|
1394
|
-
"preflight passed".to_string()
|
|
1395
|
-
} else {
|
|
1396
|
-
format!(
|
|
1397
|
-
"preflight found blockers: {}",
|
|
1398
|
-
blocker_names(&blockers).join(", ")
|
|
1399
|
-
)
|
|
1400
|
-
};
|
|
1401
|
-
let checks_value = Value::Array(checks);
|
|
1402
|
-
let details_log = write_details_log(
|
|
1403
|
-
team,
|
|
1404
|
-
"preflight",
|
|
1405
|
-
&json!({
|
|
1406
|
-
"ok": ok,
|
|
1407
|
-
"summary": summary,
|
|
1408
|
-
"checks": checks_value,
|
|
1409
|
-
"blockers": blockers,
|
|
1410
|
-
"next_actions": next_actions,
|
|
1411
|
-
}),
|
|
1412
|
-
)?;
|
|
1413
|
-
let report = json!({
|
|
1414
|
-
"blockers": blockers,
|
|
1415
|
-
"checks": checks_value,
|
|
1416
|
-
"details_log": details_log.to_string_lossy().to_string(),
|
|
1417
|
-
"next_actions": next_actions,
|
|
1418
|
-
"ok": ok,
|
|
1419
|
-
"summary": summary,
|
|
1420
|
-
});
|
|
1421
|
-
let _ = crate::event_log::EventLog::new(team).write("preflight.complete", report.clone());
|
|
1422
|
-
Ok(report)
|
|
1423
|
-
}
|
|
1424
|
-
|
|
1425
1307
|
pub(crate) fn build_profile_smoke_check_for_team(
|
|
1426
1308
|
team: &std::path::Path,
|
|
1427
1309
|
) -> Result<Value, CliError> {
|
|
@@ -1519,179 +1401,7 @@ fn copy_optional_field(from: &Value, to: &mut Value, key: &str) {
|
|
|
1519
1401
|
obj.insert(key.to_string(), value);
|
|
1520
1402
|
}
|
|
1521
1403
|
|
|
1522
|
-
|
|
1523
|
-
workspace: &std::path::Path,
|
|
1524
|
-
timeout: f64,
|
|
1525
|
-
team: Option<&str>,
|
|
1526
|
-
) -> Result<Value, CliError> {
|
|
1527
|
-
// swallow batch 3 ③: an unreadable runtime state must never read as "ready" — the
|
|
1528
|
-
// read error is surfaced verbatim (state_read_error) with ready=false instead of
|
|
1529
|
-
// silently degrading to an empty/stale state.
|
|
1530
|
-
let selected = match crate::state::selector::resolve_active_team(
|
|
1531
|
-
workspace,
|
|
1532
|
-
team,
|
|
1533
|
-
crate::state::selector::SelectorMode::RuntimeOnly,
|
|
1534
|
-
) {
|
|
1535
|
-
Ok(selected) => selected,
|
|
1536
|
-
Err(error) => {
|
|
1537
|
-
return Ok(json!({
|
|
1538
|
-
"ok": false,
|
|
1539
|
-
"status": "error",
|
|
1540
|
-
"reason": "state_read_error",
|
|
1541
|
-
"state_read_error": error.to_string(),
|
|
1542
|
-
"readiness": {"ready": false},
|
|
1543
|
-
"summary": "runtime state could not be read",
|
|
1544
|
-
"next_actions": [json!("inspect .team/runtime/state.json (corrupt or unreadable) and retry")],
|
|
1545
|
-
}));
|
|
1546
|
-
}
|
|
1547
|
-
};
|
|
1548
|
-
let timeout = if timeout.is_finite() && timeout > 0.0 {
|
|
1549
|
-
timeout
|
|
1550
|
-
} else {
|
|
1551
|
-
0.0
|
|
1552
|
-
};
|
|
1553
|
-
let deadline = std::time::Instant::now() + std::time::Duration::from_secs_f64(timeout);
|
|
1554
|
-
let mut readiness;
|
|
1555
|
-
let mut state_read_error: Option<String>;
|
|
1556
|
-
loop {
|
|
1557
|
-
let mut state = match crate::state::projection::select_runtime_state(
|
|
1558
|
-
&selected.run_workspace,
|
|
1559
|
-
Some(&selected.team_key),
|
|
1560
|
-
) {
|
|
1561
|
-
Ok(state) => {
|
|
1562
|
-
state_read_error = None;
|
|
1563
|
-
state
|
|
1564
|
-
}
|
|
1565
|
-
Err(error) => {
|
|
1566
|
-
state_read_error = Some(error.to_string());
|
|
1567
|
-
readiness = json!({"ready": false, "state_read_error": error.to_string()});
|
|
1568
|
-
break;
|
|
1569
|
-
}
|
|
1570
|
-
};
|
|
1571
|
-
inject_tmux_session_present(&selected.run_workspace, &mut state);
|
|
1572
|
-
inject_message_counts(&selected.run_workspace, &mut state)?;
|
|
1573
|
-
readiness = wait_readiness(&state);
|
|
1574
|
-
let awaiting_trust = readiness
|
|
1575
|
-
.get("awaiting_trust_prompt")
|
|
1576
|
-
.and_then(Value::as_bool)
|
|
1577
|
-
== Some(true);
|
|
1578
|
-
let ready = readiness.get("ready").and_then(Value::as_bool) == Some(true);
|
|
1579
|
-
if awaiting_trust || ready || std::time::Instant::now() >= deadline {
|
|
1580
|
-
break;
|
|
1581
|
-
}
|
|
1582
|
-
std::thread::sleep(std::time::Duration::from_millis(100));
|
|
1583
|
-
}
|
|
1584
|
-
let awaiting_trust = readiness
|
|
1585
|
-
.get("awaiting_trust_prompt")
|
|
1586
|
-
.and_then(Value::as_bool)
|
|
1587
|
-
== Some(true);
|
|
1588
|
-
let ready = readiness.get("ready").and_then(Value::as_bool) == Some(true);
|
|
1589
|
-
let (ok, status, reason, summary, next_actions) = if state_read_error.is_some() {
|
|
1590
|
-
(
|
|
1591
|
-
false,
|
|
1592
|
-
"error",
|
|
1593
|
-
"state_read_error",
|
|
1594
|
-
"runtime state could not be read",
|
|
1595
|
-
vec![json!(
|
|
1596
|
-
"inspect .team/runtime/state.json (corrupt or unreadable) and retry"
|
|
1597
|
-
)],
|
|
1598
|
-
)
|
|
1599
|
-
} else if awaiting_trust {
|
|
1600
|
-
(
|
|
1601
|
-
false,
|
|
1602
|
-
"pending",
|
|
1603
|
-
"awaiting_trust_prompt",
|
|
1604
|
-
"workers are awaiting trust prompt",
|
|
1605
|
-
vec![json!(
|
|
1606
|
-
"answer the provider trust prompt, then run wait-ready again"
|
|
1607
|
-
)],
|
|
1608
|
-
)
|
|
1609
|
-
} else if ready {
|
|
1610
|
-
(true, "ready", "ready", "workers ready", Vec::new())
|
|
1611
|
-
} else if readiness
|
|
1612
|
-
.get("session_capture_complete")
|
|
1613
|
-
.and_then(Value::as_bool)
|
|
1614
|
-
== Some(false)
|
|
1615
|
-
{
|
|
1616
|
-
(
|
|
1617
|
-
false,
|
|
1618
|
-
"pending",
|
|
1619
|
-
"session_capture_incomplete",
|
|
1620
|
-
"provider session capture is incomplete",
|
|
1621
|
-
vec![json!("wait for provider session capture before restart")],
|
|
1622
|
-
)
|
|
1623
|
-
} else {
|
|
1624
|
-
(
|
|
1625
|
-
false,
|
|
1626
|
-
"timeout",
|
|
1627
|
-
"workers_not_ready",
|
|
1628
|
-
"workers not ready before timeout",
|
|
1629
|
-
vec![json!(
|
|
1630
|
-
"inspect team-agent doctor output and worker terminals"
|
|
1631
|
-
)],
|
|
1632
|
-
)
|
|
1633
|
-
};
|
|
1634
|
-
let details_log = write_details_log(
|
|
1635
|
-
&selected.run_workspace,
|
|
1636
|
-
"wait-ready",
|
|
1637
|
-
&json!({
|
|
1638
|
-
"ok": ok,
|
|
1639
|
-
"status": status,
|
|
1640
|
-
"reason": reason,
|
|
1641
|
-
"timeout": timeout,
|
|
1642
|
-
"readiness": readiness,
|
|
1643
|
-
}),
|
|
1644
|
-
)?;
|
|
1645
|
-
let mut report = json!({
|
|
1646
|
-
"details_log": details_log.to_string_lossy().to_string(),
|
|
1647
|
-
"next_actions": next_actions,
|
|
1648
|
-
"ok": ok,
|
|
1649
|
-
"reason": reason,
|
|
1650
|
-
"readiness": readiness,
|
|
1651
|
-
"status": status,
|
|
1652
|
-
"summary": summary,
|
|
1653
|
-
});
|
|
1654
|
-
if let Some(error) = state_read_error {
|
|
1655
|
-
report["state_read_error"] = json!(error);
|
|
1656
|
-
}
|
|
1657
|
-
Ok(report)
|
|
1658
|
-
}
|
|
1659
|
-
|
|
1660
|
-
fn inject_tmux_session_present(workspace: &std::path::Path, state: &mut Value) {
|
|
1661
|
-
// Bug #7 (prerelease 0.4.0 gate review §6): probe the SAME endpoint the
|
|
1662
|
-
// runtime actually uses (state.tmux_endpoint / tmux_socket), not the
|
|
1663
|
-
// workspace-hash socket. Otherwise readiness reports `process_started=false`
|
|
1664
|
-
// even though the session is alive on the persisted socket.
|
|
1665
|
-
let Some(session_name) = state
|
|
1666
|
-
.get("session_name")
|
|
1667
|
-
.and_then(Value::as_str)
|
|
1668
|
-
.filter(|s| !s.is_empty())
|
|
1669
|
-
else {
|
|
1670
|
-
return;
|
|
1671
|
-
};
|
|
1672
|
-
let session_name_owned = session_name.to_string();
|
|
1673
|
-
// 0.5.x Phase 1d Batch 3: factory-routed read path. conpty state
|
|
1674
|
-
// hits the shim; tmux state hits the persisted endpoint (byte-
|
|
1675
|
-
// equivalent). Diagnose no longer reports `tmux_session_missing`
|
|
1676
|
-
// for a conpty team just because it can't find a tmux session on
|
|
1677
|
-
// the workspace socket (design §Batch 3 Verification anchor).
|
|
1678
|
-
let resolved = crate::transport_factory::resolve_read_only_transport(
|
|
1679
|
-
workspace,
|
|
1680
|
-
Some(state),
|
|
1681
|
-
crate::transport_factory::TransportPurpose::Diagnose,
|
|
1682
|
-
);
|
|
1683
|
-
let present = match resolved {
|
|
1684
|
-
Ok(r) => r
|
|
1685
|
-
.backend
|
|
1686
|
-
.has_session(&crate::transport::SessionName::new(session_name_owned))
|
|
1687
|
-
.unwrap_or(false),
|
|
1688
|
-
Err(_) => false,
|
|
1689
|
-
};
|
|
1690
|
-
if let Value::Object(map) = state {
|
|
1691
|
-
map.insert("tmux_session_present".to_string(), Value::Bool(present));
|
|
1692
|
-
}
|
|
1693
|
-
}
|
|
1694
|
-
|
|
1404
|
+
/// Shared status facts, independent of any polling command.
|
|
1695
1405
|
pub(crate) fn wait_readiness(state: &Value) -> Value {
|
|
1696
1406
|
let agents = state.get("agents").and_then(Value::as_object);
|
|
1697
1407
|
let mut process_started = false;
|
|
@@ -1767,29 +1477,6 @@ pub(crate) fn wait_readiness(state: &Value) -> Value {
|
|
|
1767
1477
|
})
|
|
1768
1478
|
}
|
|
1769
1479
|
|
|
1770
|
-
fn inject_message_counts(workspace: &std::path::Path, state: &mut Value) -> Result<(), CliError> {
|
|
1771
|
-
let store = crate::message_store::MessageStore::open(workspace)
|
|
1772
|
-
.map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
1773
|
-
let conn = crate::db::schema::open_db(store.db_path())
|
|
1774
|
-
.map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
1775
|
-
let mut stmt = conn
|
|
1776
|
-
.prepare("select status, count(*) from messages group by status order by status")
|
|
1777
|
-
.map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
1778
|
-
let mut rows = stmt
|
|
1779
|
-
.query([])
|
|
1780
|
-
.map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
1781
|
-
let mut messages = Map::new();
|
|
1782
|
-
while let Some(row) = rows.next().map_err(|e| CliError::Runtime(e.to_string()))? {
|
|
1783
|
-
let status: String = row.get(0).map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
1784
|
-
let count: i64 = row.get(1).map_err(|e| CliError::Runtime(e.to_string()))?;
|
|
1785
|
-
messages.insert(status, json!(count));
|
|
1786
|
-
}
|
|
1787
|
-
if let Some(obj) = state.as_object_mut() {
|
|
1788
|
-
obj.insert("messages".to_string(), Value::Object(messages));
|
|
1789
|
-
}
|
|
1790
|
-
Ok(())
|
|
1791
|
-
}
|
|
1792
|
-
|
|
1793
1480
|
fn message_counts_positive(value: Option<&Value>) -> bool {
|
|
1794
1481
|
let Some(Value::Object(counts)) = value else {
|
|
1795
1482
|
return false;
|
|
@@ -1839,98 +1526,6 @@ fn agent_mcp_ready(agent: &Value) -> bool {
|
|
|
1839
1526
|
})
|
|
1840
1527
|
}
|
|
1841
1528
|
|
|
1842
|
-
fn command_path(command: &str) -> Option<String> {
|
|
1843
|
-
let output = std::process::Command::new("which")
|
|
1844
|
-
.arg(command)
|
|
1845
|
-
.output()
|
|
1846
|
-
.ok()?;
|
|
1847
|
-
if !output.status.success() {
|
|
1848
|
-
return None;
|
|
1849
|
-
}
|
|
1850
|
-
let path = String::from_utf8_lossy(&output.stdout).trim().to_string();
|
|
1851
|
-
if path.is_empty() {
|
|
1852
|
-
None
|
|
1853
|
-
} else {
|
|
1854
|
-
Some(path)
|
|
1855
|
-
}
|
|
1856
|
-
}
|
|
1857
|
-
|
|
1858
|
-
fn compact_profile_checks(team: &std::path::Path) -> Vec<Value> {
|
|
1859
|
-
let profiles = team.join("profiles");
|
|
1860
|
-
let Ok(entries) = std::fs::read_dir(&profiles) else {
|
|
1861
|
-
return Vec::new();
|
|
1862
|
-
};
|
|
1863
|
-
let mut checks = Vec::new();
|
|
1864
|
-
for entry in entries.filter_map(std::result::Result::ok) {
|
|
1865
|
-
let path = entry.path();
|
|
1866
|
-
if !path.is_file() {
|
|
1867
|
-
continue;
|
|
1868
|
-
}
|
|
1869
|
-
let name = path
|
|
1870
|
-
.file_stem()
|
|
1871
|
-
.and_then(|stem| stem.to_str())
|
|
1872
|
-
.unwrap_or("profile")
|
|
1873
|
-
.to_string();
|
|
1874
|
-
checks.push(json!({
|
|
1875
|
-
"name": name,
|
|
1876
|
-
"ok": true,
|
|
1877
|
-
"path": path.to_string_lossy().to_string(),
|
|
1878
|
-
}));
|
|
1879
|
-
}
|
|
1880
|
-
checks
|
|
1881
|
-
}
|
|
1882
|
-
|
|
1883
|
-
fn core_runtime_status() -> &'static str {
|
|
1884
|
-
if std::env::current_exe().is_ok() {
|
|
1885
|
-
"available"
|
|
1886
|
-
} else {
|
|
1887
|
-
"python_fallback"
|
|
1888
|
-
}
|
|
1889
|
-
}
|
|
1890
|
-
|
|
1891
|
-
fn compiled_agent_ids(spec: &crate::model::yaml::Value) -> Vec<String> {
|
|
1892
|
-
spec.get("agents")
|
|
1893
|
-
.and_then(crate::model::yaml::Value::as_list)
|
|
1894
|
-
.map(|agents| {
|
|
1895
|
-
agents
|
|
1896
|
-
.iter()
|
|
1897
|
-
.filter_map(|agent| agent.get("id").and_then(crate::model::yaml::Value::as_str))
|
|
1898
|
-
.map(ToString::to_string)
|
|
1899
|
-
.collect()
|
|
1900
|
-
})
|
|
1901
|
-
.unwrap_or_default()
|
|
1902
|
-
}
|
|
1903
|
-
|
|
1904
|
-
fn preflight_blockers(checks: &[Value]) -> Vec<Value> {
|
|
1905
|
-
checks
|
|
1906
|
-
.iter()
|
|
1907
|
-
.filter(|check| check.get("ok").and_then(Value::as_bool) == Some(false))
|
|
1908
|
-
.map(|check| {
|
|
1909
|
-
let name = check
|
|
1910
|
-
.get("name")
|
|
1911
|
-
.and_then(Value::as_str)
|
|
1912
|
-
.unwrap_or("unknown");
|
|
1913
|
-
let reason = check
|
|
1914
|
-
.get("error")
|
|
1915
|
-
.or_else(|| check.get("reason"))
|
|
1916
|
-
.and_then(Value::as_str)
|
|
1917
|
-
.unwrap_or("failed");
|
|
1918
|
-
json!({
|
|
1919
|
-
"name": name,
|
|
1920
|
-
"reason": reason,
|
|
1921
|
-
})
|
|
1922
|
-
})
|
|
1923
|
-
.collect()
|
|
1924
|
-
}
|
|
1925
|
-
|
|
1926
|
-
fn blocker_names(blockers: &[Value]) -> Vec<String> {
|
|
1927
|
-
blockers
|
|
1928
|
-
.iter()
|
|
1929
|
-
.filter_map(|blocker| blocker.get("name").and_then(Value::as_str))
|
|
1930
|
-
.map(ToString::to_string)
|
|
1931
|
-
.collect()
|
|
1932
|
-
}
|
|
1933
|
-
|
|
1934
1529
|
fn yaml_path_str<'a>(value: &'a crate::model::yaml::Value, keys: &[&str]) -> Option<&'a str> {
|
|
1935
1530
|
let mut current = value;
|
|
1936
1531
|
for key in keys {
|
|
@@ -1939,23 +1534,6 @@ fn yaml_path_str<'a>(value: &'a crate::model::yaml::Value, keys: &[&str]) -> Opt
|
|
|
1939
1534
|
current.as_str()
|
|
1940
1535
|
}
|
|
1941
1536
|
|
|
1942
|
-
fn write_details_log(
|
|
1943
|
-
workspace: &std::path::Path,
|
|
1944
|
-
prefix: &str,
|
|
1945
|
-
value: &Value,
|
|
1946
|
-
) -> Result<std::path::PathBuf, CliError> {
|
|
1947
|
-
let logs = workspace.join(".team").join("logs");
|
|
1948
|
-
std::fs::create_dir_all(&logs)?;
|
|
1949
|
-
let path = logs.join(format!("{prefix}-{}.json", timestamp_slug()));
|
|
1950
|
-
let bytes = serde_json::to_vec_pretty(value)?;
|
|
1951
|
-
std::fs::write(&path, bytes)?;
|
|
1952
|
-
Ok(path)
|
|
1953
|
-
}
|
|
1954
|
-
|
|
1955
|
-
fn timestamp_slug() -> String {
|
|
1956
|
-
chrono::Utc::now().timestamp().to_string()
|
|
1957
|
-
}
|
|
1958
|
-
|
|
1959
1537
|
pub(crate) fn count_dir_entries(path: &std::path::Path) -> usize {
|
|
1960
1538
|
std::fs::read_dir(path)
|
|
1961
1539
|
.map(|entries| entries.filter_map(std::result::Result::ok).count())
|