1use crate::control::command::{CommandResult, CurrentState};
8use crate::control::outcome::{
9 ChildAttemptStatus, ChildControlFailure, ChildControlFailurePhase, ChildControlOperation,
10 ChildControlResult as RuntimeChildControlResult, ChildLivenessState, ChildRuntimeRecord,
11 ChildStopState, GenerationFenceDecision, GenerationFenceOutcome, GenerationFencePhase,
12 PendingRestartSummary, RestartLimitState,
13};
14use crate::readiness::signal::ReadinessState;
15use schemars::JsonSchema;
16use serde::{Deserialize, Serialize};
17use serde_json::Value;
18use std::collections::BTreeMap;
19
20#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
22pub struct SupportedCommand {
23 pub name: String,
25 pub idempotent: bool,
27 pub timeout_seconds: u64,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
33pub struct TargetProcessRegistration {
34 pub target_id: String,
36 pub display_name: String,
38 pub ipc_path: String,
40 pub lease_seconds: u64,
42 pub supported_commands: Vec<SupportedCommand>,
44}
45
46#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
48#[serde(rename_all = "snake_case")]
49pub enum RegistrationState {
50 Active,
52 Rejected,
54 Expired,
56}
57
58#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
60#[serde(rename_all = "snake_case")]
61pub enum TargetConnectionState {
62 Registered,
64 Connecting,
66 Connected,
68 Reconnecting,
70 Unavailable,
72 Expired,
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
78pub struct TargetProcessIdentity {
79 pub target_id: String,
81 pub display_name: String,
83 pub registration_state: RegistrationState,
85 pub connection_state: TargetConnectionState,
87}
88
89#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
91pub struct DashboardState {
92 pub target: TargetProcessIdentity,
94 pub topology: SupervisorTopology,
96 pub runtime_state: Vec<RuntimeState>,
98 pub child_runtime_records: Vec<DashboardChildRuntimeRecord>,
100 pub recent_events: Vec<EventRecord>,
102 pub recent_logs: Vec<LogRecord>,
104 pub dropped_event_count: u64,
106 pub dropped_log_count: u64,
108 pub config_version: String,
110 pub generated_at_unix_nanos: u128,
112 pub state_generation: u64,
114}
115
116#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
118pub struct SupervisorTopology {
119 pub root: SupervisorNode,
121 pub nodes: Vec<SupervisorNode>,
123 pub edges: Vec<SupervisorEdge>,
125 pub declaration_order: Vec<String>,
127}
128
129#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
131#[serde(rename_all = "snake_case")]
132pub enum SupervisorNodeKind {
133 RootSupervisor,
135 ChildTask,
137}
138
139#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
141#[serde(rename_all = "snake_case")]
142pub enum DashboardCriticality {
143 Critical,
145 Standard,
147 BestEffort,
149}
150
151#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
153pub struct SupervisorNode {
154 pub node_id: String,
156 pub child_id: Option<String>,
158 pub path: String,
160 pub name: String,
162 pub kind: SupervisorNodeKind,
164 pub tags: Vec<String>,
166 pub criticality: DashboardCriticality,
168 pub state_summary: String,
170 pub diagnostics: BTreeMap<String, String>,
172}
173
174#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
176#[serde(rename_all = "snake_case")]
177pub enum SupervisorEdgeKind {
178 ParentChild,
180 Dependency,
182}
183
184#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
186pub struct SupervisorEdge {
187 pub edge_id: String,
189 pub source_path: String,
191 pub target_path: String,
193 pub kind: SupervisorEdgeKind,
195 pub order: usize,
197}
198
199#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
201pub struct RuntimeState {
202 pub child_path: String,
204 pub lifecycle_state: String,
206 pub health: String,
208 pub readiness: String,
210 pub generation: u64,
212 pub child_start_count: u64,
214 pub restart_count: u64,
216 pub last_failure: Option<String>,
218 pub last_policy_decision: Option<String>,
220 pub shutdown_state: String,
222}
223
224#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
226#[serde(rename_all = "snake_case")]
227pub enum DashboardManagedChildState {
228 Running,
230 Paused,
232 Quarantined,
234 Removed,
236}
237
238impl From<ChildControlOperation> for DashboardManagedChildState {
239 fn from(value: ChildControlOperation) -> Self {
241 match value {
242 ChildControlOperation::Active => Self::Running,
243 ChildControlOperation::Paused => Self::Paused,
244 ChildControlOperation::Quarantined => Self::Quarantined,
245 ChildControlOperation::Removed => Self::Removed,
246 }
247 }
248}
249
250impl DashboardManagedChildState {
251 pub fn as_label(&self) -> &'static str {
261 match self {
262 Self::Running => "running",
263 Self::Paused => "paused",
264 Self::Quarantined => "quarantined",
265 Self::Removed => "removed",
266 }
267 }
268}
269
270#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
272#[serde(rename_all = "snake_case")]
273pub enum DashboardChildControlOperation {
274 Active,
276 Paused,
278 Quarantined,
280 Removed,
282}
283
284impl From<ChildControlOperation> for DashboardChildControlOperation {
285 fn from(value: ChildControlOperation) -> Self {
287 match value {
288 ChildControlOperation::Active => Self::Active,
289 ChildControlOperation::Paused => Self::Paused,
290 ChildControlOperation::Quarantined => Self::Quarantined,
291 ChildControlOperation::Removed => Self::Removed,
292 }
293 }
294}
295
296#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
298#[serde(rename_all = "snake_case")]
299pub enum DashboardChildAttemptStatus {
300 Starting,
302 Running,
304 Ready,
306 Cancelling,
308 Stopped,
310}
311
312impl From<ChildAttemptStatus> for DashboardChildAttemptStatus {
313 fn from(value: ChildAttemptStatus) -> Self {
315 match value {
316 ChildAttemptStatus::Starting => Self::Starting,
317 ChildAttemptStatus::Running => Self::Running,
318 ChildAttemptStatus::Ready => Self::Ready,
319 ChildAttemptStatus::Cancelling => Self::Cancelling,
320 ChildAttemptStatus::Stopped => Self::Stopped,
321 }
322 }
323}
324
325#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
327#[serde(rename_all = "snake_case")]
328pub enum DashboardChildStopState {
329 Idle,
331 NoActiveAttempt,
333 CancelDelivered,
335 Completed,
337 Failed,
339}
340
341impl From<ChildStopState> for DashboardChildStopState {
342 fn from(value: ChildStopState) -> Self {
344 match value {
345 ChildStopState::Idle => Self::Idle,
346 ChildStopState::NoActiveAttempt => Self::NoActiveAttempt,
347 ChildStopState::CancelDelivered => Self::CancelDelivered,
348 ChildStopState::Completed => Self::Completed,
349 ChildStopState::Failed => Self::Failed,
350 }
351 }
352}
353
354#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
356#[serde(rename_all = "snake_case")]
357pub enum DashboardReadinessState {
358 Unreported,
360 Ready,
362 NotReady,
364}
365
366impl From<ReadinessState> for DashboardReadinessState {
367 fn from(value: ReadinessState) -> Self {
369 match value {
370 ReadinessState::Unreported => Self::Unreported,
371 ReadinessState::Ready => Self::Ready,
372 ReadinessState::NotReady => Self::NotReady,
373 }
374 }
375}
376
377impl DashboardReadinessState {
378 pub fn as_label(&self) -> &'static str {
388 match self {
389 Self::Unreported => "unreported",
390 Self::Ready => "ready",
391 Self::NotReady => "not_ready",
392 }
393 }
394}
395
396#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
398#[serde(rename_all = "snake_case")]
399pub enum DashboardChildControlFailurePhase {
400 WaitCompletion,
402}
403
404impl From<ChildControlFailurePhase> for DashboardChildControlFailurePhase {
405 fn from(value: ChildControlFailurePhase) -> Self {
407 match value {
408 ChildControlFailurePhase::WaitCompletion => Self::WaitCompletion,
409 }
410 }
411}
412
413#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
415pub struct DashboardChildLivenessState {
416 pub last_heartbeat_at_unix_nanos: Option<u128>,
418 pub heartbeat_stale: bool,
420 pub readiness: DashboardReadinessState,
422}
423
424impl DashboardChildLivenessState {
425 pub fn from_liveness(value: &ChildLivenessState) -> Self {
435 Self {
436 last_heartbeat_at_unix_nanos: value.last_heartbeat_at_unix_nanos,
437 heartbeat_stale: value.heartbeat_stale,
438 readiness: DashboardReadinessState::from(value.readiness),
439 }
440 }
441}
442
443#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
445pub struct DashboardRestartLimitState {
446 pub window_millis: u128,
448 pub limit: u32,
450 pub used: u32,
452 pub remaining: u32,
454 pub exhausted: bool,
456 pub updated_at_unix_nanos: u128,
458}
459
460impl DashboardRestartLimitState {
461 pub fn from_restart_limit(value: &RestartLimitState) -> Self {
471 Self {
472 window_millis: value.window.as_millis(),
473 limit: value.limit,
474 used: value.used,
475 remaining: value.remaining,
476 exhausted: value.exhausted,
477 updated_at_unix_nanos: value.updated_at_unix_nanos,
478 }
479 }
480}
481
482#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
484pub struct DashboardChildControlFailure {
485 pub phase: DashboardChildControlFailurePhase,
487 pub reason: String,
489 pub recoverable: bool,
491}
492
493impl DashboardChildControlFailure {
494 pub fn from_failure(value: &ChildControlFailure) -> Self {
504 Self {
505 phase: DashboardChildControlFailurePhase::from(value.phase),
506 reason: value.reason.clone(),
507 recoverable: value.recoverable,
508 }
509 }
510}
511
512#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
514#[serde(rename_all = "snake_case")]
515pub enum DashboardGenerationFencePhase {
516 Open,
518 WaitingForOldStop,
520 AbortingOld,
522 ReadyToStart,
524 Closed,
526}
527
528impl From<GenerationFencePhase> for DashboardGenerationFencePhase {
529 fn from(value: GenerationFencePhase) -> Self {
531 match value {
532 GenerationFencePhase::Open => Self::Open,
533 GenerationFencePhase::WaitingForOldStop => Self::WaitingForOldStop,
534 GenerationFencePhase::AbortingOld => Self::AbortingOld,
535 GenerationFencePhase::ReadyToStart => Self::ReadyToStart,
536 GenerationFencePhase::Closed => Self::Closed,
537 }
538 }
539}
540
541#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
543#[serde(rename_all = "snake_case")]
544pub enum DashboardGenerationFenceDecision {
545 StartedImmediately,
547 QueuedAfterStop,
549 AlreadyPending,
551 BlockedByShutdown,
553 Rejected,
555}
556
557impl From<GenerationFenceDecision> for DashboardGenerationFenceDecision {
558 fn from(value: GenerationFenceDecision) -> Self {
560 match value {
561 GenerationFenceDecision::StartedImmediately => Self::StartedImmediately,
562 GenerationFenceDecision::QueuedAfterStop => Self::QueuedAfterStop,
563 GenerationFenceDecision::AlreadyPending => Self::AlreadyPending,
564 GenerationFenceDecision::BlockedByShutdown => Self::BlockedByShutdown,
565 GenerationFenceDecision::Rejected => Self::Rejected,
566 }
567 }
568}
569
570#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
572pub struct DashboardPendingRestartSummary {
573 pub old_generation: u64,
575 pub old_attempt: u64,
577 pub target_generation: u64,
579}
580
581impl From<&PendingRestartSummary> for DashboardPendingRestartSummary {
582 fn from(value: &PendingRestartSummary) -> Self {
584 Self {
585 old_generation: value.old_generation.value,
586 old_attempt: value.old_attempt.value,
587 target_generation: value.target_generation.value,
588 }
589 }
590}
591
592#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
594pub struct DashboardGenerationFenceOutcome {
595 pub decision: DashboardGenerationFenceDecision,
597 pub old_generation: Option<u64>,
599 pub old_attempt: Option<u64>,
601 pub target_generation: Option<u64>,
603 pub cancel_delivered: bool,
605 pub abort_requested: bool,
607 pub conflict: Option<DashboardChildControlFailure>,
609}
610
611impl From<&GenerationFenceOutcome> for DashboardGenerationFenceOutcome {
612 fn from(outcome: &GenerationFenceOutcome) -> Self {
614 Self {
615 decision: outcome.decision.into(),
616 old_generation: outcome.old_generation.map(|generation| generation.value),
617 old_attempt: outcome.old_attempt.map(|attempt| attempt.value),
618 target_generation: outcome.target_generation.map(|generation| generation.value),
619 cancel_delivered: outcome.cancel_delivered,
620 abort_requested: outcome.abort_requested,
621 conflict: outcome
622 .conflict
623 .as_ref()
624 .map(DashboardChildControlFailure::from_failure),
625 }
626 }
627}
628
629#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
631pub struct DashboardChildRuntimeRecord {
632 pub child_id: String,
634 pub child_path: String,
636 pub generation: Option<u64>,
638 pub attempt: Option<u64>,
640 pub status: Option<DashboardChildAttemptStatus>,
642 pub operation: DashboardChildControlOperation,
644 pub managed_child_state: DashboardManagedChildState,
646 pub liveness: DashboardChildLivenessState,
648 pub restart_limit: DashboardRestartLimitState,
650 pub stop_state: DashboardChildStopState,
652 pub failure: Option<DashboardChildControlFailure>,
654 pub generation_fence_phase: DashboardGenerationFencePhase,
656 pub pending_restart: Option<DashboardPendingRestartSummary>,
658}
659
660impl DashboardChildRuntimeRecord {
661 pub fn from_runtime_record(record: &ChildRuntimeRecord) -> Self {
671 Self {
672 child_id: record.child_id.to_string(),
673 child_path: record.path.to_string(),
674 generation: record.generation.map(|generation| generation.value),
675 attempt: record.attempt.map(|attempt| attempt.value),
676 status: record.status.map(DashboardChildAttemptStatus::from),
677 operation: DashboardChildControlOperation::from(record.operation),
678 managed_child_state: DashboardManagedChildState::from(record.operation),
679 liveness: DashboardChildLivenessState::from_liveness(&record.liveness),
680 restart_limit: DashboardRestartLimitState::from_restart_limit(&record.restart_limit),
681 stop_state: DashboardChildStopState::from(record.stop_state),
682 failure: record
683 .failure
684 .as_ref()
685 .map(DashboardChildControlFailure::from_failure),
686 generation_fence_phase: record.generation_fence_phase.into(),
687 pending_restart: record
688 .pending_restart
689 .as_ref()
690 .map(DashboardPendingRestartSummary::from),
691 }
692 }
693}
694
695#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
697pub struct DashboardCurrentState {
698 pub child_count: usize,
700 pub shutdown_completed: bool,
702 pub child_runtime_records: Vec<DashboardChildRuntimeRecord>,
704}
705
706impl DashboardCurrentState {
707 pub fn from_current_state(state: &CurrentState) -> Self {
717 Self {
718 child_count: state.child_count,
719 shutdown_completed: state.shutdown_completed,
720 child_runtime_records: state
721 .child_runtime_records
722 .iter()
723 .map(DashboardChildRuntimeRecord::from_runtime_record)
724 .collect(),
725 }
726 }
727}
728
729#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
731pub struct DashboardChildControlResult {
732 pub child_id: String,
734 pub attempt: Option<u64>,
736 pub generation: Option<u64>,
738 pub operation_before: DashboardChildControlOperation,
740 pub operation_after: DashboardChildControlOperation,
742 pub managed_child_state_before: DashboardManagedChildState,
744 pub managed_child_state_after: DashboardManagedChildState,
746 pub status: Option<DashboardChildAttemptStatus>,
748 pub cancel_delivered: bool,
750 pub stop_state: DashboardChildStopState,
752 pub restart_limit: DashboardRestartLimitState,
754 pub liveness: DashboardChildLivenessState,
756 pub idempotent: bool,
758 pub failure: Option<DashboardChildControlFailure>,
760 pub generation_fence: Option<DashboardGenerationFenceOutcome>,
762}
763
764impl DashboardChildControlResult {
765 pub fn from_child_control_result(outcome: &RuntimeChildControlResult) -> Self {
775 Self {
776 child_id: outcome.child_id.to_string(),
777 attempt: outcome.attempt.map(|attempt| attempt.value),
778 generation: outcome.generation.map(|generation| generation.value),
779 operation_before: DashboardChildControlOperation::from(outcome.operation_before),
780 operation_after: DashboardChildControlOperation::from(outcome.operation_after),
781 managed_child_state_before: DashboardManagedChildState::from(outcome.operation_before),
782 managed_child_state_after: DashboardManagedChildState::from(outcome.operation_after),
783 status: outcome.status.map(DashboardChildAttemptStatus::from),
784 cancel_delivered: outcome.cancel_delivered,
785 stop_state: DashboardChildStopState::from(outcome.stop_state),
786 restart_limit: DashboardRestartLimitState::from_restart_limit(&outcome.restart_limit),
787 liveness: DashboardChildLivenessState::from_liveness(&outcome.liveness),
788 idempotent: outcome.idempotent,
789 failure: outcome
790 .failure
791 .as_ref()
792 .map(DashboardChildControlFailure::from_failure),
793 generation_fence: outcome
794 .generation_fence
795 .as_ref()
796 .map(DashboardGenerationFenceOutcome::from),
797 }
798 }
799}
800
801#[allow(clippy::large_enum_variant)]
803#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
804#[serde(tag = "type", rename_all = "snake_case")]
805pub enum DashboardCommandResult {
806 ChildAdded {
808 child_manifest: String,
810 },
811 ChildControl {
813 outcome: DashboardChildControlResult,
815 },
816 CurrentState {
818 state: DashboardCurrentState,
820 },
821 Shutdown {
823 result: Value,
825 },
826}
827
828impl DashboardCommandResult {
829 pub fn from_command_result(result: &CommandResult) -> Result<Self, serde_json::Error> {
839 match result {
840 CommandResult::ChildAdded { child_manifest } => Ok(Self::ChildAdded {
841 child_manifest: child_manifest.clone(),
842 }),
843 CommandResult::ChildControl { outcome } => Ok(Self::ChildControl {
844 outcome: DashboardChildControlResult::from_child_control_result(outcome),
845 }),
846 CommandResult::CurrentState { state } => Ok(Self::CurrentState {
847 state: DashboardCurrentState::from_current_state(state),
848 }),
849 CommandResult::Shutdown { result } => Ok(Self::Shutdown {
850 result: serde_json::to_value(result)?,
851 }),
852 }
853 }
854}
855
856pub fn dashboard_command_result_value(result: &CommandResult) -> Result<Value, serde_json::Error> {
866 serde_json::to_value(DashboardCommandResult::from_command_result(result)?)
867}
868
869pub fn runtime_state_from_child_runtime_record(
886 record: &ChildRuntimeRecord,
887 shutdown_completed: bool,
888) -> RuntimeState {
889 let managed_child_state = DashboardManagedChildState::from(record.operation);
890 let readiness = DashboardReadinessState::from(record.liveness.readiness);
891 RuntimeState {
892 child_path: record.path.to_string(),
893 lifecycle_state: managed_child_state.as_label().to_owned(),
894 health: if record.liveness.heartbeat_stale {
895 "stale".to_owned()
896 } else {
897 "healthy".to_owned()
898 },
899 readiness: readiness.as_label().to_owned(),
900 generation: record
901 .generation
902 .map(|generation| generation.value)
903 .unwrap_or(0),
904 child_start_count: record.attempt.map(|attempt| attempt.value).unwrap_or(0),
905 restart_count: u64::from(record.restart_limit.used),
906 last_failure: record
907 .failure
908 .as_ref()
909 .map(|failure| failure.reason.clone()),
910 last_policy_decision: Some(format!(
911 "restart_limit_remaining={}",
912 record.restart_limit.remaining
913 )),
914 shutdown_state: if shutdown_completed {
915 "completed".to_owned()
916 } else {
917 "running".to_owned()
918 },
919 }
920}
921
922#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
924pub struct EventRecord {
925 pub target_id: String,
927 pub sequence: u64,
929 pub correlation_id: String,
931 pub event_type: String,
933 pub severity: String,
935 pub target_path: String,
937 pub child_id: Option<String>,
939 pub occurred_at_unix_nanos: u128,
941 pub config_version: String,
943 pub payload: Value,
945}
946
947#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
949pub struct LogRecord {
950 pub target_id: String,
952 pub sequence: Option<u64>,
954 pub correlation_id: Option<String>,
956 pub severity: String,
958 pub message: String,
960 pub fields: BTreeMap<String, String>,
962 pub occurred_at_unix_nanos: u128,
964}
965
966#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
968#[serde(rename_all = "snake_case")]
969pub enum ControlCommandKind {
970 RestartChild,
972 PauseChild,
974 ResumeChild,
976 QuarantineChild,
978 RemoveChild,
980 AddChild,
982 ShutdownTree,
984}
985
986#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
988pub struct ControlCommandTarget {
989 pub child_path: Option<String>,
991 pub child_manifest: Option<String>,
993}
994
995#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
997pub struct ControlCommandRequest {
998 pub command_id: String,
1000 pub target_id: String,
1002 pub command: ControlCommandKind,
1004 pub target: ControlCommandTarget,
1006 pub reason: String,
1008 pub requested_by: String,
1010 pub confirmed: bool,
1012 pub requested_at_unix_nanos: u128,
1014}
1015
1016#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1018pub struct ControlCommandResult {
1019 pub command_id: String,
1021 pub target_id: String,
1023 pub accepted: bool,
1025 pub status: String,
1027 pub error: Option<crate::dashboard::error::DashboardError>,
1029 pub state_delta: Option<Value>,
1031 pub completed_at_unix_nanos: Option<u128>,
1033}
1034
1035#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
1037pub struct AuditEvent {
1038 pub audit_id: String,
1040 pub identity: String,
1042 pub target_id: String,
1044 pub command_id: String,
1046 pub command: ControlCommandKind,
1048 pub target: ControlCommandTarget,
1050 pub reason: String,
1052 pub result: String,
1054 pub occurred_at_unix_nanos: u128,
1056}