@dipertq/dsh-openviking-status 0.2.2 → 0.3.0
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/README.md +1 -1
- package/lib/client.cjs +656 -12
- package/lib/client.cjs.map +1 -1
- package/lib/client.d.cts +219 -3
- package/lib/client.js +656 -12
- package/lib/client.js.map +1 -1
- package/lib/index.js +159 -1
- package/lib/index.js.map +1 -1
- package/package.json +1 -1
package/lib/client.cjs
CHANGED
|
@@ -37,26 +37,37 @@ var client_exports = {};
|
|
|
37
37
|
__export(client_exports, {
|
|
38
38
|
COMMIT_THRESHOLD: () => COMMIT_THRESHOLD,
|
|
39
39
|
DEFAULT_OPENVIKING_ENDPOINT: () => DEFAULT_OPENVIKING_ENDPOINT,
|
|
40
|
+
ExtractionSection: () => ExtractionSection,
|
|
40
41
|
OpenVikingClient: () => OpenVikingClient,
|
|
41
42
|
OpenVikingSettingsSection: () => OpenVikingSettingsSection,
|
|
42
43
|
OpenVikingStatusChip: () => OpenVikingStatusChip,
|
|
43
44
|
OpenVikingStatusPopover: () => OpenVikingStatusPopover,
|
|
45
|
+
POLL_INTERVAL_ACTIVE_MS: () => POLL_INTERVAL_ACTIVE_MS,
|
|
46
|
+
POLL_INTERVAL_IDLE_MS: () => POLL_INTERVAL_IDLE_MS,
|
|
44
47
|
PROXY_OPENVIKING_ENDPOINT: () => PROXY_OPENVIKING_ENDPOINT,
|
|
48
|
+
TASKS_LIMIT_CAP: () => TASKS_LIMIT_CAP,
|
|
45
49
|
THEME: () => THEME,
|
|
50
|
+
WarningGlyph: () => WarningGlyph,
|
|
46
51
|
apply: () => apply,
|
|
47
52
|
chatNodesToText: () => chatNodesToText,
|
|
48
53
|
checkHealth: () => checkHealth,
|
|
49
54
|
commitSession: () => commitSession,
|
|
55
|
+
computeBreakdown: () => computeBreakdown,
|
|
50
56
|
defaultOpenVikingClient: () => defaultOpenVikingClient,
|
|
51
57
|
fetchSession: () => fetchSession,
|
|
58
|
+
formatBacklog: () => formatBacklog,
|
|
52
59
|
formatDaemonVersion: () => formatDaemonVersion,
|
|
60
|
+
formatDuration: () => formatDuration,
|
|
53
61
|
formatEndpoint: () => formatEndpoint,
|
|
62
|
+
formatExtractionSummary: () => formatExtractionSummary,
|
|
54
63
|
formatMemoryLeafName: () => formatMemoryLeafName,
|
|
55
64
|
formatPendingTokens: () => formatPendingTokens,
|
|
56
65
|
formatRelativeTime: () => formatRelativeTime,
|
|
57
66
|
formatStatusLabel: () => formatStatusLabel,
|
|
58
67
|
formatTooltipTitle: () => formatTooltipTitle,
|
|
59
68
|
getCategoryBadgeStyle: () => getCategoryBadgeStyle,
|
|
69
|
+
getChipDotState: () => getChipDotState,
|
|
70
|
+
getDotColorForState: () => getDotColorForState,
|
|
60
71
|
getProgressBarColor: () => getProgressBarColor,
|
|
61
72
|
getProgressBarPercent: () => getProgressBarPercent,
|
|
62
73
|
getSession: () => getSession,
|
|
@@ -65,9 +76,13 @@ __export(client_exports, {
|
|
|
65
76
|
inferCategory: () => inferCategory,
|
|
66
77
|
inject: () => inject,
|
|
67
78
|
name: () => name,
|
|
79
|
+
normalizeExecutionEvent: () => normalizeExecutionEvent,
|
|
80
|
+
normalizeExtractionTask: () => normalizeExtractionTask,
|
|
81
|
+
normalizeTaskList: () => normalizeTaskList,
|
|
68
82
|
parseRecalledMemories: () => parseRecalledMemories,
|
|
69
83
|
resolveApiKey: () => resolveApiKey,
|
|
70
84
|
resolveEndpoint: () => resolveEndpoint,
|
|
85
|
+
seedRunningTask: () => seedRunningTask,
|
|
71
86
|
themeVar: () => themeVar,
|
|
72
87
|
truncateSessionId: () => truncateSessionId
|
|
73
88
|
});
|
|
@@ -78,6 +93,71 @@ var import_react2 = __toESM(require("react"), 1);
|
|
|
78
93
|
var import_react_dom = __toESM(require("react-dom"), 1);
|
|
79
94
|
|
|
80
95
|
// src/client/api.ts
|
|
96
|
+
var TASKS_LIMIT_CAP = 200;
|
|
97
|
+
function normalizeExtractionTask(raw) {
|
|
98
|
+
if (!raw || typeof raw !== "object") return null;
|
|
99
|
+
const taskId = typeof raw.task_id === "string" && raw.task_id || typeof raw.id === "string" && raw.id || "";
|
|
100
|
+
if (!taskId) return null;
|
|
101
|
+
const statusRaw = typeof raw.status === "string" ? raw.status : "";
|
|
102
|
+
const status = statusRaw === "running" || statusRaw === "pending" || statusRaw === "completed" || statusRaw === "failed" ? statusRaw : "pending";
|
|
103
|
+
const result = raw.result ?? {};
|
|
104
|
+
const extracted = result.memories_extracted ?? {};
|
|
105
|
+
const numberOf = (v) => typeof v === "number" && Number.isFinite(v) ? v : void 0;
|
|
106
|
+
const tokenUsageRaw = result.token_usage ?? raw.token_usage;
|
|
107
|
+
const tokenUsage = typeof tokenUsageRaw === "number" ? tokenUsageRaw : numberOf(
|
|
108
|
+
tokenUsageRaw?.total_tokens
|
|
109
|
+
);
|
|
110
|
+
const errorRaw = raw.error;
|
|
111
|
+
const errorMsg = typeof errorRaw === "string" ? errorRaw : typeof errorRaw?.message === "string" ? errorRaw.message : void 0;
|
|
112
|
+
return {
|
|
113
|
+
task_id: taskId,
|
|
114
|
+
status,
|
|
115
|
+
resource_id: typeof raw.resource_id === "string" ? raw.resource_id : void 0,
|
|
116
|
+
created_at: typeof raw.created_at === "string" ? raw.created_at : void 0,
|
|
117
|
+
updated_at: typeof raw.updated_at === "string" ? raw.updated_at : void 0,
|
|
118
|
+
memory_write: numberOf(extracted.memory_write ?? extracted.written),
|
|
119
|
+
memory_edit: numberOf(extracted.memory_edit ?? extracted.edited),
|
|
120
|
+
token_usage: tokenUsage,
|
|
121
|
+
error: errorMsg
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function computeBreakdown(tasks) {
|
|
125
|
+
const b = {
|
|
126
|
+
running: 0,
|
|
127
|
+
pending: 0,
|
|
128
|
+
completed: 0,
|
|
129
|
+
failed: 0,
|
|
130
|
+
total: tasks.length,
|
|
131
|
+
firstRunning: null,
|
|
132
|
+
lastCompleted: null,
|
|
133
|
+
lastFailed: null
|
|
134
|
+
};
|
|
135
|
+
for (const t of tasks) {
|
|
136
|
+
if (t.status === "running") {
|
|
137
|
+
b.running += 1;
|
|
138
|
+
if (!b.firstRunning) b.firstRunning = t;
|
|
139
|
+
} else if (t.status === "pending") b.pending += 1;
|
|
140
|
+
else if (t.status === "completed") {
|
|
141
|
+
b.completed += 1;
|
|
142
|
+
if (!b.lastCompleted) b.lastCompleted = t;
|
|
143
|
+
} else if (t.status === "failed") {
|
|
144
|
+
b.failed += 1;
|
|
145
|
+
if (!b.lastFailed) b.lastFailed = t;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return b;
|
|
149
|
+
}
|
|
150
|
+
function normalizeExecutionEvent(raw) {
|
|
151
|
+
return {
|
|
152
|
+
seq: typeof raw.seq === "number" ? raw.seq : void 0,
|
|
153
|
+
recorded_at: typeof raw.recorded_at === "string" ? raw.recorded_at : void 0,
|
|
154
|
+
kind: typeof raw.kind === "string" ? raw.kind : void 0,
|
|
155
|
+
status: typeof raw.status === "string" ? raw.status : void 0,
|
|
156
|
+
stage: typeof raw.stage === "string" ? raw.stage : null,
|
|
157
|
+
operation: typeof raw.operation === "string" ? raw.operation : null,
|
|
158
|
+
error: typeof raw.error === "string" ? raw.error : null
|
|
159
|
+
};
|
|
160
|
+
}
|
|
81
161
|
var DEFAULT_OPENVIKING_ENDPOINT = "http://127.0.0.1:1933";
|
|
82
162
|
var PROXY_OPENVIKING_ENDPOINT = "/openviking-status/api";
|
|
83
163
|
function resolveEndpoint(endpoint) {
|
|
@@ -386,7 +466,9 @@ var OpenVikingClient = class {
|
|
|
386
466
|
const data = await res.json().catch(() => ({}));
|
|
387
467
|
return {
|
|
388
468
|
ok: data.ok === true,
|
|
389
|
-
error: typeof data.error === "string" ? data.error : void 0
|
|
469
|
+
error: typeof data.error === "string" ? data.error : void 0,
|
|
470
|
+
task_id: typeof data.task_id === "string" ? data.task_id : void 0,
|
|
471
|
+
resource_id: typeof data.resource_id === "string" ? data.resource_id : void 0
|
|
390
472
|
};
|
|
391
473
|
} catch (err) {
|
|
392
474
|
return {
|
|
@@ -418,7 +500,14 @@ var OpenVikingClient = class {
|
|
|
418
500
|
return { ok: false, error: String(errorMsg) };
|
|
419
501
|
}
|
|
420
502
|
this.resolvedSessionIds.set(sessionId.trim(), candidateId);
|
|
421
|
-
|
|
503
|
+
const okBody = await res.json().catch(() => ({}));
|
|
504
|
+
const container = okBody.result ?? okBody.data ?? okBody;
|
|
505
|
+
const taskId = container.task_id ?? container.id;
|
|
506
|
+
return {
|
|
507
|
+
ok: true,
|
|
508
|
+
task_id: typeof taskId === "string" && taskId.trim() ? taskId.trim() : void 0,
|
|
509
|
+
resource_id: candidateId
|
|
510
|
+
};
|
|
422
511
|
} catch (err) {
|
|
423
512
|
return {
|
|
424
513
|
ok: false,
|
|
@@ -428,7 +517,193 @@ var OpenVikingClient = class {
|
|
|
428
517
|
}
|
|
429
518
|
return { ok: false, error: lastError };
|
|
430
519
|
}
|
|
520
|
+
/**
|
|
521
|
+
* Список задач Phase 2 (Memory Extraction) текущей сессии со сводной
|
|
522
|
+
* разбивкой по статусам.
|
|
523
|
+
*
|
|
524
|
+
* Через прокси: один запрос `GET /tasks?session=<id>`, разбивка считается
|
|
525
|
+
* здесь. Напрямую: разрешаем `resource_id` через кандидатов и запрашиваем
|
|
526
|
+
* `GET /api/v1/tasks?resource_id=<id>&task_type=session_commit&limit=200`.
|
|
527
|
+
*/
|
|
528
|
+
async listTasks(sessionId) {
|
|
529
|
+
if (!sessionId || !sessionId.trim()) {
|
|
530
|
+
return { status: "missing" };
|
|
531
|
+
}
|
|
532
|
+
if (this.isProxy()) {
|
|
533
|
+
try {
|
|
534
|
+
const res = await fetch(
|
|
535
|
+
`${this.endpoint}/tasks?session=${encodeURIComponent(
|
|
536
|
+
sessionId.trim()
|
|
537
|
+
)}&limit=${TASKS_LIMIT_CAP}`,
|
|
538
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
539
|
+
);
|
|
540
|
+
if (res.status === 401 || res.status === 403) {
|
|
541
|
+
return { status: "unauthorized" };
|
|
542
|
+
}
|
|
543
|
+
if (!res.ok) {
|
|
544
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
545
|
+
}
|
|
546
|
+
const data = await res.json();
|
|
547
|
+
if (data.status === "unauthorized") return { status: "unauthorized" };
|
|
548
|
+
if (data.status === "missing") return { status: "missing" };
|
|
549
|
+
if (data.status === "unreachable")
|
|
550
|
+
return {
|
|
551
|
+
status: "unreachable",
|
|
552
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
553
|
+
};
|
|
554
|
+
if (data.status === "error")
|
|
555
|
+
return {
|
|
556
|
+
status: "error",
|
|
557
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
558
|
+
};
|
|
559
|
+
const tasks = normalizeTaskList(data.tasks);
|
|
560
|
+
return { status: "ok", breakdown: computeBreakdown(tasks), tasks };
|
|
561
|
+
} catch (err) {
|
|
562
|
+
return {
|
|
563
|
+
status: "unreachable",
|
|
564
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
const candidates = this.getCandidateSessionIds(sessionId);
|
|
569
|
+
let resourceId = null;
|
|
570
|
+
for (const candidateId of candidates) {
|
|
571
|
+
try {
|
|
572
|
+
const probe = await fetch(
|
|
573
|
+
`${this.endpoint}/api/v1/sessions/${encodeURIComponent(candidateId)}`,
|
|
574
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
575
|
+
);
|
|
576
|
+
if (probe.status === 404) continue;
|
|
577
|
+
if (probe.status === 401 || probe.status === 403) {
|
|
578
|
+
return { status: "unauthorized" };
|
|
579
|
+
}
|
|
580
|
+
if (probe.ok) {
|
|
581
|
+
resourceId = candidateId;
|
|
582
|
+
this.resolvedSessionIds.set(sessionId.trim(), candidateId);
|
|
583
|
+
break;
|
|
584
|
+
}
|
|
585
|
+
} catch (err) {
|
|
586
|
+
return {
|
|
587
|
+
status: "unreachable",
|
|
588
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
589
|
+
};
|
|
590
|
+
}
|
|
591
|
+
}
|
|
592
|
+
if (!resourceId) return { status: "missing" };
|
|
593
|
+
try {
|
|
594
|
+
const query = new URLSearchParams({
|
|
595
|
+
resource_id: resourceId,
|
|
596
|
+
task_type: "session_commit",
|
|
597
|
+
limit: String(TASKS_LIMIT_CAP)
|
|
598
|
+
});
|
|
599
|
+
const res = await fetch(
|
|
600
|
+
`${this.endpoint}/api/v1/tasks?${query.toString()}`,
|
|
601
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
602
|
+
);
|
|
603
|
+
if (res.status === 401 || res.status === 403) {
|
|
604
|
+
return { status: "unauthorized" };
|
|
605
|
+
}
|
|
606
|
+
if (!res.ok) {
|
|
607
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
608
|
+
}
|
|
609
|
+
const data = await res.json();
|
|
610
|
+
const items = data?.items ?? data?.tasks ?? data?.result?.items ?? (Array.isArray(data) ? data : []);
|
|
611
|
+
const tasks = normalizeTaskList(items);
|
|
612
|
+
return { status: "ok", breakdown: computeBreakdown(tasks), tasks };
|
|
613
|
+
} catch (err) {
|
|
614
|
+
return {
|
|
615
|
+
status: "unreachable",
|
|
616
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
/**
|
|
621
|
+
* Лениво загрузить одну задачу с лентой `execution_events` для «Show log».
|
|
622
|
+
*/
|
|
623
|
+
async fetchTaskEvents(taskId) {
|
|
624
|
+
if (!taskId || !taskId.trim()) {
|
|
625
|
+
return { status: "missing" };
|
|
626
|
+
}
|
|
627
|
+
const buildOk = (taskRaw) => {
|
|
628
|
+
const task = normalizeExtractionTask(taskRaw);
|
|
629
|
+
const eventsContainer = taskRaw.execution_events ?? {};
|
|
630
|
+
const rawEvents = eventsContainer.items ?? (Array.isArray(taskRaw.execution_events) ? taskRaw.execution_events : []);
|
|
631
|
+
const events = (Array.isArray(rawEvents) ? rawEvents : []).filter(
|
|
632
|
+
(e) => !!e && typeof e === "object"
|
|
633
|
+
).map(normalizeExecutionEvent);
|
|
634
|
+
if (!task) return { status: "error", detail: "malformed task" };
|
|
635
|
+
return { status: "ok", task, events };
|
|
636
|
+
};
|
|
637
|
+
if (this.isProxy()) {
|
|
638
|
+
try {
|
|
639
|
+
const res = await fetch(
|
|
640
|
+
`${this.endpoint}/task?id=${encodeURIComponent(
|
|
641
|
+
taskId.trim()
|
|
642
|
+
)}&events=1`,
|
|
643
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
644
|
+
);
|
|
645
|
+
if (res.status === 401 || res.status === 403) {
|
|
646
|
+
return { status: "unauthorized" };
|
|
647
|
+
}
|
|
648
|
+
if (!res.ok) {
|
|
649
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
650
|
+
}
|
|
651
|
+
const data = await res.json();
|
|
652
|
+
if (data.status === "unauthorized") return { status: "unauthorized" };
|
|
653
|
+
if (data.status === "missing") return { status: "missing" };
|
|
654
|
+
if (data.status === "unreachable")
|
|
655
|
+
return {
|
|
656
|
+
status: "unreachable",
|
|
657
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
658
|
+
};
|
|
659
|
+
if (data.status === "error")
|
|
660
|
+
return {
|
|
661
|
+
status: "error",
|
|
662
|
+
detail: typeof data.detail === "string" ? data.detail : void 0
|
|
663
|
+
};
|
|
664
|
+
return buildOk(data.task ?? {});
|
|
665
|
+
} catch (err) {
|
|
666
|
+
return {
|
|
667
|
+
status: "unreachable",
|
|
668
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
669
|
+
};
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
try {
|
|
673
|
+
const res = await fetch(
|
|
674
|
+
`${this.endpoint}/api/v1/tasks/${encodeURIComponent(
|
|
675
|
+
taskId.trim()
|
|
676
|
+
)}?include_events=true`,
|
|
677
|
+
{ method: "GET", headers: this.getHeaders() }
|
|
678
|
+
);
|
|
679
|
+
if (res.status === 404) return { status: "missing" };
|
|
680
|
+
if (res.status === 401 || res.status === 403) {
|
|
681
|
+
return { status: "unauthorized" };
|
|
682
|
+
}
|
|
683
|
+
if (!res.ok) {
|
|
684
|
+
return { status: "error", detail: `HTTP ${res.status}` };
|
|
685
|
+
}
|
|
686
|
+
const data = await res.json();
|
|
687
|
+
const taskRaw = data?.result ?? data?.data ?? data;
|
|
688
|
+
return buildOk(taskRaw);
|
|
689
|
+
} catch (err) {
|
|
690
|
+
return {
|
|
691
|
+
status: "unreachable",
|
|
692
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
}
|
|
431
696
|
};
|
|
697
|
+
function normalizeTaskList(raw) {
|
|
698
|
+
if (!Array.isArray(raw)) return [];
|
|
699
|
+
const tasks = [];
|
|
700
|
+
for (const entry of raw) {
|
|
701
|
+
if (!entry || typeof entry !== "object") continue;
|
|
702
|
+
const task = normalizeExtractionTask(entry);
|
|
703
|
+
if (task) tasks.push(task);
|
|
704
|
+
}
|
|
705
|
+
return tasks;
|
|
706
|
+
}
|
|
432
707
|
var defaultOpenVikingClient = new OpenVikingClient();
|
|
433
708
|
function checkHealth(endpoint, apiKey) {
|
|
434
709
|
const client = endpoint || apiKey ? new OpenVikingClient(endpoint, apiKey) : defaultOpenVikingClient;
|
|
@@ -802,6 +1077,39 @@ function dshIcon(name2) {
|
|
|
802
1077
|
return null;
|
|
803
1078
|
}
|
|
804
1079
|
}
|
|
1080
|
+
function formatDuration(startIso, endIso) {
|
|
1081
|
+
if (!startIso || !endIso) return void 0;
|
|
1082
|
+
const start = new Date(startIso).getTime();
|
|
1083
|
+
const end = new Date(endIso).getTime();
|
|
1084
|
+
if (isNaN(start) || isNaN(end) || end < start) return void 0;
|
|
1085
|
+
const ms = end - start;
|
|
1086
|
+
const sec = ms / 1e3;
|
|
1087
|
+
if (sec < 60) {
|
|
1088
|
+
return `${sec < 10 ? sec.toFixed(1) : Math.round(sec)}s`;
|
|
1089
|
+
}
|
|
1090
|
+
const min = Math.floor(sec / 60);
|
|
1091
|
+
const rem = Math.round(sec % 60);
|
|
1092
|
+
return `${min}m ${rem}s`;
|
|
1093
|
+
}
|
|
1094
|
+
function formatExtractionSummary(task, now = Date.now()) {
|
|
1095
|
+
const write = task.memory_write ?? 0;
|
|
1096
|
+
const edit = task.memory_edit ?? 0;
|
|
1097
|
+
const parts = [`${write} written, ${edit} edited`];
|
|
1098
|
+
const duration = formatDuration(task.created_at, task.updated_at);
|
|
1099
|
+
if (duration) parts.push(duration);
|
|
1100
|
+
const rel = formatRelativeTime(task.updated_at, now);
|
|
1101
|
+
if (rel) parts.push(rel);
|
|
1102
|
+
return parts.join(" \xB7 ");
|
|
1103
|
+
}
|
|
1104
|
+
function formatBacklog(breakdown) {
|
|
1105
|
+
if (!breakdown) return null;
|
|
1106
|
+
const { pending, failed } = breakdown;
|
|
1107
|
+
if (pending <= 0 && failed <= 0) return null;
|
|
1108
|
+
const parts = [];
|
|
1109
|
+
if (pending > 0) parts.push(`${pending} pending`);
|
|
1110
|
+
if (failed > 0) parts.push(`${failed} failed`);
|
|
1111
|
+
return parts.join(" / ");
|
|
1112
|
+
}
|
|
805
1113
|
function getProgressBarPercent(pendingTokens, threshold = COMMIT_THRESHOLD) {
|
|
806
1114
|
if (!threshold || threshold <= 0) return 0;
|
|
807
1115
|
const ratio = (pendingTokens || 0) / threshold;
|
|
@@ -868,6 +1176,229 @@ function handleEscapeKey(event, onClose) {
|
|
|
868
1176
|
}
|
|
869
1177
|
return false;
|
|
870
1178
|
}
|
|
1179
|
+
function ExtractionSection({
|
|
1180
|
+
breakdown,
|
|
1181
|
+
client
|
|
1182
|
+
}) {
|
|
1183
|
+
const [showLog, setShowLog] = (0, import_react.useState)(false);
|
|
1184
|
+
const [events, setEvents] = (0, import_react.useState)(null);
|
|
1185
|
+
const [logError, setLogError] = (0, import_react.useState)(null);
|
|
1186
|
+
const [loadingLog, setLoadingLog] = (0, import_react.useState)(false);
|
|
1187
|
+
if (!breakdown) return null;
|
|
1188
|
+
const isRunning = breakdown.running >= 1;
|
|
1189
|
+
const lastCompleted = breakdown.lastCompleted;
|
|
1190
|
+
const lastFailed = breakdown.lastFailed;
|
|
1191
|
+
const backlog = formatBacklog(breakdown);
|
|
1192
|
+
const failedActive = !isRunning && breakdown.failed >= 1;
|
|
1193
|
+
const logTaskId = (isRunning ? breakdown.firstRunning?.task_id : null) ?? (failedActive ? lastFailed?.task_id : null) ?? lastCompleted?.task_id ?? lastFailed?.task_id ?? null;
|
|
1194
|
+
const mutedStyle = { color: themeVar("labelTertiary") };
|
|
1195
|
+
const toggleLog = (0, import_react.useCallback)(async () => {
|
|
1196
|
+
const next = !showLog;
|
|
1197
|
+
setShowLog(next);
|
|
1198
|
+
if (next && events === null && logTaskId && client) {
|
|
1199
|
+
setLoadingLog(true);
|
|
1200
|
+
setLogError(null);
|
|
1201
|
+
const res = await client.fetchTaskEvents(logTaskId);
|
|
1202
|
+
setLoadingLog(false);
|
|
1203
|
+
if (res.status === "ok") {
|
|
1204
|
+
setEvents(res.events);
|
|
1205
|
+
} else {
|
|
1206
|
+
setLogError(
|
|
1207
|
+
res.status === "unauthorized" ? "No access to task log" : "Could not load log"
|
|
1208
|
+
);
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
}, [showLog, events, logTaskId, client]);
|
|
1212
|
+
if (!isRunning && !failedActive && !lastCompleted && !backlog) {
|
|
1213
|
+
return null;
|
|
1214
|
+
}
|
|
1215
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { "data-testid": "extraction-section", style: { marginBottom: "12px" }, children: [
|
|
1216
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1217
|
+
"div",
|
|
1218
|
+
{
|
|
1219
|
+
style: {
|
|
1220
|
+
marginBottom: "4px",
|
|
1221
|
+
color: themeVar("labelPrimary"),
|
|
1222
|
+
fontWeight: 500
|
|
1223
|
+
},
|
|
1224
|
+
children: "Memory Extraction"
|
|
1225
|
+
}
|
|
1226
|
+
),
|
|
1227
|
+
isRunning ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { children: [
|
|
1228
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1229
|
+
"div",
|
|
1230
|
+
{
|
|
1231
|
+
style: {
|
|
1232
|
+
display: "flex",
|
|
1233
|
+
alignItems: "center",
|
|
1234
|
+
gap: "6px",
|
|
1235
|
+
marginBottom: "4px"
|
|
1236
|
+
},
|
|
1237
|
+
children: [
|
|
1238
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1239
|
+
"span",
|
|
1240
|
+
{
|
|
1241
|
+
"data-testid": "extraction-status-dot",
|
|
1242
|
+
"aria-hidden": "true",
|
|
1243
|
+
style: {
|
|
1244
|
+
width: "6px",
|
|
1245
|
+
height: "6px",
|
|
1246
|
+
borderRadius: "50%",
|
|
1247
|
+
background: themeVar("stateSuccess"),
|
|
1248
|
+
animation: "ov-pulse 1.2s ease-in-out infinite",
|
|
1249
|
+
flexShrink: 0
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
),
|
|
1253
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "data-testid": "extraction-running-label", children: "Extracting\u2026" })
|
|
1254
|
+
]
|
|
1255
|
+
}
|
|
1256
|
+
),
|
|
1257
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1258
|
+
"div",
|
|
1259
|
+
{
|
|
1260
|
+
"data-testid": "extraction-indeterminate-track",
|
|
1261
|
+
style: {
|
|
1262
|
+
width: "100%",
|
|
1263
|
+
height: "6px",
|
|
1264
|
+
borderRadius: "3px",
|
|
1265
|
+
backgroundColor: themeVar("insetSurface"),
|
|
1266
|
+
overflow: "hidden",
|
|
1267
|
+
position: "relative"
|
|
1268
|
+
},
|
|
1269
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1270
|
+
"div",
|
|
1271
|
+
{
|
|
1272
|
+
"data-testid": "extraction-indeterminate-fill",
|
|
1273
|
+
style: {
|
|
1274
|
+
position: "absolute",
|
|
1275
|
+
left: 0,
|
|
1276
|
+
top: 0,
|
|
1277
|
+
height: "100%",
|
|
1278
|
+
width: "40%",
|
|
1279
|
+
borderRadius: "3px",
|
|
1280
|
+
backgroundColor: themeVar("stateSuccess"),
|
|
1281
|
+
animation: "ov-indeterminate 1.2s ease-in-out infinite"
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
)
|
|
1285
|
+
}
|
|
1286
|
+
)
|
|
1287
|
+
] }) : failedActive && lastFailed ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1288
|
+
"div",
|
|
1289
|
+
{
|
|
1290
|
+
"data-testid": "extraction-failed-line",
|
|
1291
|
+
style: {
|
|
1292
|
+
display: "flex",
|
|
1293
|
+
alignItems: "center",
|
|
1294
|
+
gap: "6px",
|
|
1295
|
+
color: themeVar("stateWarning")
|
|
1296
|
+
},
|
|
1297
|
+
children: [
|
|
1298
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1299
|
+
"span",
|
|
1300
|
+
{
|
|
1301
|
+
"aria-hidden": "true",
|
|
1302
|
+
style: { display: "inline-flex", flexShrink: 0 },
|
|
1303
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(WarningGlyph, { size: 12, testId: "extraction-warning-glyph" })
|
|
1304
|
+
}
|
|
1305
|
+
),
|
|
1306
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "data-testid": "extraction-failed-text", children: lastFailed.error || "extraction failed" })
|
|
1307
|
+
]
|
|
1308
|
+
}
|
|
1309
|
+
) : lastCompleted ? /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1310
|
+
"div",
|
|
1311
|
+
{
|
|
1312
|
+
style: {
|
|
1313
|
+
display: "flex",
|
|
1314
|
+
alignItems: "center",
|
|
1315
|
+
gap: "6px"
|
|
1316
|
+
},
|
|
1317
|
+
children: [
|
|
1318
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1319
|
+
"span",
|
|
1320
|
+
{
|
|
1321
|
+
"data-testid": "extraction-status-dot",
|
|
1322
|
+
"aria-hidden": "true",
|
|
1323
|
+
style: {
|
|
1324
|
+
width: "6px",
|
|
1325
|
+
height: "6px",
|
|
1326
|
+
borderRadius: "50%",
|
|
1327
|
+
background: themeVar("stateSuccess"),
|
|
1328
|
+
flexShrink: 0
|
|
1329
|
+
}
|
|
1330
|
+
}
|
|
1331
|
+
),
|
|
1332
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { "data-testid": "extraction-last-line", children: formatExtractionSummary(lastCompleted) })
|
|
1333
|
+
]
|
|
1334
|
+
}
|
|
1335
|
+
) : null,
|
|
1336
|
+
backlog && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1337
|
+
"div",
|
|
1338
|
+
{
|
|
1339
|
+
"data-testid": "extraction-backlog-line",
|
|
1340
|
+
style: { ...mutedStyle, marginTop: "4px" },
|
|
1341
|
+
children: backlog
|
|
1342
|
+
}
|
|
1343
|
+
),
|
|
1344
|
+
logTaskId && client && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { marginTop: "6px" }, children: [
|
|
1345
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1346
|
+
"button",
|
|
1347
|
+
{
|
|
1348
|
+
type: "button",
|
|
1349
|
+
"data-testid": "extraction-show-log-btn",
|
|
1350
|
+
onClick: () => void toggleLog(),
|
|
1351
|
+
style: {
|
|
1352
|
+
background: "none",
|
|
1353
|
+
border: "none",
|
|
1354
|
+
padding: 0,
|
|
1355
|
+
cursor: "pointer",
|
|
1356
|
+
font: "inherit",
|
|
1357
|
+
color: themeVar("labelTertiary"),
|
|
1358
|
+
textDecoration: "underline"
|
|
1359
|
+
},
|
|
1360
|
+
"aria-expanded": showLog,
|
|
1361
|
+
children: showLog ? "Hide log" : "Show log"
|
|
1362
|
+
}
|
|
1363
|
+
),
|
|
1364
|
+
showLog && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1365
|
+
"div",
|
|
1366
|
+
{
|
|
1367
|
+
"data-testid": "extraction-log",
|
|
1368
|
+
style: {
|
|
1369
|
+
marginTop: "6px",
|
|
1370
|
+
display: "flex",
|
|
1371
|
+
flexDirection: "column",
|
|
1372
|
+
gap: "2px",
|
|
1373
|
+
maxHeight: "120px",
|
|
1374
|
+
overflowY: "auto"
|
|
1375
|
+
},
|
|
1376
|
+
children: [
|
|
1377
|
+
loadingLog && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-testid": "extraction-log-loading", style: mutedStyle, children: "Loading\u2026" }),
|
|
1378
|
+
logError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-testid": "extraction-log-error", style: mutedStyle, children: logError }),
|
|
1379
|
+
events && events.length === 0 && !loadingLog && !logError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("div", { "data-testid": "extraction-log-empty", style: mutedStyle, children: "No events" }),
|
|
1380
|
+
events?.map((ev, idx) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
1381
|
+
"div",
|
|
1382
|
+
{
|
|
1383
|
+
"data-testid": "extraction-log-event",
|
|
1384
|
+
style: {
|
|
1385
|
+
display: "flex",
|
|
1386
|
+
justifyContent: "space-between",
|
|
1387
|
+
gap: "8px"
|
|
1388
|
+
},
|
|
1389
|
+
children: [
|
|
1390
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { children: ev.status || ev.kind || "event" }),
|
|
1391
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { style: mutedStyle, children: formatRelativeTime(ev.recorded_at) || "" })
|
|
1392
|
+
]
|
|
1393
|
+
},
|
|
1394
|
+
`${ev.seq ?? idx}-${ev.recorded_at ?? idx}`
|
|
1395
|
+
))
|
|
1396
|
+
]
|
|
1397
|
+
}
|
|
1398
|
+
)
|
|
1399
|
+
] })
|
|
1400
|
+
] });
|
|
1401
|
+
}
|
|
871
1402
|
function OpenVikingStatusPopover({
|
|
872
1403
|
sessionId,
|
|
873
1404
|
health,
|
|
@@ -877,6 +1408,8 @@ function OpenVikingStatusPopover({
|
|
|
877
1408
|
endpoint,
|
|
878
1409
|
isCommitting = false,
|
|
879
1410
|
commitError = null,
|
|
1411
|
+
breakdown = null,
|
|
1412
|
+
client,
|
|
880
1413
|
onCommitNow,
|
|
881
1414
|
onClose,
|
|
882
1415
|
className,
|
|
@@ -957,7 +1490,7 @@ function OpenVikingStatusPopover({
|
|
|
957
1490
|
...style
|
|
958
1491
|
},
|
|
959
1492
|
children: [
|
|
960
|
-
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `@keyframes ov-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } }` }),
|
|
1493
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("style", { children: `@keyframes ov-spin { from { transform: rotate(0deg); } to { transform: rotate(360deg); } } @keyframes ov-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.35; transform: scale(0.72); } } @keyframes ov-indeterminate { 0% { left: -40%; } 100% { left: 100%; } }` }),
|
|
961
1494
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
962
1495
|
"div",
|
|
963
1496
|
{
|
|
@@ -1240,6 +1773,7 @@ function OpenVikingStatusPopover({
|
|
|
1240
1773
|
}
|
|
1241
1774
|
)
|
|
1242
1775
|
] }),
|
|
1776
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)(ExtractionSection, { breakdown, client }),
|
|
1243
1777
|
commitError && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
1244
1778
|
"div",
|
|
1245
1779
|
{
|
|
@@ -1297,6 +1831,77 @@ function OpenVikingStatusPopover({
|
|
|
1297
1831
|
// src/client/OpenVikingStatusChip.tsx
|
|
1298
1832
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
1299
1833
|
var COMMIT_THRESHOLD = 2e4;
|
|
1834
|
+
var POLL_INTERVAL_IDLE_MS = 15e3;
|
|
1835
|
+
var POLL_INTERVAL_ACTIVE_MS = 2500;
|
|
1836
|
+
function getChipDotState({
|
|
1837
|
+
isOnline,
|
|
1838
|
+
sessionUnreadable,
|
|
1839
|
+
breakdown
|
|
1840
|
+
}) {
|
|
1841
|
+
if (!isOnline) return "offline";
|
|
1842
|
+
if (sessionUnreadable) return "session-unreadable";
|
|
1843
|
+
if (breakdown && breakdown.running >= 1) return "busy";
|
|
1844
|
+
if (breakdown && breakdown.failed >= 1) return "extraction-failed";
|
|
1845
|
+
return "online-idle";
|
|
1846
|
+
}
|
|
1847
|
+
function WarningGlyph({
|
|
1848
|
+
size = 11,
|
|
1849
|
+
testId = "extraction-warning-glyph"
|
|
1850
|
+
}) {
|
|
1851
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1852
|
+
"svg",
|
|
1853
|
+
{
|
|
1854
|
+
"data-testid": testId,
|
|
1855
|
+
width: size,
|
|
1856
|
+
height: size,
|
|
1857
|
+
viewBox: "0 0 16 16",
|
|
1858
|
+
fill: "none",
|
|
1859
|
+
"aria-hidden": "true",
|
|
1860
|
+
style: { flexShrink: 0, display: "block" },
|
|
1861
|
+
children: [
|
|
1862
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "8", r: "6.5", stroke: "currentColor", strokeWidth: "1.5" }),
|
|
1863
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1864
|
+
"path",
|
|
1865
|
+
{
|
|
1866
|
+
d: "M8 5v3.5",
|
|
1867
|
+
stroke: "currentColor",
|
|
1868
|
+
strokeWidth: "1.5",
|
|
1869
|
+
strokeLinecap: "round"
|
|
1870
|
+
}
|
|
1871
|
+
),
|
|
1872
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: "8", cy: "11", r: "0.9", fill: "currentColor" })
|
|
1873
|
+
]
|
|
1874
|
+
}
|
|
1875
|
+
);
|
|
1876
|
+
}
|
|
1877
|
+
function seedRunningTask(prev, taskId, resourceId) {
|
|
1878
|
+
const existing = prev && prev.status === "ok" ? prev.tasks : [];
|
|
1879
|
+
if (existing.some((t) => t.task_id === taskId)) {
|
|
1880
|
+
return prev;
|
|
1881
|
+
}
|
|
1882
|
+
const seeded = {
|
|
1883
|
+
task_id: taskId,
|
|
1884
|
+
status: "running",
|
|
1885
|
+
resource_id: resourceId,
|
|
1886
|
+
updated_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
1887
|
+
};
|
|
1888
|
+
const tasks = [seeded, ...existing];
|
|
1889
|
+
return { status: "ok", breakdown: computeBreakdown(tasks), tasks };
|
|
1890
|
+
}
|
|
1891
|
+
function getDotColorForState(state) {
|
|
1892
|
+
switch (state) {
|
|
1893
|
+
case "offline":
|
|
1894
|
+
return themeVar("stateError");
|
|
1895
|
+
case "session-unreadable":
|
|
1896
|
+
case "extraction-failed":
|
|
1897
|
+
return themeVar("stateWarning");
|
|
1898
|
+
case "busy":
|
|
1899
|
+
return themeVar("stateSuccess");
|
|
1900
|
+
case "online-idle":
|
|
1901
|
+
default:
|
|
1902
|
+
return themeVar("stateSuccess");
|
|
1903
|
+
}
|
|
1904
|
+
}
|
|
1300
1905
|
function formatPendingTokens(pendingTokens) {
|
|
1301
1906
|
const k = Math.round((pendingTokens || 0) / 1e3);
|
|
1302
1907
|
return `${k}k pend`;
|
|
@@ -1399,6 +2004,7 @@ function StatusChipView({
|
|
|
1399
2004
|
initialHealth,
|
|
1400
2005
|
initialSessionData,
|
|
1401
2006
|
initialSessionRead,
|
|
2007
|
+
initialTasksRead,
|
|
1402
2008
|
initialOpen = false
|
|
1403
2009
|
}) {
|
|
1404
2010
|
const [health, setHealth] = (0, import_react2.useState)(
|
|
@@ -1407,6 +2013,9 @@ function StatusChipView({
|
|
|
1407
2013
|
const [sessionRead, setSessionRead] = (0, import_react2.useState)(
|
|
1408
2014
|
initialSessionRead ?? (initialSessionData ? { status: "ok", session: initialSessionData } : null)
|
|
1409
2015
|
);
|
|
2016
|
+
const [tasksRead, setTasksRead] = (0, import_react2.useState)(
|
|
2017
|
+
initialTasksRead ?? null
|
|
2018
|
+
);
|
|
1410
2019
|
const [isOpen, setIsOpen] = (0, import_react2.useState)(initialOpen);
|
|
1411
2020
|
const [isCommitting, setIsCommitting] = (0, import_react2.useState)(false);
|
|
1412
2021
|
const [commitError, setCommitError] = (0, import_react2.useState)(null);
|
|
@@ -1442,16 +2051,24 @@ function StatusChipView({
|
|
|
1442
2051
|
if (!healthRes.ok) {
|
|
1443
2052
|
return;
|
|
1444
2053
|
}
|
|
1445
|
-
|
|
2054
|
+
const [session, tasks] = await Promise.all([
|
|
2055
|
+
apiClient.readSession(sessionId),
|
|
2056
|
+
apiClient.listTasks(sessionId)
|
|
2057
|
+
]);
|
|
2058
|
+
setSessionRead(session);
|
|
2059
|
+
setTasksRead(tasks);
|
|
1446
2060
|
} catch {
|
|
1447
2061
|
setHealth({ ok: false });
|
|
1448
2062
|
}
|
|
1449
2063
|
}, [sessionId, apiClient]);
|
|
2064
|
+
const breakdown = tasksRead?.status === "ok" ? tasksRead.breakdown : null;
|
|
2065
|
+
const hasRunning = (breakdown?.running ?? 0) >= 1;
|
|
1450
2066
|
(0, import_react2.useEffect)(() => {
|
|
1451
2067
|
fetchStatus();
|
|
1452
|
-
const
|
|
2068
|
+
const interval = hasRunning ? POLL_INTERVAL_ACTIVE_MS : POLL_INTERVAL_IDLE_MS;
|
|
2069
|
+
const timer = setInterval(fetchStatus, interval);
|
|
1453
2070
|
return () => clearInterval(timer);
|
|
1454
|
-
}, [fetchStatus]);
|
|
2071
|
+
}, [fetchStatus, hasRunning]);
|
|
1455
2072
|
(0, import_react2.useEffect)(() => {
|
|
1456
2073
|
function handleClickOutside(event) {
|
|
1457
2074
|
if (popoverRef.current && !popoverRef.current.contains(event.target)) {
|
|
@@ -1481,6 +2098,11 @@ function StatusChipView({
|
|
|
1481
2098
|
keep_recent_count: 10
|
|
1482
2099
|
});
|
|
1483
2100
|
if (res.ok) {
|
|
2101
|
+
if (res.task_id) {
|
|
2102
|
+
setTasksRead(
|
|
2103
|
+
(prev) => seedRunningTask(prev, res.task_id, res.resource_id)
|
|
2104
|
+
);
|
|
2105
|
+
}
|
|
1484
2106
|
await fetchStatus();
|
|
1485
2107
|
onCommit?.();
|
|
1486
2108
|
} else {
|
|
@@ -1499,11 +2121,15 @@ function StatusChipView({
|
|
|
1499
2121
|
const sessionUnreadable = isOnline && sessionRead !== null && sessionRead.status !== "ok";
|
|
1500
2122
|
const pendingTokens = sessionData?.pending_tokens ?? 0;
|
|
1501
2123
|
const recalledCount = recalledResult.recalledCount;
|
|
1502
|
-
const
|
|
2124
|
+
const rawDotState = getChipDotState({
|
|
1503
2125
|
isOnline,
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
);
|
|
2126
|
+
sessionUnreadable,
|
|
2127
|
+
breakdown
|
|
2128
|
+
});
|
|
2129
|
+
const dotState = isCommitting && (rawDotState === "online-idle" || rawDotState === "busy") ? "busy" : rawDotState;
|
|
2130
|
+
const dotBusy = dotState === "busy";
|
|
2131
|
+
const dotFailed = dotState === "extraction-failed";
|
|
2132
|
+
const statusColor = getDotColorForState(dotState);
|
|
1507
2133
|
const tooltipTitle = formatTooltipTitle({
|
|
1508
2134
|
isOnline,
|
|
1509
2135
|
isCommitting,
|
|
@@ -1520,6 +2146,7 @@ function StatusChipView({
|
|
|
1520
2146
|
style: { minWidth: 0, display: "inline-flex", position: "relative" },
|
|
1521
2147
|
ref: popoverRef,
|
|
1522
2148
|
children: [
|
|
2149
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("style", { children: `@keyframes ov-pulse { 0%, 100% { opacity: 1; transform: scale(1); } 50% { opacity: 0.35; transform: scale(0.72); } }` }),
|
|
1523
2150
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
1524
2151
|
"button",
|
|
1525
2152
|
{
|
|
@@ -1550,17 +2177,32 @@ function StatusChipView({
|
|
|
1550
2177
|
title: tooltipTitle,
|
|
1551
2178
|
"aria-label": tooltipTitle,
|
|
1552
2179
|
children: [
|
|
1553
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2180
|
+
dotFailed ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
1554
2181
|
"span",
|
|
1555
2182
|
{
|
|
1556
2183
|
"data-testid": "status-dot",
|
|
2184
|
+
"data-dot-state": dotState,
|
|
2185
|
+
"aria-hidden": "true",
|
|
2186
|
+
style: {
|
|
2187
|
+
display: "inline-flex",
|
|
2188
|
+
color: statusColor,
|
|
2189
|
+
flexShrink: 0
|
|
2190
|
+
},
|
|
2191
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(WarningGlyph, { size: 11, testId: "chip-warning-glyph" })
|
|
2192
|
+
}
|
|
2193
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
2194
|
+
"span",
|
|
2195
|
+
{
|
|
2196
|
+
"data-testid": "status-dot",
|
|
2197
|
+
"data-dot-state": dotBusy ? "busy" : dotState,
|
|
1557
2198
|
"aria-hidden": "true",
|
|
1558
2199
|
style: {
|
|
1559
2200
|
width: "6px",
|
|
1560
2201
|
height: "6px",
|
|
1561
2202
|
borderRadius: "50%",
|
|
1562
2203
|
background: statusColor,
|
|
1563
|
-
flexShrink: 0
|
|
2204
|
+
flexShrink: 0,
|
|
2205
|
+
...dotBusy ? { animation: "ov-pulse 1.2s ease-in-out infinite" } : {}
|
|
1564
2206
|
}
|
|
1565
2207
|
}
|
|
1566
2208
|
),
|
|
@@ -1579,6 +2221,8 @@ function StatusChipView({
|
|
|
1579
2221
|
endpoint: apiClient.endpoint,
|
|
1580
2222
|
isCommitting,
|
|
1581
2223
|
commitError,
|
|
2224
|
+
breakdown,
|
|
2225
|
+
client: apiClient,
|
|
1582
2226
|
onCommitNow: handleCommitNow,
|
|
1583
2227
|
onClose: () => setIsOpen(false)
|
|
1584
2228
|
}
|