Rebuild grid agent TUI with Ratatui dashboards
This commit is contained in:
271
Cargo.lock
generated
271
Cargo.lock
generated
@@ -1867,6 +1867,12 @@ version = "3.20.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
|
||||
|
||||
[[package]]
|
||||
name = "by_address"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.25.2"
|
||||
@@ -1931,6 +1937,15 @@ dependencies = [
|
||||
"wayland-client",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "castaway"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dec551ab6e7578819132c713a93c022a05d60159dc86e7a7050223577484c55a"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.4.2"
|
||||
@@ -2151,6 +2166,20 @@ dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "compact_str"
|
||||
version = "0.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dfdd1c2274d9aa354115b09dc9a901d6c5576818cdf70d14cae2bdb47df00ab"
|
||||
dependencies = [
|
||||
"castaway",
|
||||
"cfg-if",
|
||||
"itoa",
|
||||
"rustversion",
|
||||
"ryu",
|
||||
"static_assertions",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "concurrent-queue"
|
||||
version = "2.5.0"
|
||||
@@ -2539,6 +2568,40 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"darling_macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_core"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff"
|
||||
dependencies = [
|
||||
"ident_case",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"strsim",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "darling_macro"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785"
|
||||
dependencies = [
|
||||
"darling_core",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dasp_sample"
|
||||
version = "0.11.0"
|
||||
@@ -3495,6 +3558,8 @@ version = "0.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash 0.2.0",
|
||||
]
|
||||
|
||||
@@ -3861,6 +3926,12 @@ version = "2.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e4a2c462a4d927d512f5f882a033ddd62f33a05bb9f230d98f736ac3dc85938f"
|
||||
|
||||
[[package]]
|
||||
name = "ident_case"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39"
|
||||
|
||||
[[package]]
|
||||
name = "idna"
|
||||
version = "1.1.0"
|
||||
@@ -3917,6 +3988,15 @@ dependencies = [
|
||||
"serde_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "indoc"
|
||||
version = "2.0.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706"
|
||||
dependencies = [
|
||||
"rustversion",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inflections"
|
||||
version = "1.1.1"
|
||||
@@ -3932,6 +4012,19 @@ dependencies = [
|
||||
"generic-array",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "instability"
|
||||
version = "0.3.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2bf84e73fa6f27f299dec58e13223cf70db80da872eb921d4f6138342a0eabc8"
|
||||
dependencies = [
|
||||
"darling",
|
||||
"indoc",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 3.0.3",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "inventory"
|
||||
version = "0.3.24"
|
||||
@@ -4139,6 +4232,17 @@ dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "kasuari"
|
||||
version = "0.4.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899"
|
||||
dependencies = [
|
||||
"hashbrown 0.16.1",
|
||||
"portable-atomic",
|
||||
"thiserror 2.0.20",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ktx2"
|
||||
version = "0.5.0"
|
||||
@@ -4411,6 +4515,15 @@ dependencies = [
|
||||
"vcpkg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "line-clipping"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e752191d037c44ad111a8caa762921926658402f01cc1253f7bef2020ece4f5e"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "linebender_resource_handle"
|
||||
version = "0.1.1"
|
||||
@@ -4456,6 +4569,15 @@ version = "0.4.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
|
||||
|
||||
[[package]]
|
||||
name = "lru"
|
||||
version = "0.18.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d2f2f9b4ba7e6b24d95e7e899329d35be83bcded72c8540cdd5368932d1d90a"
|
||||
dependencies = [
|
||||
"hashbrown 0.17.1",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lru-slab"
|
||||
version = "0.1.2"
|
||||
@@ -4544,7 +4666,7 @@ dependencies = [
|
||||
"serde_json",
|
||||
"serde_yaml_ng",
|
||||
"similar",
|
||||
"strum",
|
||||
"strum 0.27.2",
|
||||
"thiserror 2.0.20",
|
||||
"time",
|
||||
"tokio",
|
||||
@@ -4564,7 +4686,7 @@ dependencies = [
|
||||
"reqwest 0.12.28",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"strum",
|
||||
"strum 0.27.2",
|
||||
"thiserror 2.0.20",
|
||||
"time",
|
||||
"tokio",
|
||||
@@ -4631,6 +4753,7 @@ dependencies = [
|
||||
"metacrate-game-loop",
|
||||
"metacrate-lsl-tools",
|
||||
"metacrate-rendering-wgpu",
|
||||
"ratatui",
|
||||
"rustls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
@@ -4942,6 +5065,15 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_threads"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "objc-sys"
|
||||
version = "0.3.5"
|
||||
@@ -5509,6 +5641,39 @@ dependencies = [
|
||||
"sha2 0.10.9",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "palette"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ddeed8580d347d2abf3dcf06a5f0b3dc020258338526b277847cd4248a70fc64"
|
||||
dependencies = [
|
||||
"approx",
|
||||
"libm",
|
||||
"palette_derive",
|
||||
"palette_math",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "palette_derive"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88537020289b719d81be994ccf1bbf4990f477e2f69ee52fe3e45f43a02e56be"
|
||||
dependencies = [
|
||||
"by_address",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "palette_math"
|
||||
version = "0.7.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6e6eb142958d64335fb0e345c5b9ead2ecd6fc438c307e9d7d3c4fd428dbaf12"
|
||||
dependencies = [
|
||||
"libm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "parking"
|
||||
version = "2.2.1"
|
||||
@@ -6005,6 +6170,72 @@ version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ca45419789ae5a7899559e9512e58ca889e41f04f1f2445e9f4b290ceccd1d08"
|
||||
|
||||
[[package]]
|
||||
name = "ratatui"
|
||||
version = "0.30.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3274ba0a2c5e1bcad2a2005d20f4dc59dad26b2eb0940fb094500dba4099d57d"
|
||||
dependencies = [
|
||||
"instability",
|
||||
"ratatui-core",
|
||||
"ratatui-crossterm",
|
||||
"ratatui-widgets",
|
||||
"serde",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ratatui-core"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cbb175c433c8e28a809d1f5773a2ae96e68c0ce40db865cbab1020bf33ae479c"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"compact_str",
|
||||
"hashbrown 0.17.1",
|
||||
"itertools 0.14.0",
|
||||
"kasuari",
|
||||
"lru",
|
||||
"palette",
|
||||
"serde",
|
||||
"strum 0.28.0",
|
||||
"thiserror 2.0.20",
|
||||
"unicode-segmentation",
|
||||
"unicode-truncate",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ratatui-crossterm"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "567584a3b0e6a8203c23de40b4861497266725eb5363dbfd18a1edd603cca9f0"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossterm",
|
||||
"instability",
|
||||
"ratatui-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ratatui-widgets"
|
||||
version = "0.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "66e3d19bcc9130ca376277d93b60767ff121ace3be06f5f95f81dd68956407d1"
|
||||
dependencies = [
|
||||
"bitflags 2.13.1",
|
||||
"hashbrown 0.17.1",
|
||||
"indoc",
|
||||
"instability",
|
||||
"itertools 0.14.0",
|
||||
"line-clipping",
|
||||
"ratatui-core",
|
||||
"serde",
|
||||
"strum 0.28.0",
|
||||
"time",
|
||||
"unicode-segmentation",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "raw-window-handle"
|
||||
version = "0.6.2"
|
||||
@@ -7149,7 +7380,16 @@ version = "0.27.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
|
||||
dependencies = [
|
||||
"strum_macros",
|
||||
"strum_macros 0.27.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd"
|
||||
dependencies = [
|
||||
"strum_macros 0.28.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7164,6 +7404,18 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "strum_macros"
|
||||
version = "0.28.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "subtle"
|
||||
version = "2.6.1"
|
||||
@@ -7314,7 +7566,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134"
|
||||
dependencies = [
|
||||
"deranged",
|
||||
"libc",
|
||||
"num-conv",
|
||||
"num_threads",
|
||||
"powerfmt",
|
||||
"serde_core",
|
||||
"time-core",
|
||||
@@ -7693,6 +7947,17 @@ version = "1.13.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-truncate"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5"
|
||||
dependencies = [
|
||||
"itertools 0.14.0",
|
||||
"unicode-segmentation",
|
||||
"unicode-width 0.2.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.14"
|
||||
|
||||
@@ -22,6 +22,7 @@ metacrate-game-loop = { version = "0.0.1", path = "../metacrate-game-loop" }
|
||||
metacrate-rendering-wgpu = { version = "0.0.1", path = "../metacrate-rendering-wgpu" }
|
||||
mentra = { version = "0.18.3", default-features = false }
|
||||
jpeg-encoder = "0.6.1"
|
||||
ratatui = { version = "0.30.2", default-features = false, features = ["crossterm_0_29"] }
|
||||
rustls = { version = "0.23.43", default-features = false, features = ["aws_lc_rs", "std", "tls12"] }
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
serde_json = "1"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -75,7 +75,7 @@ impl TuiTransport for FakeTransport {
|
||||
}
|
||||
ControlRequest::ListObservabilityEvents { .. } => {
|
||||
ControlPayload::ObservabilityEvents(crate::Page {
|
||||
items: Vec::new(),
|
||||
items: vec![structured_event()],
|
||||
next_cursor: None,
|
||||
})
|
||||
}
|
||||
@@ -91,6 +91,31 @@ impl TuiTransport for FakeTransport {
|
||||
}
|
||||
}
|
||||
|
||||
fn structured_event() -> crate::StructuredEvent {
|
||||
serde_json::from_value(serde_json::json!({
|
||||
"schema_version": 1,
|
||||
"event_id": 7,
|
||||
"unix_millis": 1234,
|
||||
"severity": "warning",
|
||||
"component": "control_queue",
|
||||
"family": "queue_pressure",
|
||||
"correlation": {
|
||||
"avatar_id": "avatar-7",
|
||||
"session_id": "session-7",
|
||||
"request_id": null,
|
||||
"action_id": "action-7"
|
||||
},
|
||||
"origin": "service",
|
||||
"duration_millis": 19,
|
||||
"retry_count": 1,
|
||||
"result_code": "delayed",
|
||||
"reason_code": "capacity",
|
||||
"redaction_flags": [],
|
||||
"fields": {}
|
||||
}))
|
||||
.expect("event fixture")
|
||||
}
|
||||
|
||||
fn empty_metrics() -> crate::MetricsSnapshot {
|
||||
serde_json::from_value(serde_json::json!({
|
||||
"ready":true,"reconnects":0,"active_sessions":0,"active_tasks":0,
|
||||
@@ -116,6 +141,96 @@ async fn snapshot_refresh_is_transport_neutral_and_bounded() {
|
||||
"running"
|
||||
);
|
||||
assert_eq!(transport.0.lock().expect("requests").len(), 8);
|
||||
app.refresh(&transport).await.expect("stable refresh");
|
||||
assert_eq!(
|
||||
app.snapshot.timeline.len(),
|
||||
1,
|
||||
"refresh must deduplicate events"
|
||||
);
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn performance_history_is_bounded_and_large_dashboards_are_deterministic() {
|
||||
let transport = FakeTransport::new();
|
||||
let mut app = OperatorTui::default();
|
||||
for _ in 0..140 {
|
||||
app.refresh(&transport).await.expect("refresh");
|
||||
}
|
||||
assert_eq!(app.performance_history_len(), 120);
|
||||
app.screen = OperatorScreen::Overview;
|
||||
let unicode = app.render(TuiRenderOptions {
|
||||
width: 140,
|
||||
height: 42,
|
||||
color: false,
|
||||
});
|
||||
assert!(unicode.contains("Café") && unicode.contains('世') && unicode.contains('界'));
|
||||
for (screen, marker) in [
|
||||
(OperatorScreen::Overview, "Runtime"),
|
||||
(OperatorScreen::Sessions, "Sessions"),
|
||||
(OperatorScreen::QueuesAndBudgets, "Work queue"),
|
||||
(OperatorScreen::Roaming, "Roaming schedules"),
|
||||
(OperatorScreen::Approvals, "Pending approvals"),
|
||||
(OperatorScreen::Timeline, "Timeline / audit"),
|
||||
(OperatorScreen::Health, "Health"),
|
||||
(OperatorScreen::Errors, "Recent errors"),
|
||||
(OperatorScreen::Diagnostics, "Diagnostics (redacted)"),
|
||||
(OperatorScreen::Preferences, "Preferences"),
|
||||
] {
|
||||
app.screen = screen;
|
||||
let first = app.render(TuiRenderOptions {
|
||||
width: 140,
|
||||
height: 42,
|
||||
color: true,
|
||||
});
|
||||
let second = app.render(TuiRenderOptions {
|
||||
width: 140,
|
||||
height: 42,
|
||||
color: true,
|
||||
});
|
||||
assert!(first.contains(marker), "missing {marker}");
|
||||
assert_eq!(first.lines().count(), second.lines().count());
|
||||
}
|
||||
app.screen = OperatorScreen::QueuesAndBudgets;
|
||||
let output = app.render(TuiRenderOptions {
|
||||
width: 140,
|
||||
height: 42,
|
||||
color: false,
|
||||
});
|
||||
for label in [
|
||||
"Work queue",
|
||||
"Rate limit",
|
||||
"Inference avg ms",
|
||||
"Tool avg ms",
|
||||
"Dropped total",
|
||||
] {
|
||||
assert!(output.contains(label), "missing {label}");
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn event_search_covers_correlation_duration_result_and_reason() {
|
||||
let mut app = OperatorTui::default();
|
||||
app.snapshot.timeline.push_back(structured_event());
|
||||
app.screen = OperatorScreen::Timeline;
|
||||
for query in [
|
||||
"warning",
|
||||
"avatar-7",
|
||||
"session-7",
|
||||
"action-7",
|
||||
"19",
|
||||
"delayed",
|
||||
"capacity",
|
||||
] {
|
||||
app.filter.query = query.into();
|
||||
assert!(
|
||||
app.render(TuiRenderOptions {
|
||||
width: 120,
|
||||
height: 30,
|
||||
color: false
|
||||
})
|
||||
.contains("queue_pressure")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
@@ -142,7 +257,7 @@ fn every_screen_renders_without_a_terminal_at_small_unicode_and_mono_sizes() {
|
||||
color: false,
|
||||
});
|
||||
assert!(!output.is_empty());
|
||||
assert!(output.lines().count() <= 4);
|
||||
assert!(output.lines().count() <= 5);
|
||||
assert!(
|
||||
output
|
||||
.lines()
|
||||
@@ -196,6 +311,21 @@ fn every_global_command_has_a_keyboard_path() {
|
||||
assert_eq!(app.command_shortcut('s'), Some(OperatorCommand::Shutdown));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn confirmation_dialog_is_visible_without_color() {
|
||||
let mut app = OperatorTui::default();
|
||||
assert_eq!(
|
||||
app.reduce(TuiInput::Command(OperatorCommand::Shutdown)),
|
||||
TuiAction::None
|
||||
);
|
||||
let output = app.render(TuiRenderOptions {
|
||||
width: 100,
|
||||
height: 24,
|
||||
color: false,
|
||||
});
|
||||
assert!(output.contains("confirm HighRisk (y/n)"));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn transport_errors_are_safe_display_text() {
|
||||
struct Broken;
|
||||
|
||||
@@ -2,7 +2,7 @@ use std::collections::BTreeSet;
|
||||
use std::fs;
|
||||
use std::path::{Path, PathBuf};
|
||||
|
||||
const ALLOWED_DEPENDENCIES: [&str; 22] = [
|
||||
const ALLOWED_DEPENDENCIES: [&str; 23] = [
|
||||
"async-trait",
|
||||
"base64",
|
||||
"crossterm",
|
||||
@@ -16,6 +16,7 @@ const ALLOWED_DEPENDENCIES: [&str; 22] = [
|
||||
"mentra",
|
||||
"jpeg-encoder",
|
||||
"libremetaverse-types",
|
||||
"ratatui",
|
||||
"rustls",
|
||||
"serde",
|
||||
"serde_json",
|
||||
|
||||
@@ -108,3 +108,23 @@ fn dotenv_import_is_explicit_and_preferences_panel_never_renders_secrets() {
|
||||
let _ = tui.reduce(TuiInput::PreferenceSave);
|
||||
fs::remove_dir_all(directory).unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn preferences_form_keeps_validation_feedback_visible() {
|
||||
let directory = temporary("validation");
|
||||
let config = directory.join("config.yml");
|
||||
let mut tui = OperatorTui::with_preferences(config).unwrap();
|
||||
tui.screen = OperatorScreen::Preferences;
|
||||
let _ = tui.reduce(TuiInput::PreferenceEditOrCommit);
|
||||
for value in "not-a-mode".chars() {
|
||||
let _ = tui.reduce(TuiInput::PreferenceCharacter(value));
|
||||
}
|
||||
let _ = tui.reduce(TuiInput::PreferenceEditOrCommit);
|
||||
let rendered = tui.render(TuiRenderOptions {
|
||||
width: 100,
|
||||
height: 24,
|
||||
color: false,
|
||||
});
|
||||
assert!(rendered.contains("[VALIDATION] mode must be offline, integrated, or split"));
|
||||
fs::remove_dir_all(directory).unwrap();
|
||||
}
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# Grid-agent operator TUI
|
||||
|
||||
The operator TUI is a pure-Rust, cross-platform view of the versioned control
|
||||
plane. Embedded and split modes use the same reducer, snapshots, commands, and
|
||||
authenticated control requests; the UI never reads private runtime state.
|
||||
The operator TUI is a pure-Rust, cross-platform Ratatui dashboard over the
|
||||
versioned control plane. Embedded and split modes use the same reducer,
|
||||
snapshots, widgets, commands, and authenticated control requests; the UI never
|
||||
reads private runtime state. Standalone preferences uses the same Ratatui form.
|
||||
|
||||
Run an embedded UI with a live-grid integrated configuration:
|
||||
|
||||
@@ -31,19 +32,33 @@ Up/Down to select, Enter to edit/commit, `w` to save, and Esc to cancel. Changes
|
||||
to connection or authorization settings apply after restart. A one-time
|
||||
`--import-env /path/to/.env` migrates legacy `GRID_*` and `OPENAPI_*` values.
|
||||
|
||||
Use Tab/arrow keys to change panels and scroll. `r` refreshes; `p`/`u` pause or
|
||||
resume; `f` reconnects the grid; `a`/`d` approve or deny the first pending
|
||||
approval; `x` cancels the newest visible action; `e` expires the first visible
|
||||
session; `t` toggles the first roaming schedule; `s` requests shutdown; and
|
||||
`q` or Ctrl-C exits. Mutations other than pause/resume require confirmation,
|
||||
with shutdown marked high risk. Exiting or crashing a split TUI does not stop
|
||||
the service.
|
||||
The Overview dashboard shows readiness, region/pose, behavior, build and visual
|
||||
work, approvals, and recent failures. Queues & budgets shows real queue and
|
||||
rate-limit gauges, active work, outcome counts, dropped events, aggregate
|
||||
latency, and a bounded 120-refresh history. Sessions, roaming schedules,
|
||||
approvals, timeline/audit, errors, diagnostics, health, and preferences retain
|
||||
focused tables or panels and contextual detail. Missing backend data is marked
|
||||
unavailable rather than estimated.
|
||||
|
||||
Rendering is terminal-independent, Unicode display-width aware, responsive to
|
||||
resize events, and monochrome when `NO_COLOR` is set or `TERM=dumb`. Input uses
|
||||
a bounded queue. Snapshot ticks coalesce while a request is slow, and timeline
|
||||
storage is bounded. The terminal guard restores raw mode, cursor visibility,
|
||||
and the alternate screen on success, error, Ctrl-C, or panic unwinding.
|
||||
Use Tab/Shift-Tab or Left/Right to change panels and Up/Down to select rows.
|
||||
`/` searches event, audit, correlation, time, duration, result, and reason data;
|
||||
Enter applies the search and Esc clears it. `r` refreshes; `p`/`u` pause or
|
||||
resume; `f` reconnects the grid; `a`/`d` approve or deny the selected approval;
|
||||
`x` cancels the newest visible action; `e` expires the selected session; `t`
|
||||
toggles the selected roaming schedule; `s` requests shutdown; and `q` or
|
||||
Ctrl-C exits. Mutations other than pause/resume require confirmation, with
|
||||
shutdown marked high risk. Exiting or crashing a split TUI does not stop the
|
||||
service.
|
||||
|
||||
Ratatui's Crossterm backend provides buffered/diff rendering without clearing
|
||||
the full screen on every frame. Layouts respond to resize and collapse to a
|
||||
compact status view on very small terminals; focus and failure markers remain
|
||||
visible without color when `NO_COLOR` is set or `TERM=dumb`. Input, refresh
|
||||
work, results, event history, errors, and performance history are all bounded.
|
||||
Slow snapshots run outside the input reducer, and refresh ticks are dropped
|
||||
when the bounded worker queue is full. The terminal guard restores raw mode,
|
||||
cursor visibility, and the alternate screen on success, error, Ctrl-C, or
|
||||
panic unwinding.
|
||||
|
||||
Diagnostic panels show only explicitly captured redacted envelopes. Prompt
|
||||
and response content, API keys, grid passwords, operator tokens, capability
|
||||
@@ -52,7 +67,8 @@ URLs, and model reasoning have no TUI representation.
|
||||
Focused verification:
|
||||
|
||||
```sh
|
||||
cargo test --locked -p metacrate-grid-agent --lib tui_tests
|
||||
cargo test --locked -p metacrate-grid-agent tui --lib
|
||||
cargo test --locked -p metacrate-grid-agent --test preferences_config
|
||||
cargo test --locked -p metacrate-grid-agent --test dependency_policy
|
||||
cargo clippy --locked -p metacrate-grid-agent --all-targets --features live-grid -- -D warnings
|
||||
RUSTDOCFLAGS="-D warnings" cargo doc --locked -p metacrate-grid-agent --no-deps
|
||||
|
||||
Reference in New Issue
Block a user