feat: detached-runs monitor page (T3 parity)
Some checks failed
Security / Security check (push) Failing after 1s
Some checks failed
Security / Security check (push) Failing after 1s
New Runs sidebar destination listing detached invocations (detach:true) with phase, current step, project and a Cancel button while pending/running. Workspace-scoped like Audit/Approvals, polls every 2s. Detached runs are opt-in (detached.enabled) — the empty state explains how to enable them. Inline help doc DE+EN. DetachedRun model + listDetachedRuns/cancelDetachedRun in HubService, backed by the SDK's listInvocations()/cancelInvocation(). flutter analyze clean; 29 tests green (sidebar Y-stability updated for the new destination, model mapping unit-tested). Screenshot verification (light+dark) deferred — shared desktop in use. Signed-off-by: flemming-it <stefan.a.flemming@googlemail.com>
This commit is contained in:
parent
984c91f91d
commit
54ccd3936a
13 changed files with 764 additions and 2 deletions
|
|
@ -1017,6 +1017,23 @@ class HubService {
|
|||
Future<void> reject(String id, String reviewer, String reason) =>
|
||||
_client.reject(approvalId: id, reviewer: reviewer, reason: reason);
|
||||
|
||||
/// Every tracked detached invocation (newest-first). Optionally
|
||||
/// scoped to one [project]. Empty when detached invocations are
|
||||
/// disabled or none have run this process.
|
||||
Future<List<DetachedRun>> listDetachedRuns({String project = ''}) async {
|
||||
final entries = await _client.listInvocations();
|
||||
final runs = entries
|
||||
.where((e) => project.isEmpty || e.project == project)
|
||||
.map(DetachedRun.fromEntry)
|
||||
.toList();
|
||||
return runs;
|
||||
}
|
||||
|
||||
/// Cancel a running/pending detached invocation. Returns true when
|
||||
/// it was signalled, false if already finished or unknown.
|
||||
Future<bool> cancelDetachedRun(String id) =>
|
||||
_client.cancelInvocation(id);
|
||||
|
||||
/// Federation satellites currently connected to this hub
|
||||
/// (primary side). Empty when none are connected.
|
||||
Future<List<Satellite>> listSatellites() async {
|
||||
|
|
@ -1547,6 +1564,73 @@ class ApprovalRecord {
|
|||
});
|
||||
}
|
||||
|
||||
/// Lifecycle phase of a detached run, mirroring the wire enum.
|
||||
enum DetachedPhase { pending, running, succeeded, failed, cancelled, unknown }
|
||||
|
||||
/// A detached invocation as shown in the runs monitor.
|
||||
class DetachedRun {
|
||||
final String id;
|
||||
final DetachedPhase phase;
|
||||
final String flowName;
|
||||
final String project;
|
||||
|
||||
/// Step currently executing (running), else empty.
|
||||
final String currentStep;
|
||||
final DateTime? startedAt;
|
||||
final DateTime? finishedAt;
|
||||
|
||||
/// Error message when [phase] is failed, else empty.
|
||||
final String error;
|
||||
|
||||
const DetachedRun({
|
||||
required this.id,
|
||||
required this.phase,
|
||||
required this.flowName,
|
||||
required this.project,
|
||||
required this.currentStep,
|
||||
required this.startedAt,
|
||||
required this.finishedAt,
|
||||
required this.error,
|
||||
});
|
||||
|
||||
/// True while the run can still be cancelled.
|
||||
bool get isCancellable =>
|
||||
phase == DetachedPhase.pending || phase == DetachedPhase.running;
|
||||
|
||||
factory DetachedRun.fromEntry(InvocationEntry e) {
|
||||
// Map by the proto enum's integer value (PENDING=1 … CANCELLED=5)
|
||||
// so Studio needn't import the generated enum type.
|
||||
DetachedPhase mapPhase(int v) {
|
||||
switch (v) {
|
||||
case 1:
|
||||
return DetachedPhase.pending;
|
||||
case 2:
|
||||
return DetachedPhase.running;
|
||||
case 3:
|
||||
return DetachedPhase.succeeded;
|
||||
case 4:
|
||||
return DetachedPhase.failed;
|
||||
case 5:
|
||||
return DetachedPhase.cancelled;
|
||||
default:
|
||||
return DetachedPhase.unknown;
|
||||
}
|
||||
}
|
||||
|
||||
final s = e.status;
|
||||
return DetachedRun(
|
||||
id: e.invocationId,
|
||||
phase: mapPhase(s.phase.value),
|
||||
flowName: e.flowName,
|
||||
project: e.project,
|
||||
currentStep: s.currentStep,
|
||||
startedAt: s.startedAt.isEmpty ? null : DateTime.tryParse(s.startedAt),
|
||||
finishedAt: s.finishedAt.isEmpty ? null : DateTime.tryParse(s.finishedAt),
|
||||
error: s.error,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class SystemAiStatus {
|
||||
final bool enabled;
|
||||
final String provider;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue