155 lines
4.8 KiB
Dart
155 lines
4.8 KiB
Dart
import 'dart:convert';
|
|
|
|
import 'package:flutter/foundation.dart';
|
|
import 'runtime_models.dart';
|
|
|
|
class DerivedTasksController extends ChangeNotifier {
|
|
List<DerivedTaskItem> queueInternal = const <DerivedTaskItem>[];
|
|
List<DerivedTaskItem> runningInternal = const <DerivedTaskItem>[];
|
|
List<DerivedTaskItem> historyInternal = const <DerivedTaskItem>[];
|
|
List<DerivedTaskItem> failedInternal = const <DerivedTaskItem>[];
|
|
List<DerivedTaskItem> scheduledInternal = const <DerivedTaskItem>[];
|
|
|
|
List<DerivedTaskItem> get queue => queueInternal;
|
|
List<DerivedTaskItem> get running => runningInternal;
|
|
List<DerivedTaskItem> get history => historyInternal;
|
|
List<DerivedTaskItem> get failed => failedInternal;
|
|
List<DerivedTaskItem> get scheduled => scheduledInternal;
|
|
|
|
int get totalCount =>
|
|
queueInternal.length +
|
|
runningInternal.length +
|
|
historyInternal.length +
|
|
failedInternal.length;
|
|
|
|
void recompute({
|
|
required List<GatewaySessionSummary> sessions,
|
|
required List<GatewayCronJobSummary> cronJobs,
|
|
required String currentSessionKey,
|
|
required bool hasPendingRun,
|
|
required String activeAgentName,
|
|
}) {
|
|
final sorted = sessions.toList(growable: false)
|
|
..sort(
|
|
(left, right) =>
|
|
(right.updatedAtMs ?? 0).compareTo(left.updatedAtMs ?? 0),
|
|
);
|
|
final queue = <DerivedTaskItem>[];
|
|
final running = <DerivedTaskItem>[];
|
|
final history = <DerivedTaskItem>[];
|
|
final failed = <DerivedTaskItem>[];
|
|
for (final session in sorted) {
|
|
final item = DerivedTaskItem(
|
|
id: session.key,
|
|
title: session.label,
|
|
owner: activeAgentName,
|
|
status: statusForSessionInternal(
|
|
session: session,
|
|
currentSessionKey: currentSessionKey,
|
|
hasPendingRun: hasPendingRun,
|
|
),
|
|
surface: session.surface ?? session.kind ?? 'Assistant',
|
|
startedAtLabel: timeLabelInternal(session.updatedAtMs),
|
|
durationLabel: durationLabelInternal(session.updatedAtMs),
|
|
summary:
|
|
session.lastMessagePreview ?? session.subject ?? 'Session activity',
|
|
sessionKey: session.key,
|
|
);
|
|
switch (item.status) {
|
|
case 'Running':
|
|
running.add(item);
|
|
case 'Failed':
|
|
failed.add(item);
|
|
case 'Queued':
|
|
queue.add(item);
|
|
default:
|
|
history.add(item);
|
|
}
|
|
}
|
|
queueInternal = queue;
|
|
runningInternal = running;
|
|
historyInternal = history;
|
|
failedInternal = failed;
|
|
scheduledInternal = cronJobs
|
|
.map(
|
|
(job) => DerivedTaskItem(
|
|
id: job.id,
|
|
title: job.name,
|
|
owner: job.agentId?.trim().isNotEmpty == true
|
|
? job.agentId!
|
|
: activeAgentName,
|
|
status: job.enabled ? 'Scheduled' : 'Disabled',
|
|
surface: 'Cron',
|
|
startedAtLabel: timeLabelInternal(job.nextRunAtMs?.toDouble()),
|
|
durationLabel: job.scheduleLabel,
|
|
summary:
|
|
job.description ??
|
|
job.lastError ??
|
|
job.lastStatus ??
|
|
'Scheduled automation',
|
|
sessionKey: 'cron:${job.id}',
|
|
),
|
|
)
|
|
.toList(growable: false);
|
|
notifyListeners();
|
|
}
|
|
|
|
String statusForSessionInternal({
|
|
required GatewaySessionSummary session,
|
|
required String currentSessionKey,
|
|
required bool hasPendingRun,
|
|
}) {
|
|
if (session.abortedLastRun == true) {
|
|
return 'Failed';
|
|
}
|
|
if (hasPendingRun && matchesSessionKey(session.key, currentSessionKey)) {
|
|
return 'Running';
|
|
}
|
|
if ((session.lastMessagePreview ?? '').isEmpty) {
|
|
return 'Queued';
|
|
}
|
|
return 'Open';
|
|
}
|
|
|
|
String timeLabelInternal(double? timestampMs) {
|
|
if (timestampMs == null) {
|
|
return 'Unknown';
|
|
}
|
|
final date = DateTime.fromMillisecondsSinceEpoch(timestampMs.toInt());
|
|
return '${date.month}/${date.day} ${date.hour.toString().padLeft(2, '0')}:${date.minute.toString().padLeft(2, '0')}';
|
|
}
|
|
|
|
String durationLabelInternal(double? timestampMs) {
|
|
if (timestampMs == null) {
|
|
return 'n/a';
|
|
}
|
|
final delta = DateTime.now().difference(
|
|
DateTime.fromMillisecondsSinceEpoch(timestampMs.toInt()),
|
|
);
|
|
if (delta.inMinutes < 1) {
|
|
return 'just now';
|
|
}
|
|
if (delta.inHours < 1) {
|
|
return '${delta.inMinutes}m ago';
|
|
}
|
|
if (delta.inDays < 1) {
|
|
return '${delta.inHours}h ago';
|
|
}
|
|
return '${delta.inDays}d ago';
|
|
}
|
|
}
|
|
|
|
bool matchesSessionKey(String incoming, String current) {
|
|
final left = incoming.trim().toLowerCase();
|
|
final right = current.trim().toLowerCase();
|
|
return left == right;
|
|
}
|
|
|
|
String encodePrettyJson(Object value) {
|
|
const encoder = JsonEncoder.withIndent(' ');
|
|
return encoder.convert(value);
|
|
}
|
|
|
|
String ephemeralIdInternal() =>
|
|
DateTime.now().microsecondsSinceEpoch.toString();
|