@absolutejs/voice 0.0.22-beta.153 → 0.0.22-beta.154
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 +37 -0
- package/dist/angular/index.d.ts +1 -0
- package/dist/angular/index.js +349 -140
- package/dist/angular/voice-ops-action-center.service.d.ts +13 -0
- package/dist/client/index.d.ts +4 -0
- package/dist/client/index.js +370 -114
- package/dist/client/opsActionCenter.d.ts +50 -0
- package/dist/client/opsActionCenterWidget.d.ts +29 -0
- package/dist/react/VoiceOpsActionCenter.d.ts +5 -0
- package/dist/react/index.d.ts +2 -0
- package/dist/react/index.js +611 -289
- package/dist/react/useVoiceOpsActionCenter.d.ts +11 -0
- package/dist/svelte/createVoiceOpsActionCenter.d.ts +10 -0
- package/dist/svelte/index.d.ts +1 -0
- package/dist/svelte/index.js +378 -114
- package/dist/vue/VoiceOpsActionCenter.d.ts +13 -0
- package/dist/vue/index.d.ts +2 -0
- package/dist/vue/index.js +639 -298
- package/dist/vue/useVoiceOpsActionCenter.d.ts +11 -0
- package/package.json +1 -1
package/dist/svelte/index.js
CHANGED
|
@@ -468,6 +468,269 @@ var createVoiceDeliveryRuntime = (path = "/api/voice-delivery-runtime", options
|
|
|
468
468
|
tick: store.tick
|
|
469
469
|
};
|
|
470
470
|
};
|
|
471
|
+
// src/client/opsActionCenter.ts
|
|
472
|
+
var createVoiceOpsActionCenterActions = (options = {}) => {
|
|
473
|
+
const deliveryRuntimePath = options.deliveryRuntimePath ?? "/api/voice-delivery-runtime";
|
|
474
|
+
const actions = [];
|
|
475
|
+
if (options.includeProductionReadiness !== false) {
|
|
476
|
+
actions.push({
|
|
477
|
+
description: "Refresh the production readiness report.",
|
|
478
|
+
id: "production-readiness",
|
|
479
|
+
label: "Refresh readiness",
|
|
480
|
+
method: "GET",
|
|
481
|
+
path: options.productionReadinessPath ?? "/api/production-readiness"
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
if (options.includeDeliveryRuntime !== false) {
|
|
485
|
+
actions.push({
|
|
486
|
+
description: "Drain pending and failed audit/trace deliveries.",
|
|
487
|
+
id: "delivery-runtime.tick",
|
|
488
|
+
label: "Tick delivery workers",
|
|
489
|
+
method: "POST",
|
|
490
|
+
path: `${deliveryRuntimePath.replace(/\/$/, "")}/tick`
|
|
491
|
+
}, {
|
|
492
|
+
description: "Move reviewed dead letters back to live delivery queues.",
|
|
493
|
+
id: "delivery-runtime.requeue-dead-letters",
|
|
494
|
+
label: "Requeue dead letters",
|
|
495
|
+
method: "POST",
|
|
496
|
+
path: `${deliveryRuntimePath.replace(/\/$/, "")}/requeue-dead-letters`
|
|
497
|
+
});
|
|
498
|
+
}
|
|
499
|
+
if (options.includeTurnLatencyProof !== false) {
|
|
500
|
+
actions.push({
|
|
501
|
+
description: "Run the synthetic turn latency proof.",
|
|
502
|
+
id: "turn-latency.proof",
|
|
503
|
+
label: "Run latency proof",
|
|
504
|
+
method: "POST",
|
|
505
|
+
path: options.turnLatencyProofPath ?? "/api/turn-latency/proof"
|
|
506
|
+
});
|
|
507
|
+
}
|
|
508
|
+
if (options.includeProviderSimulation !== false) {
|
|
509
|
+
const pathPrefix = options.providerSimulationPathPrefix ?? "/api/stt-simulate";
|
|
510
|
+
for (const provider of options.providers ?? []) {
|
|
511
|
+
actions.push({
|
|
512
|
+
description: `Simulate ${provider} provider failure.`,
|
|
513
|
+
id: `provider.${provider}.failure`,
|
|
514
|
+
label: `Simulate ${provider} failure`,
|
|
515
|
+
method: "POST",
|
|
516
|
+
path: `${pathPrefix}/failure?provider=${encodeURIComponent(provider)}`
|
|
517
|
+
}, {
|
|
518
|
+
description: `Mark ${provider} provider recovered.`,
|
|
519
|
+
id: `provider.${provider}.recovery`,
|
|
520
|
+
label: `Recover ${provider}`,
|
|
521
|
+
method: "POST",
|
|
522
|
+
path: `${pathPrefix}/recovery?provider=${encodeURIComponent(provider)}`
|
|
523
|
+
});
|
|
524
|
+
}
|
|
525
|
+
}
|
|
526
|
+
return actions;
|
|
527
|
+
};
|
|
528
|
+
var runVoiceOpsAction = async (action, options = {}) => {
|
|
529
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
530
|
+
const response = await fetchImpl(action.path, {
|
|
531
|
+
method: action.method ?? "POST"
|
|
532
|
+
});
|
|
533
|
+
const body = await response.json().catch(() => null);
|
|
534
|
+
if (!response.ok) {
|
|
535
|
+
const message = body && typeof body === "object" && "error" in body ? String(body.error) : `Voice ops action "${action.id}" failed: HTTP ${response.status}`;
|
|
536
|
+
throw new Error(message);
|
|
537
|
+
}
|
|
538
|
+
return {
|
|
539
|
+
actionId: action.id,
|
|
540
|
+
body,
|
|
541
|
+
ok: response.ok,
|
|
542
|
+
ranAt: Date.now(),
|
|
543
|
+
status: response.status
|
|
544
|
+
};
|
|
545
|
+
};
|
|
546
|
+
var createVoiceOpsActionCenterStore = (options = {}) => {
|
|
547
|
+
const listeners = new Set;
|
|
548
|
+
let closed = false;
|
|
549
|
+
let timer;
|
|
550
|
+
let snapshot = {
|
|
551
|
+
actions: options.actions ?? createVoiceOpsActionCenterActions(),
|
|
552
|
+
error: null,
|
|
553
|
+
isRunning: false
|
|
554
|
+
};
|
|
555
|
+
const emit = () => {
|
|
556
|
+
for (const listener of listeners) {
|
|
557
|
+
listener();
|
|
558
|
+
}
|
|
559
|
+
};
|
|
560
|
+
const setActions = (actions) => {
|
|
561
|
+
snapshot = { ...snapshot, actions, updatedAt: Date.now() };
|
|
562
|
+
emit();
|
|
563
|
+
};
|
|
564
|
+
const run = async (actionId) => {
|
|
565
|
+
if (closed) {
|
|
566
|
+
return snapshot.lastResult;
|
|
567
|
+
}
|
|
568
|
+
const action = snapshot.actions.find((item) => item.id === actionId);
|
|
569
|
+
if (!action) {
|
|
570
|
+
throw new Error(`Voice ops action "${actionId}" is not configured.`);
|
|
571
|
+
}
|
|
572
|
+
if (action.disabled) {
|
|
573
|
+
throw new Error(`Voice ops action "${actionId}" is disabled.`);
|
|
574
|
+
}
|
|
575
|
+
snapshot = {
|
|
576
|
+
...snapshot,
|
|
577
|
+
error: null,
|
|
578
|
+
isRunning: true,
|
|
579
|
+
runningActionId: action.id
|
|
580
|
+
};
|
|
581
|
+
emit();
|
|
582
|
+
try {
|
|
583
|
+
const result = await runVoiceOpsAction(action, options);
|
|
584
|
+
snapshot = {
|
|
585
|
+
...snapshot,
|
|
586
|
+
error: null,
|
|
587
|
+
isRunning: false,
|
|
588
|
+
lastResult: result,
|
|
589
|
+
runningActionId: undefined,
|
|
590
|
+
updatedAt: Date.now()
|
|
591
|
+
};
|
|
592
|
+
emit();
|
|
593
|
+
return result;
|
|
594
|
+
} catch (error) {
|
|
595
|
+
snapshot = {
|
|
596
|
+
...snapshot,
|
|
597
|
+
error: error instanceof Error ? error.message : String(error),
|
|
598
|
+
isRunning: false,
|
|
599
|
+
runningActionId: undefined
|
|
600
|
+
};
|
|
601
|
+
emit();
|
|
602
|
+
throw error;
|
|
603
|
+
}
|
|
604
|
+
};
|
|
605
|
+
const close = () => {
|
|
606
|
+
closed = true;
|
|
607
|
+
if (timer) {
|
|
608
|
+
clearInterval(timer);
|
|
609
|
+
timer = undefined;
|
|
610
|
+
}
|
|
611
|
+
listeners.clear();
|
|
612
|
+
};
|
|
613
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
614
|
+
timer = setInterval(() => {
|
|
615
|
+
emit();
|
|
616
|
+
}, options.intervalMs);
|
|
617
|
+
}
|
|
618
|
+
return {
|
|
619
|
+
close,
|
|
620
|
+
getServerSnapshot: () => snapshot,
|
|
621
|
+
getSnapshot: () => snapshot,
|
|
622
|
+
run,
|
|
623
|
+
setActions,
|
|
624
|
+
subscribe: (listener) => {
|
|
625
|
+
listeners.add(listener);
|
|
626
|
+
return () => {
|
|
627
|
+
listeners.delete(listener);
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
};
|
|
632
|
+
|
|
633
|
+
// src/client/opsActionCenterWidget.ts
|
|
634
|
+
var DEFAULT_TITLE2 = "Voice Ops Action Center";
|
|
635
|
+
var DEFAULT_DESCRIPTION2 = "Run production voice proofs and operator actions from one primitive panel.";
|
|
636
|
+
var escapeHtml2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
637
|
+
var createVoiceOpsActionCenterViewModel = (snapshot, options = {}) => {
|
|
638
|
+
const status = snapshot.error ? "error" : snapshot.isRunning ? "running" : snapshot.lastResult ? "completed" : "ready";
|
|
639
|
+
return {
|
|
640
|
+
actions: snapshot.actions.map((action) => ({
|
|
641
|
+
description: action.description ?? "",
|
|
642
|
+
disabled: Boolean(action.disabled || snapshot.isRunning),
|
|
643
|
+
id: action.id,
|
|
644
|
+
isRunning: snapshot.runningActionId === action.id,
|
|
645
|
+
label: action.label
|
|
646
|
+
})),
|
|
647
|
+
description: options.description ?? DEFAULT_DESCRIPTION2,
|
|
648
|
+
error: snapshot.error,
|
|
649
|
+
isRunning: snapshot.isRunning,
|
|
650
|
+
label: status === "error" ? "Needs attention" : status === "running" ? "Running" : status === "completed" ? "Action completed" : "Ready",
|
|
651
|
+
lastResultLabel: snapshot.lastResult ? `${snapshot.lastResult.actionId} returned HTTP ${snapshot.lastResult.status}` : "No action has run yet.",
|
|
652
|
+
status,
|
|
653
|
+
title: options.title ?? DEFAULT_TITLE2
|
|
654
|
+
};
|
|
655
|
+
};
|
|
656
|
+
var renderVoiceOpsActionCenterHTML = (snapshot, options = {}) => {
|
|
657
|
+
const model = createVoiceOpsActionCenterViewModel(snapshot, options);
|
|
658
|
+
const actions = model.actions.map((action) => `<button type="button" data-absolute-voice-ops-action="${escapeHtml2(action.id)}"${action.disabled ? " disabled" : ""}>
|
|
659
|
+
${escapeHtml2(action.isRunning ? "Working..." : action.label)}
|
|
660
|
+
</button>`).join("");
|
|
661
|
+
return `<section class="absolute-voice-ops-action-center absolute-voice-ops-action-center--${escapeHtml2(model.status)}">
|
|
662
|
+
<header class="absolute-voice-ops-action-center__header">
|
|
663
|
+
<span class="absolute-voice-ops-action-center__eyebrow">${escapeHtml2(model.title)}</span>
|
|
664
|
+
<strong class="absolute-voice-ops-action-center__label">${escapeHtml2(model.label)}</strong>
|
|
665
|
+
</header>
|
|
666
|
+
<p class="absolute-voice-ops-action-center__description">${escapeHtml2(model.description)}</p>
|
|
667
|
+
<div class="absolute-voice-ops-action-center__actions">${actions}</div>
|
|
668
|
+
<p class="absolute-voice-ops-action-center__result">${escapeHtml2(model.lastResultLabel)}</p>
|
|
669
|
+
${model.error ? `<p class="absolute-voice-ops-action-center__error">${escapeHtml2(model.error)}</p>` : ""}
|
|
670
|
+
</section>`;
|
|
671
|
+
};
|
|
672
|
+
var getVoiceOpsActionCenterCSS = () => `.absolute-voice-ops-action-center{border:1px solid #d5cbb8;border-radius:20px;background:#fffaf1;color:#17130b;padding:18px;box-shadow:0 18px 40px rgba(58,42,16,.12);font-family:inherit}.absolute-voice-ops-action-center--error{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-ops-action-center__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-ops-action-center__eyebrow{color:#725d37;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-ops-action-center__label{font-size:28px;line-height:1}.absolute-voice-ops-action-center__description,.absolute-voice-ops-action-center__result{color:#5b4b2f;margin:12px 0 0}.absolute-voice-ops-action-center__actions{display:flex;flex-wrap:wrap;gap:8px;margin-top:14px}.absolute-voice-ops-action-center__actions button{background:#7c4a03;border:0;border-radius:999px;color:#fff8e8;cursor:pointer;font:inherit;font-weight:800;padding:8px 12px}.absolute-voice-ops-action-center__actions button:disabled{cursor:not-allowed;opacity:.5}.absolute-voice-ops-action-center__error{color:#9f1239;font-weight:700}`;
|
|
673
|
+
var mountVoiceOpsActionCenter = (element, options = {}) => {
|
|
674
|
+
const store = createVoiceOpsActionCenterStore(options);
|
|
675
|
+
const render = () => {
|
|
676
|
+
element.innerHTML = renderVoiceOpsActionCenterHTML(store.getSnapshot(), options);
|
|
677
|
+
};
|
|
678
|
+
const unsubscribe = store.subscribe(render);
|
|
679
|
+
const handleClick = (event) => {
|
|
680
|
+
const target = event.target;
|
|
681
|
+
if (!(target instanceof Element)) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
const action = target.closest("[data-absolute-voice-ops-action]");
|
|
685
|
+
const actionId = action?.getAttribute("data-absolute-voice-ops-action");
|
|
686
|
+
if (actionId) {
|
|
687
|
+
store.run(actionId).catch(() => {});
|
|
688
|
+
}
|
|
689
|
+
};
|
|
690
|
+
element.addEventListener?.("click", handleClick);
|
|
691
|
+
render();
|
|
692
|
+
return {
|
|
693
|
+
close: () => {
|
|
694
|
+
element.removeEventListener?.("click", handleClick);
|
|
695
|
+
unsubscribe();
|
|
696
|
+
store.close();
|
|
697
|
+
},
|
|
698
|
+
run: store.run
|
|
699
|
+
};
|
|
700
|
+
};
|
|
701
|
+
var defineVoiceOpsActionCenterElement = (tagName = "absolute-voice-ops-action-center", options = {}) => {
|
|
702
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
703
|
+
return;
|
|
704
|
+
}
|
|
705
|
+
customElements.define(tagName, class AbsoluteVoiceOpsActionCenterElement extends HTMLElement {
|
|
706
|
+
mounted;
|
|
707
|
+
connectedCallback() {
|
|
708
|
+
this.mounted = mountVoiceOpsActionCenter(this, {
|
|
709
|
+
...options,
|
|
710
|
+
description: this.getAttribute("description") ?? options.description,
|
|
711
|
+
title: this.getAttribute("title") ?? options.title
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
disconnectedCallback() {
|
|
715
|
+
this.mounted?.close();
|
|
716
|
+
this.mounted = undefined;
|
|
717
|
+
}
|
|
718
|
+
});
|
|
719
|
+
};
|
|
720
|
+
|
|
721
|
+
// src/svelte/createVoiceOpsActionCenter.ts
|
|
722
|
+
var createVoiceOpsActionCenter = (options = {}) => {
|
|
723
|
+
const store = createVoiceOpsActionCenterStore(options);
|
|
724
|
+
return {
|
|
725
|
+
close: store.close,
|
|
726
|
+
getHTML: () => renderVoiceOpsActionCenterHTML(store.getSnapshot(), options),
|
|
727
|
+
getSnapshot: store.getSnapshot,
|
|
728
|
+
getViewModel: () => createVoiceOpsActionCenterViewModel(store.getSnapshot(), options),
|
|
729
|
+
run: store.run,
|
|
730
|
+
setActions: store.setActions,
|
|
731
|
+
subscribe: store.subscribe
|
|
732
|
+
};
|
|
733
|
+
};
|
|
471
734
|
// src/client/opsStatus.ts
|
|
472
735
|
var fetchVoiceOpsStatus = async (path = "/api/voice/ops-status", options = {}) => {
|
|
473
736
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -548,8 +811,8 @@ var createVoiceOpsStatusStore = (path = "/api/voice/ops-status", options = {}) =
|
|
|
548
811
|
};
|
|
549
812
|
|
|
550
813
|
// src/client/opsStatusWidget.ts
|
|
551
|
-
var
|
|
552
|
-
var
|
|
814
|
+
var DEFAULT_TITLE3 = "Voice Ops Status";
|
|
815
|
+
var DEFAULT_DESCRIPTION3 = "Certified workflow, provider, and handoff readiness from your AbsoluteJS voice app.";
|
|
553
816
|
var SURFACE_LABELS = {
|
|
554
817
|
handoffs: "Handoffs",
|
|
555
818
|
providers: "Providers",
|
|
@@ -557,7 +820,7 @@ var SURFACE_LABELS = {
|
|
|
557
820
|
sessions: "Sessions",
|
|
558
821
|
workflows: "Workflows"
|
|
559
822
|
};
|
|
560
|
-
var
|
|
823
|
+
var escapeHtml3 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
561
824
|
var readNumber = (value, key) => value && typeof value === "object" && (key in value) ? Number(value[key] ?? 0) : 0;
|
|
562
825
|
var surfaceDetail = (surface) => {
|
|
563
826
|
const total = readNumber(surface, "total");
|
|
@@ -597,7 +860,7 @@ var createVoiceOpsStatusViewModel = (snapshot, options = {}) => {
|
|
|
597
860
|
};
|
|
598
861
|
});
|
|
599
862
|
return {
|
|
600
|
-
description: options.description ??
|
|
863
|
+
description: options.description ?? DEFAULT_DESCRIPTION3,
|
|
601
864
|
error: snapshot.error,
|
|
602
865
|
isLoading: snapshot.isLoading,
|
|
603
866
|
label: getVoiceOpsStatusLabel(report, snapshot.error),
|
|
@@ -605,31 +868,31 @@ var createVoiceOpsStatusViewModel = (snapshot, options = {}) => {
|
|
|
605
868
|
passed: report?.passed ?? 0,
|
|
606
869
|
status: snapshot.error ? "error" : report ? report.status : snapshot.isLoading ? "loading" : "loading",
|
|
607
870
|
surfaces,
|
|
608
|
-
title: options.title ??
|
|
871
|
+
title: options.title ?? DEFAULT_TITLE3,
|
|
609
872
|
total: report?.total ?? 0,
|
|
610
873
|
updatedAt: snapshot.updatedAt
|
|
611
874
|
};
|
|
612
875
|
};
|
|
613
876
|
var renderVoiceOpsStatusHTML = (snapshot, options = {}) => {
|
|
614
877
|
const model = createVoiceOpsStatusViewModel(snapshot, options);
|
|
615
|
-
const surfaces = model.surfaces.length ? model.surfaces.map((surface) => `<li class="absolute-voice-ops-status__surface absolute-voice-ops-status__surface--${
|
|
616
|
-
<span>${
|
|
617
|
-
<strong>${
|
|
878
|
+
const surfaces = model.surfaces.length ? model.surfaces.map((surface) => `<li class="absolute-voice-ops-status__surface absolute-voice-ops-status__surface--${escapeHtml3(surface.status)}">
|
|
879
|
+
<span>${escapeHtml3(surface.label)}</span>
|
|
880
|
+
<strong>${escapeHtml3(surface.detail)}</strong>
|
|
618
881
|
</li>`).join("") : '<li class="absolute-voice-ops-status__surface"><span>Status</span><strong>Waiting for first check</strong></li>';
|
|
619
|
-
const links = model.links.length ? `<nav class="absolute-voice-ops-status__links">${model.links.slice(0, 4).map((link) => `<a href="${
|
|
620
|
-
return `<section class="absolute-voice-ops-status absolute-voice-ops-status--${
|
|
882
|
+
const links = model.links.length ? `<nav class="absolute-voice-ops-status__links">${model.links.slice(0, 4).map((link) => `<a href="${escapeHtml3(link.href)}">${escapeHtml3(link.label)}</a>`).join("")}</nav>` : "";
|
|
883
|
+
return `<section class="absolute-voice-ops-status absolute-voice-ops-status--${escapeHtml3(model.status)}">
|
|
621
884
|
<header class="absolute-voice-ops-status__header">
|
|
622
|
-
<span class="absolute-voice-ops-status__eyebrow">${
|
|
623
|
-
<strong class="absolute-voice-ops-status__label">${
|
|
885
|
+
<span class="absolute-voice-ops-status__eyebrow">${escapeHtml3(model.title)}</span>
|
|
886
|
+
<strong class="absolute-voice-ops-status__label">${escapeHtml3(model.label)}</strong>
|
|
624
887
|
</header>
|
|
625
|
-
<p class="absolute-voice-ops-status__description">${
|
|
888
|
+
<p class="absolute-voice-ops-status__description">${escapeHtml3(model.description)}</p>
|
|
626
889
|
<div class="absolute-voice-ops-status__summary">
|
|
627
890
|
<span>${model.passed} passing</span>
|
|
628
891
|
<span>${Math.max(model.total - model.passed, 0)} failing</span>
|
|
629
892
|
<span>${model.total} checks</span>
|
|
630
893
|
</div>
|
|
631
894
|
<ul class="absolute-voice-ops-status__surfaces">${surfaces}</ul>
|
|
632
|
-
${model.error ? `<p class="absolute-voice-ops-status__error">${
|
|
895
|
+
${model.error ? `<p class="absolute-voice-ops-status__error">${escapeHtml3(model.error)}</p>` : ""}
|
|
633
896
|
${links}
|
|
634
897
|
</section>`;
|
|
635
898
|
};
|
|
@@ -764,7 +1027,7 @@ var createVoiceProviderSimulationControlsStore = (options) => {
|
|
|
764
1027
|
};
|
|
765
1028
|
|
|
766
1029
|
// src/client/providerSimulationControlsWidget.ts
|
|
767
|
-
var
|
|
1030
|
+
var escapeHtml4 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
768
1031
|
var formatKind = (kind) => (kind ?? "stt").toUpperCase();
|
|
769
1032
|
var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
|
|
770
1033
|
const configuredProviders = options.providers.filter((provider) => provider.configured !== false);
|
|
@@ -784,18 +1047,18 @@ var createVoiceProviderSimulationControlsViewModel = (snapshot, options) => {
|
|
|
784
1047
|
};
|
|
785
1048
|
var renderVoiceProviderSimulationControlsHTML = (snapshot, options) => {
|
|
786
1049
|
const model = createVoiceProviderSimulationControlsViewModel(snapshot, options);
|
|
787
|
-
const failureButtons = model.failureProviders.map((provider) => `<button type="button" data-voice-provider-fail="${
|
|
788
|
-
const recoveryButtons = model.providers.map((provider) => `<button type="button" data-voice-provider-recover="${
|
|
1050
|
+
const failureButtons = model.failureProviders.map((provider) => `<button type="button" data-voice-provider-fail="${escapeHtml4(provider.provider)}"${!model.canSimulateFailure || snapshot.isRunning ? " disabled" : ""}>Simulate ${escapeHtml4(provider.provider)} ${escapeHtml4(formatKind(options.kind))} failure</button>`).join("");
|
|
1051
|
+
const recoveryButtons = model.providers.map((provider) => `<button type="button" data-voice-provider-recover="${escapeHtml4(provider.provider)}"${snapshot.isRunning ? " disabled" : ""}>Mark ${escapeHtml4(provider.provider)} recovered</button>`).join("");
|
|
789
1052
|
return `<section class="absolute-voice-provider-simulation absolute-voice-provider-simulation--${snapshot.error ? "error" : snapshot.isRunning ? "running" : "ready"}">
|
|
790
1053
|
<header class="absolute-voice-provider-simulation__header">
|
|
791
|
-
<span class="absolute-voice-provider-simulation__eyebrow">${
|
|
792
|
-
<strong class="absolute-voice-provider-simulation__label">${
|
|
1054
|
+
<span class="absolute-voice-provider-simulation__eyebrow">${escapeHtml4(model.title)}</span>
|
|
1055
|
+
<strong class="absolute-voice-provider-simulation__label">${escapeHtml4(model.label)}</strong>
|
|
793
1056
|
</header>
|
|
794
|
-
<p class="absolute-voice-provider-simulation__description">${
|
|
795
|
-
${model.canSimulateFailure ? "" : `<p class="absolute-voice-provider-simulation__empty">${
|
|
1057
|
+
<p class="absolute-voice-provider-simulation__description">${escapeHtml4(model.description)}</p>
|
|
1058
|
+
${model.canSimulateFailure ? "" : `<p class="absolute-voice-provider-simulation__empty">${escapeHtml4(options.fallbackRequiredMessage ?? "Configure fallback providers before simulating failure.")}</p>`}
|
|
796
1059
|
<div class="absolute-voice-provider-simulation__actions">${failureButtons}${recoveryButtons}</div>
|
|
797
|
-
${snapshot.error ? `<p class="absolute-voice-provider-simulation__error">${
|
|
798
|
-
${model.resultText ? `<pre class="absolute-voice-provider-simulation__result">${
|
|
1060
|
+
${snapshot.error ? `<p class="absolute-voice-provider-simulation__error">${escapeHtml4(snapshot.error)}</p>` : ""}
|
|
1061
|
+
${model.resultText ? `<pre class="absolute-voice-provider-simulation__result">${escapeHtml4(model.resultText)}</pre>` : ""}
|
|
799
1062
|
</section>`;
|
|
800
1063
|
};
|
|
801
1064
|
var bindVoiceProviderSimulationControls = (element, store) => {
|
|
@@ -950,9 +1213,9 @@ var createVoiceProviderCapabilitiesStore = (path = "/api/provider-capabilities",
|
|
|
950
1213
|
};
|
|
951
1214
|
|
|
952
1215
|
// src/client/providerCapabilitiesWidget.ts
|
|
953
|
-
var
|
|
954
|
-
var
|
|
955
|
-
var
|
|
1216
|
+
var DEFAULT_TITLE4 = "Provider Capabilities";
|
|
1217
|
+
var DEFAULT_DESCRIPTION4 = "Configured, selected, and healthy voice providers for this deployment.";
|
|
1218
|
+
var escapeHtml5 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
956
1219
|
var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
|
|
957
1220
|
var formatKind2 = (kind) => kind.toUpperCase();
|
|
958
1221
|
var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
@@ -996,36 +1259,36 @@ var createVoiceProviderCapabilitiesViewModel = (snapshot, options = {}) => {
|
|
|
996
1259
|
const selectedCount = snapshot.report?.selected ?? capabilities.filter((capability) => capability.selected).length;
|
|
997
1260
|
return {
|
|
998
1261
|
capabilities,
|
|
999
|
-
description: options.description ??
|
|
1262
|
+
description: options.description ?? DEFAULT_DESCRIPTION4,
|
|
1000
1263
|
error: snapshot.error,
|
|
1001
1264
|
isLoading: snapshot.isLoading,
|
|
1002
1265
|
label: snapshot.error ? "Unavailable" : capabilities.length ? warningCount > 0 ? `${warningCount} needs attention` : `${selectedCount} selected` : snapshot.isLoading ? "Checking" : "No capabilities",
|
|
1003
1266
|
status: snapshot.error ? "error" : capabilities.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
1004
|
-
title: options.title ??
|
|
1267
|
+
title: options.title ?? DEFAULT_TITLE4,
|
|
1005
1268
|
updatedAt: snapshot.updatedAt
|
|
1006
1269
|
};
|
|
1007
1270
|
};
|
|
1008
1271
|
var renderVoiceProviderCapabilitiesHTML = (snapshot, options = {}) => {
|
|
1009
1272
|
const model = createVoiceProviderCapabilitiesViewModel(snapshot, options);
|
|
1010
|
-
const capabilities = model.capabilities.length ? `<div class="absolute-voice-provider-capabilities__providers">${model.capabilities.map((capability) => `<article class="absolute-voice-provider-capabilities__provider absolute-voice-provider-capabilities__provider--${
|
|
1273
|
+
const capabilities = model.capabilities.length ? `<div class="absolute-voice-provider-capabilities__providers">${model.capabilities.map((capability) => `<article class="absolute-voice-provider-capabilities__provider absolute-voice-provider-capabilities__provider--${escapeHtml5(capability.status)}">
|
|
1011
1274
|
<header>
|
|
1012
|
-
<strong>${
|
|
1013
|
-
<span>${
|
|
1275
|
+
<strong>${escapeHtml5(capability.label)}</strong>
|
|
1276
|
+
<span>${escapeHtml5(formatStatus(capability.status))}</span>
|
|
1014
1277
|
</header>
|
|
1015
|
-
<p>${
|
|
1278
|
+
<p>${escapeHtml5(capability.detail)}</p>
|
|
1016
1279
|
<dl>${capability.rows.map((row) => `<div>
|
|
1017
|
-
<dt>${
|
|
1018
|
-
<dd>${
|
|
1280
|
+
<dt>${escapeHtml5(row.label)}</dt>
|
|
1281
|
+
<dd>${escapeHtml5(row.value)}</dd>
|
|
1019
1282
|
</div>`).join("")}</dl>
|
|
1020
1283
|
</article>`).join("")}</div>` : '<p class="absolute-voice-provider-capabilities__empty">Configure provider capabilities to see deployment coverage.</p>';
|
|
1021
|
-
return `<section class="absolute-voice-provider-capabilities absolute-voice-provider-capabilities--${
|
|
1284
|
+
return `<section class="absolute-voice-provider-capabilities absolute-voice-provider-capabilities--${escapeHtml5(model.status)}">
|
|
1022
1285
|
<header class="absolute-voice-provider-capabilities__header">
|
|
1023
|
-
<span class="absolute-voice-provider-capabilities__eyebrow">${
|
|
1024
|
-
<strong class="absolute-voice-provider-capabilities__label">${
|
|
1286
|
+
<span class="absolute-voice-provider-capabilities__eyebrow">${escapeHtml5(model.title)}</span>
|
|
1287
|
+
<strong class="absolute-voice-provider-capabilities__label">${escapeHtml5(model.label)}</strong>
|
|
1025
1288
|
</header>
|
|
1026
|
-
<p class="absolute-voice-provider-capabilities__description">${
|
|
1289
|
+
<p class="absolute-voice-provider-capabilities__description">${escapeHtml5(model.description)}</p>
|
|
1027
1290
|
${capabilities}
|
|
1028
|
-
${model.error ? `<p class="absolute-voice-provider-capabilities__error">${
|
|
1291
|
+
${model.error ? `<p class="absolute-voice-provider-capabilities__error">${escapeHtml5(model.error)}</p>` : ""}
|
|
1029
1292
|
</section>`;
|
|
1030
1293
|
};
|
|
1031
1294
|
var getVoiceProviderCapabilitiesCSS = () => `.absolute-voice-provider-capabilities{border:1px solid #bfd7ea;border-radius:20px;background:#f6fbff;color:#08131f;padding:18px;box-shadow:0 18px 40px rgba(14,51,78,.12);font-family:inherit}.absolute-voice-provider-capabilities--error,.absolute-voice-provider-capabilities--warning{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-provider-capabilities__header,.absolute-voice-provider-capabilities__provider header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-provider-capabilities__eyebrow{color:#255f85;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-provider-capabilities__label{font-size:24px;line-height:1}.absolute-voice-provider-capabilities__description,.absolute-voice-provider-capabilities__provider p,.absolute-voice-provider-capabilities__provider dt,.absolute-voice-provider-capabilities__empty{color:#405467}.absolute-voice-provider-capabilities__providers{display:grid;gap:12px;margin-top:14px}.absolute-voice-provider-capabilities__provider{background:#fff;border:1px solid #d7e7f3;border-radius:16px;padding:14px}.absolute-voice-provider-capabilities__provider--selected,.absolute-voice-provider-capabilities__provider--healthy{border-color:#86efac}.absolute-voice-provider-capabilities__provider--degraded,.absolute-voice-provider-capabilities__provider--rate-limited,.absolute-voice-provider-capabilities__provider--suppressed,.absolute-voice-provider-capabilities__provider--unconfigured{border-color:#f2a7a7}.absolute-voice-provider-capabilities__provider p{margin:10px 0}.absolute-voice-provider-capabilities__provider dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-provider-capabilities__provider div{background:#f6fbff;border:1px solid #d7e7f3;border-radius:12px;padding:8px}.absolute-voice-provider-capabilities__provider dt{font-size:12px}.absolute-voice-provider-capabilities__provider dd{font-weight:800;margin:4px 0 0}.absolute-voice-provider-capabilities__empty{margin:14px 0 0}.absolute-voice-provider-capabilities__error{color:#9f1239;font-weight:700}`;
|
|
@@ -1798,9 +2061,9 @@ var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {
|
|
|
1798
2061
|
};
|
|
1799
2062
|
|
|
1800
2063
|
// src/client/providerStatusWidget.ts
|
|
1801
|
-
var
|
|
1802
|
-
var
|
|
1803
|
-
var
|
|
2064
|
+
var DEFAULT_TITLE5 = "Voice Providers";
|
|
2065
|
+
var DEFAULT_DESCRIPTION5 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
|
|
2066
|
+
var escapeHtml6 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
1804
2067
|
var formatProvider2 = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
|
|
1805
2068
|
var formatStatus2 = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
1806
2069
|
var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
|
|
@@ -1844,37 +2107,37 @@ var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
|
|
|
1844
2107
|
const warningCount = providers.filter((provider) => isWarningStatus2(provider.status)).length;
|
|
1845
2108
|
const healthyCount = providers.filter((provider) => provider.status === "healthy").length;
|
|
1846
2109
|
return {
|
|
1847
|
-
description: options.description ??
|
|
2110
|
+
description: options.description ?? DEFAULT_DESCRIPTION5,
|
|
1848
2111
|
error: snapshot.error,
|
|
1849
2112
|
isLoading: snapshot.isLoading,
|
|
1850
2113
|
label: snapshot.error ? "Unavailable" : providers.length ? warningCount > 0 ? `${warningCount} needs attention` : `${healthyCount} healthy` : snapshot.isLoading ? "Checking" : "No provider traffic",
|
|
1851
2114
|
providers,
|
|
1852
2115
|
status: snapshot.error ? "error" : providers.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
1853
|
-
title: options.title ??
|
|
2116
|
+
title: options.title ?? DEFAULT_TITLE5,
|
|
1854
2117
|
updatedAt: snapshot.updatedAt
|
|
1855
2118
|
};
|
|
1856
2119
|
};
|
|
1857
2120
|
var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
|
|
1858
2121
|
const model = createVoiceProviderStatusViewModel(snapshot, options);
|
|
1859
|
-
const providers = model.providers.length ? `<div class="absolute-voice-provider-status__providers">${model.providers.map((provider) => `<article class="absolute-voice-provider-status__provider absolute-voice-provider-status__provider--${
|
|
2122
|
+
const providers = model.providers.length ? `<div class="absolute-voice-provider-status__providers">${model.providers.map((provider) => `<article class="absolute-voice-provider-status__provider absolute-voice-provider-status__provider--${escapeHtml6(provider.status)}">
|
|
1860
2123
|
<header>
|
|
1861
|
-
<strong>${
|
|
1862
|
-
<span>${
|
|
2124
|
+
<strong>${escapeHtml6(provider.label)}</strong>
|
|
2125
|
+
<span>${escapeHtml6(formatStatus2(provider.status))}</span>
|
|
1863
2126
|
</header>
|
|
1864
|
-
<p>${
|
|
2127
|
+
<p>${escapeHtml6(provider.detail)}</p>
|
|
1865
2128
|
<dl>${provider.rows.map((row) => `<div>
|
|
1866
|
-
<dt>${
|
|
1867
|
-
<dd>${
|
|
2129
|
+
<dt>${escapeHtml6(row.label)}</dt>
|
|
2130
|
+
<dd>${escapeHtml6(row.value)}</dd>
|
|
1868
2131
|
</div>`).join("")}</dl>
|
|
1869
2132
|
</article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
|
|
1870
|
-
return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${
|
|
2133
|
+
return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml6(model.status)}">
|
|
1871
2134
|
<header class="absolute-voice-provider-status__header">
|
|
1872
|
-
<span class="absolute-voice-provider-status__eyebrow">${
|
|
1873
|
-
<strong class="absolute-voice-provider-status__label">${
|
|
2135
|
+
<span class="absolute-voice-provider-status__eyebrow">${escapeHtml6(model.title)}</span>
|
|
2136
|
+
<strong class="absolute-voice-provider-status__label">${escapeHtml6(model.label)}</strong>
|
|
1874
2137
|
</header>
|
|
1875
|
-
<p class="absolute-voice-provider-status__description">${
|
|
2138
|
+
<p class="absolute-voice-provider-status__description">${escapeHtml6(model.description)}</p>
|
|
1876
2139
|
${providers}
|
|
1877
|
-
${model.error ? `<p class="absolute-voice-provider-status__error">${
|
|
2140
|
+
${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml6(model.error)}</p>` : ""}
|
|
1878
2141
|
</section>`;
|
|
1879
2142
|
};
|
|
1880
2143
|
var getVoiceProviderStatusCSS = () => `.absolute-voice-provider-status{border:1px solid #d8d2c4;border-radius:20px;background:#fffaf0;color:#16130d;padding:18px;box-shadow:0 18px 40px rgba(47,37,18,.12);font-family:inherit}.absolute-voice-provider-status--error,.absolute-voice-provider-status--warning{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-provider-status__header,.absolute-voice-provider-status__provider header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-provider-status__eyebrow{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-provider-status__label{font-size:24px;line-height:1}.absolute-voice-provider-status__description,.absolute-voice-provider-status__provider p,.absolute-voice-provider-status__provider dt,.absolute-voice-provider-status__empty{color:#514733}.absolute-voice-provider-status__providers{display:grid;gap:12px;margin-top:14px}.absolute-voice-provider-status__provider{background:#fff;border:1px solid #eee4d2;border-radius:16px;padding:14px}.absolute-voice-provider-status__provider--degraded,.absolute-voice-provider-status__provider--rate-limited,.absolute-voice-provider-status__provider--suppressed{border-color:#f2a7a7}.absolute-voice-provider-status__provider--recoverable{border-color:#fbbf24}.absolute-voice-provider-status__provider p{margin:10px 0}.absolute-voice-provider-status__provider dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-provider-status__provider div{background:#fffaf0;border:1px solid #eee4d2;border-radius:12px;padding:8px}.absolute-voice-provider-status__provider dt{font-size:12px}.absolute-voice-provider-status__provider dd{font-weight:800;margin:4px 0 0}.absolute-voice-provider-status__empty{margin:14px 0 0}.absolute-voice-provider-status__error{color:#9f1239;font-weight:700}`;
|
|
@@ -2005,9 +2268,9 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
|
|
|
2005
2268
|
};
|
|
2006
2269
|
|
|
2007
2270
|
// src/client/routingStatusWidget.ts
|
|
2008
|
-
var
|
|
2009
|
-
var
|
|
2010
|
-
var
|
|
2271
|
+
var DEFAULT_TITLE6 = "Voice Routing";
|
|
2272
|
+
var DEFAULT_DESCRIPTION6 = "Latest provider routing decision from the self-hosted trace store.";
|
|
2273
|
+
var escapeHtml7 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2011
2274
|
var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
|
|
2012
2275
|
var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
2013
2276
|
const decision = snapshot.decision;
|
|
@@ -2031,30 +2294,30 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
|
2031
2294
|
] : [];
|
|
2032
2295
|
return {
|
|
2033
2296
|
decision,
|
|
2034
|
-
description: options.description ??
|
|
2297
|
+
description: options.description ?? DEFAULT_DESCRIPTION6,
|
|
2035
2298
|
error: snapshot.error,
|
|
2036
2299
|
isLoading: snapshot.isLoading,
|
|
2037
2300
|
label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
|
|
2038
2301
|
rows,
|
|
2039
2302
|
status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
2040
|
-
title: options.title ??
|
|
2303
|
+
title: options.title ?? DEFAULT_TITLE6,
|
|
2041
2304
|
updatedAt: snapshot.updatedAt
|
|
2042
2305
|
};
|
|
2043
2306
|
};
|
|
2044
2307
|
var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
2045
2308
|
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
2046
2309
|
const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
|
|
2047
|
-
<span>${
|
|
2048
|
-
<strong>${
|
|
2310
|
+
<span>${escapeHtml7(row.label)}</span>
|
|
2311
|
+
<strong>${escapeHtml7(row.value)}</strong>
|
|
2049
2312
|
</div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
|
|
2050
|
-
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${
|
|
2313
|
+
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml7(model.status)}">
|
|
2051
2314
|
<header class="absolute-voice-routing-status__header">
|
|
2052
|
-
<span class="absolute-voice-routing-status__eyebrow">${
|
|
2053
|
-
<strong class="absolute-voice-routing-status__label">${
|
|
2315
|
+
<span class="absolute-voice-routing-status__eyebrow">${escapeHtml7(model.title)}</span>
|
|
2316
|
+
<strong class="absolute-voice-routing-status__label">${escapeHtml7(model.label)}</strong>
|
|
2054
2317
|
</header>
|
|
2055
|
-
<p class="absolute-voice-routing-status__description">${
|
|
2318
|
+
<p class="absolute-voice-routing-status__description">${escapeHtml7(model.description)}</p>
|
|
2056
2319
|
${rows}
|
|
2057
|
-
${model.error ? `<p class="absolute-voice-routing-status__error">${
|
|
2320
|
+
${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml7(model.error)}</p>` : ""}
|
|
2058
2321
|
</section>`;
|
|
2059
2322
|
};
|
|
2060
2323
|
var getVoiceRoutingStatusCSS = () => `.absolute-voice-routing-status{border:1px solid #d8d2c4;border-radius:20px;background:#fffaf0;color:#16130d;padding:18px;box-shadow:0 18px 40px rgba(47,37,18,.12);font-family:inherit}.absolute-voice-routing-status--error{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-routing-status__header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-routing-status__eyebrow{color:#73664f;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-routing-status__label{font-size:24px;line-height:1}.absolute-voice-routing-status__description{color:#514733;margin:12px 0 0}.absolute-voice-routing-status__grid{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin-top:14px}.absolute-voice-routing-status__grid div{background:#fff;border:1px solid #eee4d2;border-radius:14px;padding:10px 12px}.absolute-voice-routing-status__grid span{color:#655944;display:block;font-size:12px;margin-bottom:4px}.absolute-voice-routing-status__grid strong{overflow-wrap:anywhere}.absolute-voice-routing-status__empty{color:#655944;margin:14px 0 0}.absolute-voice-routing-status__error{color:#9f1239;font-weight:700}`;
|
|
@@ -2185,9 +2448,9 @@ var createVoiceTraceTimelineStore = (path = "/api/voice-traces", options = {}) =
|
|
|
2185
2448
|
};
|
|
2186
2449
|
|
|
2187
2450
|
// src/client/traceTimelineWidget.ts
|
|
2188
|
-
var
|
|
2189
|
-
var
|
|
2190
|
-
var
|
|
2451
|
+
var DEFAULT_TITLE7 = "Voice Traces";
|
|
2452
|
+
var DEFAULT_DESCRIPTION7 = "Latest call timelines with provider latency, fallbacks, handoffs, and errors from your self-hosted trace store.";
|
|
2453
|
+
var escapeHtml8 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2191
2454
|
var formatMs = (value) => typeof value === "number" ? `${value}ms` : "n/a";
|
|
2192
2455
|
var formatProviders = (session) => session.providers.length ? session.providers.map((provider) => provider.provider).join(", ") : "No providers";
|
|
2193
2456
|
var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
|
|
@@ -2201,34 +2464,34 @@ var createVoiceTraceTimelineViewModel = (snapshot, options = {}) => {
|
|
|
2201
2464
|
const failed = sessions.filter((session) => session.status === "failed").length;
|
|
2202
2465
|
const warnings = sessions.filter((session) => session.status === "warning").length;
|
|
2203
2466
|
return {
|
|
2204
|
-
description: options.description ??
|
|
2467
|
+
description: options.description ?? DEFAULT_DESCRIPTION7,
|
|
2205
2468
|
error: snapshot.error,
|
|
2206
2469
|
isLoading: snapshot.isLoading,
|
|
2207
2470
|
label: snapshot.error ? "Unavailable" : failed > 0 ? `${failed} failed` : warnings > 0 ? `${warnings} warning` : sessions.length ? `${sessions.length} recent` : snapshot.isLoading ? "Checking" : "No traces yet",
|
|
2208
2471
|
sessions,
|
|
2209
2472
|
status: snapshot.error ? "error" : failed > 0 ? "failed" : warnings > 0 ? "warning" : sessions.length ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
2210
|
-
title: options.title ??
|
|
2473
|
+
title: options.title ?? DEFAULT_TITLE7,
|
|
2211
2474
|
updatedAt: snapshot.updatedAt
|
|
2212
2475
|
};
|
|
2213
2476
|
};
|
|
2214
2477
|
var renderVoiceTraceTimelineWidgetHTML = (snapshot, options = {}) => {
|
|
2215
2478
|
const model = createVoiceTraceTimelineViewModel(snapshot, options);
|
|
2216
|
-
const sessions = model.sessions.length ? `<div class="absolute-voice-trace-timeline__sessions">${model.sessions.map((session) => `<article class="absolute-voice-trace-timeline__session absolute-voice-trace-timeline__session--${
|
|
2479
|
+
const sessions = model.sessions.length ? `<div class="absolute-voice-trace-timeline__sessions">${model.sessions.map((session) => `<article class="absolute-voice-trace-timeline__session absolute-voice-trace-timeline__session--${escapeHtml8(session.status)}">
|
|
2217
2480
|
<header>
|
|
2218
|
-
<strong>${
|
|
2219
|
-
<span>${
|
|
2481
|
+
<strong>${escapeHtml8(session.sessionId)}</strong>
|
|
2482
|
+
<span>${escapeHtml8(session.status)}</span>
|
|
2220
2483
|
</header>
|
|
2221
|
-
<p>${
|
|
2222
|
-
<a href="${
|
|
2484
|
+
<p>${escapeHtml8(session.label)} \xB7 ${escapeHtml8(session.durationLabel)} \xB7 ${escapeHtml8(session.providerLabel)}</p>
|
|
2485
|
+
<a href="${escapeHtml8(session.detailHref)}">Open timeline</a>
|
|
2223
2486
|
</article>`).join("")}</div>` : '<p class="absolute-voice-trace-timeline__empty">Run a voice session to see call timelines.</p>';
|
|
2224
|
-
return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${
|
|
2487
|
+
return `<section class="absolute-voice-trace-timeline absolute-voice-trace-timeline--${escapeHtml8(model.status)}">
|
|
2225
2488
|
<header class="absolute-voice-trace-timeline__header">
|
|
2226
|
-
<span class="absolute-voice-trace-timeline__eyebrow">${
|
|
2227
|
-
<strong class="absolute-voice-trace-timeline__label">${
|
|
2489
|
+
<span class="absolute-voice-trace-timeline__eyebrow">${escapeHtml8(model.title)}</span>
|
|
2490
|
+
<strong class="absolute-voice-trace-timeline__label">${escapeHtml8(model.label)}</strong>
|
|
2228
2491
|
</header>
|
|
2229
|
-
<p class="absolute-voice-trace-timeline__description">${
|
|
2492
|
+
<p class="absolute-voice-trace-timeline__description">${escapeHtml8(model.description)}</p>
|
|
2230
2493
|
${sessions}
|
|
2231
|
-
${model.error ? `<p class="absolute-voice-trace-timeline__error">${
|
|
2494
|
+
${model.error ? `<p class="absolute-voice-trace-timeline__error">${escapeHtml8(model.error)}</p>` : ""}
|
|
2232
2495
|
</section>`;
|
|
2233
2496
|
};
|
|
2234
2497
|
var getVoiceTraceTimelineCSS = () => `.absolute-voice-trace-timeline{border:1px solid #bad7d3;border-radius:20px;background:#f3fffb;color:#09201c;padding:18px;box-shadow:0 18px 40px rgba(9,32,28,.12);font-family:inherit}.absolute-voice-trace-timeline--error,.absolute-voice-trace-timeline--failed{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-trace-timeline--warning{border-color:#fbbf24;background:#fffaf0}.absolute-voice-trace-timeline__header,.absolute-voice-trace-timeline__session header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-trace-timeline__eyebrow{color:#17665b;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-trace-timeline__label{font-size:24px;line-height:1}.absolute-voice-trace-timeline__description,.absolute-voice-trace-timeline__session p,.absolute-voice-trace-timeline__empty{color:#35544f}.absolute-voice-trace-timeline__sessions{display:grid;gap:12px;margin-top:14px}.absolute-voice-trace-timeline__session{background:#fff;border:1px solid #cfe7e2;border-radius:16px;padding:14px}.absolute-voice-trace-timeline__session--failed{border-color:#f2a7a7}.absolute-voice-trace-timeline__session--warning{border-color:#fbbf24}.absolute-voice-trace-timeline__session p{margin:10px 0}.absolute-voice-trace-timeline__session a{color:#0f766e;font-weight:800}.absolute-voice-trace-timeline__empty{margin:14px 0 0}.absolute-voice-trace-timeline__error{color:#9f1239;font-weight:700}`;
|
|
@@ -2385,10 +2648,10 @@ var createVoiceTurnLatencyStore = (path = "/api/turn-latency", options = {}) =>
|
|
|
2385
2648
|
};
|
|
2386
2649
|
|
|
2387
2650
|
// src/client/turnLatencyWidget.ts
|
|
2388
|
-
var
|
|
2389
|
-
var
|
|
2651
|
+
var DEFAULT_TITLE8 = "Turn Latency";
|
|
2652
|
+
var DEFAULT_DESCRIPTION8 = "Per-turn timing from first transcript to commit and assistant response start.";
|
|
2390
2653
|
var DEFAULT_PROOF_LABEL = "Run latency proof";
|
|
2391
|
-
var
|
|
2654
|
+
var escapeHtml9 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2392
2655
|
var formatMs2 = (value) => typeof value === "number" ? `${Math.round(value)}ms` : "n/a";
|
|
2393
2656
|
var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
|
|
2394
2657
|
const turns = (snapshot.report?.turns ?? []).map((turn) => ({
|
|
@@ -2402,39 +2665,39 @@ var createVoiceTurnLatencyViewModel = (snapshot, options = {}) => {
|
|
|
2402
2665
|
const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
|
|
2403
2666
|
const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
|
|
2404
2667
|
return {
|
|
2405
|
-
description: options.description ??
|
|
2668
|
+
description: options.description ?? DEFAULT_DESCRIPTION8,
|
|
2406
2669
|
error: snapshot.error,
|
|
2407
2670
|
isLoading: snapshot.isLoading,
|
|
2408
2671
|
label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} slow` : warningCount > 0 ? `${warningCount} warnings` : `avg ${formatMs2(snapshot.report?.averageTotalMs)}` : snapshot.isLoading ? "Checking" : "No turns",
|
|
2409
2672
|
proofLabel: options.proofPath ? options.proofLabel ?? DEFAULT_PROOF_LABEL : undefined,
|
|
2410
2673
|
showProofAction: Boolean(options.proofPath),
|
|
2411
2674
|
status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
2412
|
-
title: options.title ??
|
|
2675
|
+
title: options.title ?? DEFAULT_TITLE8,
|
|
2413
2676
|
turns,
|
|
2414
2677
|
updatedAt: snapshot.updatedAt
|
|
2415
2678
|
};
|
|
2416
2679
|
};
|
|
2417
2680
|
var renderVoiceTurnLatencyHTML = (snapshot, options = {}) => {
|
|
2418
2681
|
const model = createVoiceTurnLatencyViewModel(snapshot, options);
|
|
2419
|
-
const turns = model.turns.length ? `<div class="absolute-voice-turn-latency__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-latency__turn absolute-voice-turn-latency__turn--${
|
|
2682
|
+
const turns = model.turns.length ? `<div class="absolute-voice-turn-latency__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-latency__turn absolute-voice-turn-latency__turn--${escapeHtml9(turn.status)}">
|
|
2420
2683
|
<header>
|
|
2421
|
-
<strong>${
|
|
2422
|
-
<span>${
|
|
2684
|
+
<strong>${escapeHtml9(turn.label)}</strong>
|
|
2685
|
+
<span>${escapeHtml9(turn.status)}</span>
|
|
2423
2686
|
</header>
|
|
2424
2687
|
<dl>${turn.rows.map((row) => `<div>
|
|
2425
|
-
<dt>${
|
|
2426
|
-
<dd>${
|
|
2688
|
+
<dt>${escapeHtml9(row.label)}</dt>
|
|
2689
|
+
<dd>${escapeHtml9(row.value)}</dd>
|
|
2427
2690
|
</div>`).join("")}</dl>
|
|
2428
2691
|
</article>`).join("")}</div>` : '<p class="absolute-voice-turn-latency__empty">Complete a voice turn to see latency diagnostics.</p>';
|
|
2429
|
-
return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${
|
|
2692
|
+
return `<section class="absolute-voice-turn-latency absolute-voice-turn-latency--${escapeHtml9(model.status)}">
|
|
2430
2693
|
<header class="absolute-voice-turn-latency__header">
|
|
2431
|
-
<span class="absolute-voice-turn-latency__eyebrow">${
|
|
2432
|
-
<strong class="absolute-voice-turn-latency__label">${
|
|
2694
|
+
<span class="absolute-voice-turn-latency__eyebrow">${escapeHtml9(model.title)}</span>
|
|
2695
|
+
<strong class="absolute-voice-turn-latency__label">${escapeHtml9(model.label)}</strong>
|
|
2433
2696
|
</header>
|
|
2434
|
-
<p class="absolute-voice-turn-latency__description">${
|
|
2435
|
-
${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${
|
|
2697
|
+
<p class="absolute-voice-turn-latency__description">${escapeHtml9(model.description)}</p>
|
|
2698
|
+
${model.showProofAction ? `<button class="absolute-voice-turn-latency__proof" data-absolute-voice-turn-latency-proof type="button">${escapeHtml9(model.proofLabel ?? DEFAULT_PROOF_LABEL)}</button>` : ""}
|
|
2436
2699
|
${turns}
|
|
2437
|
-
${model.error ? `<p class="absolute-voice-turn-latency__error">${
|
|
2700
|
+
${model.error ? `<p class="absolute-voice-turn-latency__error">${escapeHtml9(model.error)}</p>` : ""}
|
|
2438
2701
|
</section>`;
|
|
2439
2702
|
};
|
|
2440
2703
|
var mountVoiceTurnLatency = (element, path = "/api/turn-latency", options = {}) => {
|
|
@@ -2573,9 +2836,9 @@ var createVoiceTurnQualityStore = (path = "/api/turn-quality", options = {}) =>
|
|
|
2573
2836
|
};
|
|
2574
2837
|
|
|
2575
2838
|
// src/client/turnQualityWidget.ts
|
|
2576
|
-
var
|
|
2577
|
-
var
|
|
2578
|
-
var
|
|
2839
|
+
var DEFAULT_TITLE9 = "Turn Quality";
|
|
2840
|
+
var DEFAULT_DESCRIPTION9 = "Per-turn STT confidence, fallback selection, corrections, and transcript coverage.";
|
|
2841
|
+
var escapeHtml10 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
2579
2842
|
var formatConfidence = (value) => typeof value === "number" ? `${Math.round(value * 100)}%` : "n/a";
|
|
2580
2843
|
var formatMaybe = (value) => value === undefined || value === "" ? "n/a" : String(value);
|
|
2581
2844
|
var getTurnDetail = (turn) => {
|
|
@@ -2613,37 +2876,37 @@ var createVoiceTurnQualityViewModel = (snapshot, options = {}) => {
|
|
|
2613
2876
|
const warningCount = snapshot.report?.warnings ?? turns.filter((turn) => turn.status === "warn").length;
|
|
2614
2877
|
const failedCount = snapshot.report?.failed ?? turns.filter((turn) => turn.status === "fail").length;
|
|
2615
2878
|
return {
|
|
2616
|
-
description: options.description ??
|
|
2879
|
+
description: options.description ?? DEFAULT_DESCRIPTION9,
|
|
2617
2880
|
error: snapshot.error,
|
|
2618
2881
|
isLoading: snapshot.isLoading,
|
|
2619
2882
|
label: snapshot.error ? "Unavailable" : turns.length ? failedCount > 0 ? `${failedCount} failed` : warningCount > 0 ? `${warningCount} warnings` : `${turns.length} healthy` : snapshot.isLoading ? "Checking" : "No turns",
|
|
2620
2883
|
status: snapshot.error ? "error" : turns.length ? failedCount > 0 || warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
2621
|
-
title: options.title ??
|
|
2884
|
+
title: options.title ?? DEFAULT_TITLE9,
|
|
2622
2885
|
turns,
|
|
2623
2886
|
updatedAt: snapshot.updatedAt
|
|
2624
2887
|
};
|
|
2625
2888
|
};
|
|
2626
2889
|
var renderVoiceTurnQualityHTML = (snapshot, options = {}) => {
|
|
2627
2890
|
const model = createVoiceTurnQualityViewModel(snapshot, options);
|
|
2628
|
-
const turns = model.turns.length ? `<div class="absolute-voice-turn-quality__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-quality__turn absolute-voice-turn-quality__turn--${
|
|
2891
|
+
const turns = model.turns.length ? `<div class="absolute-voice-turn-quality__turns">${model.turns.map((turn) => `<article class="absolute-voice-turn-quality__turn absolute-voice-turn-quality__turn--${escapeHtml10(turn.status)}">
|
|
2629
2892
|
<header>
|
|
2630
|
-
<strong>${
|
|
2631
|
-
<span>${
|
|
2893
|
+
<strong>${escapeHtml10(turn.label)}</strong>
|
|
2894
|
+
<span>${escapeHtml10(turn.status)}</span>
|
|
2632
2895
|
</header>
|
|
2633
|
-
<p>${
|
|
2896
|
+
<p>${escapeHtml10(turn.detail)}</p>
|
|
2634
2897
|
<dl>${turn.rows.map((row) => `<div>
|
|
2635
|
-
<dt>${
|
|
2636
|
-
<dd>${
|
|
2898
|
+
<dt>${escapeHtml10(row.label)}</dt>
|
|
2899
|
+
<dd>${escapeHtml10(row.value)}</dd>
|
|
2637
2900
|
</div>`).join("")}</dl>
|
|
2638
2901
|
</article>`).join("")}</div>` : '<p class="absolute-voice-turn-quality__empty">Complete a voice turn to see STT quality diagnostics.</p>';
|
|
2639
|
-
return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${
|
|
2902
|
+
return `<section class="absolute-voice-turn-quality absolute-voice-turn-quality--${escapeHtml10(model.status)}">
|
|
2640
2903
|
<header class="absolute-voice-turn-quality__header">
|
|
2641
|
-
<span class="absolute-voice-turn-quality__eyebrow">${
|
|
2642
|
-
<strong class="absolute-voice-turn-quality__label">${
|
|
2904
|
+
<span class="absolute-voice-turn-quality__eyebrow">${escapeHtml10(model.title)}</span>
|
|
2905
|
+
<strong class="absolute-voice-turn-quality__label">${escapeHtml10(model.label)}</strong>
|
|
2643
2906
|
</header>
|
|
2644
|
-
<p class="absolute-voice-turn-quality__description">${
|
|
2907
|
+
<p class="absolute-voice-turn-quality__description">${escapeHtml10(model.description)}</p>
|
|
2645
2908
|
${turns}
|
|
2646
|
-
${model.error ? `<p class="absolute-voice-turn-quality__error">${
|
|
2909
|
+
${model.error ? `<p class="absolute-voice-turn-quality__error">${escapeHtml10(model.error)}</p>` : ""}
|
|
2647
2910
|
</section>`;
|
|
2648
2911
|
};
|
|
2649
2912
|
var getVoiceTurnQualityCSS = () => `.absolute-voice-turn-quality{border:1px solid #e4d1a3;border-radius:20px;background:#fff9eb;color:#17120a;padding:18px;box-shadow:0 18px 40px rgba(73,48,14,.12);font-family:inherit}.absolute-voice-turn-quality--error,.absolute-voice-turn-quality--warning{border-color:#f2a7a7;background:#fff5f3}.absolute-voice-turn-quality__header,.absolute-voice-turn-quality__turn header{align-items:start;display:flex;gap:12px;justify-content:space-between}.absolute-voice-turn-quality__eyebrow{color:#8a5a0a;font-size:12px;font-weight:800;letter-spacing:.08em;text-transform:uppercase}.absolute-voice-turn-quality__label{font-size:24px;line-height:1}.absolute-voice-turn-quality__description,.absolute-voice-turn-quality__turn p,.absolute-voice-turn-quality__turn dt,.absolute-voice-turn-quality__empty{color:#5a4930}.absolute-voice-turn-quality__turns{display:grid;gap:12px;margin-top:14px}.absolute-voice-turn-quality__turn{background:#fff;border:1px solid #f0dfba;border-radius:16px;padding:14px}.absolute-voice-turn-quality__turn--pass{border-color:#86efac}.absolute-voice-turn-quality__turn--warn,.absolute-voice-turn-quality__turn--unknown{border-color:#fbbf24}.absolute-voice-turn-quality__turn--fail{border-color:#f2a7a7}.absolute-voice-turn-quality__turn p{margin:10px 0}.absolute-voice-turn-quality__turn dl{display:grid;gap:8px;grid-template-columns:repeat(2,minmax(0,1fr));margin:0}.absolute-voice-turn-quality__turn div{background:#fff9eb;border:1px solid #f0dfba;border-radius:12px;padding:8px}.absolute-voice-turn-quality__turn dt{font-size:12px}.absolute-voice-turn-quality__turn dd{font-weight:800;margin:4px 0 0}.absolute-voice-turn-quality__empty{margin:14px 0 0}.absolute-voice-turn-quality__error{color:#9f1239;font-weight:700}`;
|
|
@@ -3425,6 +3688,7 @@ export {
|
|
|
3425
3688
|
createVoiceProviderSimulationControls,
|
|
3426
3689
|
createVoiceProviderCapabilities,
|
|
3427
3690
|
createVoiceOpsStatus,
|
|
3691
|
+
createVoiceOpsActionCenter,
|
|
3428
3692
|
createVoiceDeliveryRuntime,
|
|
3429
3693
|
createVoiceController,
|
|
3430
3694
|
createVoiceCampaignDialerProof
|