@absolutejs/voice 0.0.22-beta.59 → 0.0.22-beta.60
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/dist/client/index.d.ts +2 -0
- package/dist/client/index.js +122 -0
- package/dist/client/providerStatusWidget.d.ts +32 -0
- package/dist/react/VoiceProviderStatus.d.ts +6 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +334 -135
- package/dist/svelte/createVoiceProviderStatus.d.ts +4 -2
- package/dist/svelte/index.js +138 -13
- package/dist/vue/VoiceProviderStatus.d.ts +51 -0
- package/dist/vue/index.d.ts +1 -0
- package/dist/vue/index.js +358 -166
- package/package.json +1 -1
package/dist/vue/index.js
CHANGED
|
@@ -374,9 +374,312 @@ var VoiceOpsStatus = defineComponent({
|
|
|
374
374
|
};
|
|
375
375
|
}
|
|
376
376
|
});
|
|
377
|
-
// src/vue/
|
|
377
|
+
// src/vue/VoiceProviderStatus.ts
|
|
378
378
|
import { computed, defineComponent as defineComponent2, h as h2 } from "vue";
|
|
379
379
|
|
|
380
|
+
// src/client/providerStatus.ts
|
|
381
|
+
var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
|
|
382
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
383
|
+
const response = await fetchImpl(path);
|
|
384
|
+
if (!response.ok) {
|
|
385
|
+
throw new Error(`Voice provider status failed: HTTP ${response.status}`);
|
|
386
|
+
}
|
|
387
|
+
return await response.json();
|
|
388
|
+
};
|
|
389
|
+
var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
|
|
390
|
+
const listeners = new Set;
|
|
391
|
+
let closed = false;
|
|
392
|
+
let timer;
|
|
393
|
+
let snapshot = {
|
|
394
|
+
error: null,
|
|
395
|
+
isLoading: false,
|
|
396
|
+
providers: []
|
|
397
|
+
};
|
|
398
|
+
const emit = () => {
|
|
399
|
+
for (const listener of listeners) {
|
|
400
|
+
listener();
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
const refresh = async () => {
|
|
404
|
+
if (closed) {
|
|
405
|
+
return snapshot.providers;
|
|
406
|
+
}
|
|
407
|
+
snapshot = {
|
|
408
|
+
...snapshot,
|
|
409
|
+
error: null,
|
|
410
|
+
isLoading: true
|
|
411
|
+
};
|
|
412
|
+
emit();
|
|
413
|
+
try {
|
|
414
|
+
const providers = await fetchVoiceProviderStatus(path, options);
|
|
415
|
+
snapshot = {
|
|
416
|
+
error: null,
|
|
417
|
+
isLoading: false,
|
|
418
|
+
providers,
|
|
419
|
+
updatedAt: Date.now()
|
|
420
|
+
};
|
|
421
|
+
emit();
|
|
422
|
+
return providers;
|
|
423
|
+
} catch (error) {
|
|
424
|
+
snapshot = {
|
|
425
|
+
...snapshot,
|
|
426
|
+
error: error instanceof Error ? error.message : String(error),
|
|
427
|
+
isLoading: false
|
|
428
|
+
};
|
|
429
|
+
emit();
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
432
|
+
};
|
|
433
|
+
const close = () => {
|
|
434
|
+
closed = true;
|
|
435
|
+
if (timer) {
|
|
436
|
+
clearInterval(timer);
|
|
437
|
+
timer = undefined;
|
|
438
|
+
}
|
|
439
|
+
listeners.clear();
|
|
440
|
+
};
|
|
441
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
442
|
+
timer = setInterval(() => {
|
|
443
|
+
refresh().catch(() => {});
|
|
444
|
+
}, options.intervalMs);
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
close,
|
|
448
|
+
getServerSnapshot: () => snapshot,
|
|
449
|
+
getSnapshot: () => snapshot,
|
|
450
|
+
refresh,
|
|
451
|
+
subscribe: (listener) => {
|
|
452
|
+
listeners.add(listener);
|
|
453
|
+
return () => {
|
|
454
|
+
listeners.delete(listener);
|
|
455
|
+
};
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
// src/client/providerStatusWidget.ts
|
|
461
|
+
var DEFAULT_TITLE2 = "Voice Providers";
|
|
462
|
+
var DEFAULT_DESCRIPTION2 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
|
|
463
|
+
var escapeHtml2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
464
|
+
var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
|
|
465
|
+
var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
466
|
+
var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
|
|
467
|
+
var formatSuppression = (value) => typeof value === "number" ? `${Math.ceil(value / 1000)}s` : "None";
|
|
468
|
+
var getProviderDetail = (provider) => {
|
|
469
|
+
if (provider.status === "suppressed") {
|
|
470
|
+
return provider.lastError ? `Suppressed for ${formatSuppression(provider.suppressionRemainingMs)} after ${provider.lastError}.` : `Suppressed for ${formatSuppression(provider.suppressionRemainingMs)}.`;
|
|
471
|
+
}
|
|
472
|
+
if (provider.status === "recoverable") {
|
|
473
|
+
return "Cooldown expired; ready for recovery traffic.";
|
|
474
|
+
}
|
|
475
|
+
if (provider.status === "rate-limited") {
|
|
476
|
+
return "Rate limit detected; router should avoid this provider.";
|
|
477
|
+
}
|
|
478
|
+
if (provider.status === "degraded") {
|
|
479
|
+
return provider.lastError ?? "Recent provider errors detected.";
|
|
480
|
+
}
|
|
481
|
+
if (provider.status === "healthy") {
|
|
482
|
+
return provider.recommended ? "Healthy and currently recommended." : "Healthy and available for routing.";
|
|
483
|
+
}
|
|
484
|
+
return "No provider traffic observed yet.";
|
|
485
|
+
};
|
|
486
|
+
var isWarningStatus = (status) => status === "degraded" || status === "rate-limited" || status === "recoverable" || status === "suppressed";
|
|
487
|
+
var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
|
|
488
|
+
const providers = snapshot.providers.map((provider) => ({
|
|
489
|
+
...provider,
|
|
490
|
+
detail: getProviderDetail(provider),
|
|
491
|
+
label: `${formatProvider(provider.provider)}${provider.recommended ? " recommended" : ""}`,
|
|
492
|
+
rows: [
|
|
493
|
+
{ label: "Runs", value: String(provider.runCount) },
|
|
494
|
+
{ label: "Avg latency", value: formatLatency(provider.averageElapsedMs) },
|
|
495
|
+
{ label: "Errors", value: String(provider.errorCount) },
|
|
496
|
+
{ label: "Timeouts", value: String(provider.timeoutCount) },
|
|
497
|
+
{ label: "Fallbacks", value: String(provider.fallbackCount) },
|
|
498
|
+
{
|
|
499
|
+
label: "Suppression",
|
|
500
|
+
value: formatSuppression(provider.suppressionRemainingMs)
|
|
501
|
+
}
|
|
502
|
+
]
|
|
503
|
+
}));
|
|
504
|
+
const warningCount = providers.filter((provider) => isWarningStatus(provider.status)).length;
|
|
505
|
+
const healthyCount = providers.filter((provider) => provider.status === "healthy").length;
|
|
506
|
+
return {
|
|
507
|
+
description: options.description ?? DEFAULT_DESCRIPTION2,
|
|
508
|
+
error: snapshot.error,
|
|
509
|
+
isLoading: snapshot.isLoading,
|
|
510
|
+
label: snapshot.error ? "Unavailable" : providers.length ? warningCount > 0 ? `${warningCount} needs attention` : `${healthyCount} healthy` : snapshot.isLoading ? "Checking" : "No provider traffic",
|
|
511
|
+
providers,
|
|
512
|
+
status: snapshot.error ? "error" : providers.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
513
|
+
title: options.title ?? DEFAULT_TITLE2,
|
|
514
|
+
updatedAt: snapshot.updatedAt
|
|
515
|
+
};
|
|
516
|
+
};
|
|
517
|
+
var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
|
|
518
|
+
const model = createVoiceProviderStatusViewModel(snapshot, options);
|
|
519
|
+
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--${escapeHtml2(provider.status)}">
|
|
520
|
+
<header>
|
|
521
|
+
<strong>${escapeHtml2(provider.label)}</strong>
|
|
522
|
+
<span>${escapeHtml2(formatStatus(provider.status))}</span>
|
|
523
|
+
</header>
|
|
524
|
+
<p>${escapeHtml2(provider.detail)}</p>
|
|
525
|
+
<dl>${provider.rows.map((row) => `<div>
|
|
526
|
+
<dt>${escapeHtml2(row.label)}</dt>
|
|
527
|
+
<dd>${escapeHtml2(row.value)}</dd>
|
|
528
|
+
</div>`).join("")}</dl>
|
|
529
|
+
</article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
|
|
530
|
+
return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml2(model.status)}">
|
|
531
|
+
<header class="absolute-voice-provider-status__header">
|
|
532
|
+
<span class="absolute-voice-provider-status__eyebrow">${escapeHtml2(model.title)}</span>
|
|
533
|
+
<strong class="absolute-voice-provider-status__label">${escapeHtml2(model.label)}</strong>
|
|
534
|
+
</header>
|
|
535
|
+
<p class="absolute-voice-provider-status__description">${escapeHtml2(model.description)}</p>
|
|
536
|
+
${providers}
|
|
537
|
+
${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml2(model.error)}</p>` : ""}
|
|
538
|
+
</section>`;
|
|
539
|
+
};
|
|
540
|
+
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}`;
|
|
541
|
+
var mountVoiceProviderStatus = (element, path = "/api/provider-status", options = {}) => {
|
|
542
|
+
const store = createVoiceProviderStatusStore(path, options);
|
|
543
|
+
const render = () => {
|
|
544
|
+
element.innerHTML = renderVoiceProviderStatusHTML(store.getSnapshot(), options);
|
|
545
|
+
};
|
|
546
|
+
const unsubscribe = store.subscribe(render);
|
|
547
|
+
render();
|
|
548
|
+
store.refresh().catch(() => {});
|
|
549
|
+
return {
|
|
550
|
+
close: () => {
|
|
551
|
+
unsubscribe();
|
|
552
|
+
store.close();
|
|
553
|
+
},
|
|
554
|
+
refresh: store.refresh
|
|
555
|
+
};
|
|
556
|
+
};
|
|
557
|
+
var defineVoiceProviderStatusElement = (tagName = "absolute-voice-provider-status") => {
|
|
558
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
customElements.define(tagName, class AbsoluteVoiceProviderStatusElement extends HTMLElement {
|
|
562
|
+
mounted;
|
|
563
|
+
connectedCallback() {
|
|
564
|
+
const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
|
|
565
|
+
this.mounted = mountVoiceProviderStatus(this, this.getAttribute("path") ?? "/api/provider-status", {
|
|
566
|
+
description: this.getAttribute("description") ?? undefined,
|
|
567
|
+
intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
|
|
568
|
+
title: this.getAttribute("title") ?? undefined
|
|
569
|
+
});
|
|
570
|
+
}
|
|
571
|
+
disconnectedCallback() {
|
|
572
|
+
this.mounted?.close();
|
|
573
|
+
this.mounted = undefined;
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
};
|
|
577
|
+
|
|
578
|
+
// src/vue/useVoiceProviderStatus.ts
|
|
579
|
+
import { onUnmounted as onUnmounted2, ref as ref2, shallowRef as shallowRef2 } from "vue";
|
|
580
|
+
var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
|
|
581
|
+
const store = createVoiceProviderStatusStore(path, options);
|
|
582
|
+
const error = ref2(null);
|
|
583
|
+
const isLoading = ref2(false);
|
|
584
|
+
const providers = shallowRef2([]);
|
|
585
|
+
const updatedAt = ref2(undefined);
|
|
586
|
+
const sync = () => {
|
|
587
|
+
const snapshot = store.getSnapshot();
|
|
588
|
+
error.value = snapshot.error;
|
|
589
|
+
isLoading.value = snapshot.isLoading;
|
|
590
|
+
providers.value = [...snapshot.providers];
|
|
591
|
+
updatedAt.value = snapshot.updatedAt;
|
|
592
|
+
};
|
|
593
|
+
const unsubscribe = store.subscribe(sync);
|
|
594
|
+
sync();
|
|
595
|
+
store.refresh().catch(() => {});
|
|
596
|
+
onUnmounted2(() => {
|
|
597
|
+
unsubscribe();
|
|
598
|
+
store.close();
|
|
599
|
+
});
|
|
600
|
+
return {
|
|
601
|
+
error,
|
|
602
|
+
isLoading,
|
|
603
|
+
providers,
|
|
604
|
+
refresh: store.refresh,
|
|
605
|
+
updatedAt
|
|
606
|
+
};
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
// src/vue/VoiceProviderStatus.ts
|
|
610
|
+
var VoiceProviderStatus = defineComponent2({
|
|
611
|
+
name: "VoiceProviderStatus",
|
|
612
|
+
props: {
|
|
613
|
+
class: {
|
|
614
|
+
default: "",
|
|
615
|
+
type: String
|
|
616
|
+
},
|
|
617
|
+
description: {
|
|
618
|
+
default: undefined,
|
|
619
|
+
type: String
|
|
620
|
+
},
|
|
621
|
+
intervalMs: {
|
|
622
|
+
default: 5000,
|
|
623
|
+
type: Number
|
|
624
|
+
},
|
|
625
|
+
path: {
|
|
626
|
+
default: "/api/provider-status",
|
|
627
|
+
type: String
|
|
628
|
+
},
|
|
629
|
+
title: {
|
|
630
|
+
default: undefined,
|
|
631
|
+
type: String
|
|
632
|
+
}
|
|
633
|
+
},
|
|
634
|
+
setup(props) {
|
|
635
|
+
const options = {
|
|
636
|
+
description: props.description,
|
|
637
|
+
intervalMs: props.intervalMs,
|
|
638
|
+
title: props.title
|
|
639
|
+
};
|
|
640
|
+
const status = useVoiceProviderStatus(props.path, options);
|
|
641
|
+
const model = computed(() => createVoiceProviderStatusViewModel({
|
|
642
|
+
error: status.error.value,
|
|
643
|
+
isLoading: status.isLoading.value,
|
|
644
|
+
providers: status.providers.value,
|
|
645
|
+
updatedAt: status.updatedAt.value
|
|
646
|
+
}, options));
|
|
647
|
+
return () => h2("section", {
|
|
648
|
+
class: [
|
|
649
|
+
"absolute-voice-provider-status",
|
|
650
|
+
`absolute-voice-provider-status--${model.value.status}`,
|
|
651
|
+
props.class
|
|
652
|
+
]
|
|
653
|
+
}, [
|
|
654
|
+
h2("header", { class: "absolute-voice-provider-status__header" }, [
|
|
655
|
+
h2("span", { class: "absolute-voice-provider-status__eyebrow" }, model.value.title),
|
|
656
|
+
h2("strong", { class: "absolute-voice-provider-status__label" }, model.value.label)
|
|
657
|
+
]),
|
|
658
|
+
h2("p", { class: "absolute-voice-provider-status__description" }, model.value.description),
|
|
659
|
+
model.value.providers.length ? h2("div", { class: "absolute-voice-provider-status__providers" }, model.value.providers.map((provider) => h2("article", {
|
|
660
|
+
class: [
|
|
661
|
+
"absolute-voice-provider-status__provider",
|
|
662
|
+
`absolute-voice-provider-status__provider--${provider.status}`
|
|
663
|
+
],
|
|
664
|
+
key: provider.provider
|
|
665
|
+
}, [
|
|
666
|
+
h2("header", [
|
|
667
|
+
h2("strong", provider.label),
|
|
668
|
+
h2("span", provider.status)
|
|
669
|
+
]),
|
|
670
|
+
h2("p", provider.detail),
|
|
671
|
+
h2("dl", provider.rows.map((row) => h2("div", { key: row.label }, [
|
|
672
|
+
h2("dt", row.label),
|
|
673
|
+
h2("dd", row.value)
|
|
674
|
+
])))
|
|
675
|
+
]))) : h2("p", { class: "absolute-voice-provider-status__empty" }, "Run voice traffic to see provider health."),
|
|
676
|
+
model.value.error ? h2("p", { class: "absolute-voice-provider-status__error" }, model.value.error) : null
|
|
677
|
+
]);
|
|
678
|
+
}
|
|
679
|
+
});
|
|
680
|
+
// src/vue/VoiceRoutingStatus.ts
|
|
681
|
+
import { computed as computed2, defineComponent as defineComponent3, h as h3 } from "vue";
|
|
682
|
+
|
|
380
683
|
// src/client/routingStatus.ts
|
|
381
684
|
var fetchVoiceRoutingStatus = async (path = "/api/routing/latest", options = {}) => {
|
|
382
685
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -458,9 +761,9 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
|
|
|
458
761
|
};
|
|
459
762
|
|
|
460
763
|
// src/client/routingStatusWidget.ts
|
|
461
|
-
var
|
|
462
|
-
var
|
|
463
|
-
var
|
|
764
|
+
var DEFAULT_TITLE3 = "Voice Routing";
|
|
765
|
+
var DEFAULT_DESCRIPTION3 = "Latest provider routing decision from the self-hosted trace store.";
|
|
766
|
+
var escapeHtml3 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
464
767
|
var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
|
|
465
768
|
var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
466
769
|
const decision = snapshot.decision;
|
|
@@ -484,30 +787,30 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
|
484
787
|
] : [];
|
|
485
788
|
return {
|
|
486
789
|
decision,
|
|
487
|
-
description: options.description ??
|
|
790
|
+
description: options.description ?? DEFAULT_DESCRIPTION3,
|
|
488
791
|
error: snapshot.error,
|
|
489
792
|
isLoading: snapshot.isLoading,
|
|
490
793
|
label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
|
|
491
794
|
rows,
|
|
492
795
|
status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
493
|
-
title: options.title ??
|
|
796
|
+
title: options.title ?? DEFAULT_TITLE3,
|
|
494
797
|
updatedAt: snapshot.updatedAt
|
|
495
798
|
};
|
|
496
799
|
};
|
|
497
800
|
var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
498
801
|
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
499
802
|
const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
|
|
500
|
-
<span>${
|
|
501
|
-
<strong>${
|
|
803
|
+
<span>${escapeHtml3(row.label)}</span>
|
|
804
|
+
<strong>${escapeHtml3(row.value)}</strong>
|
|
502
805
|
</div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
|
|
503
|
-
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${
|
|
806
|
+
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml3(model.status)}">
|
|
504
807
|
<header class="absolute-voice-routing-status__header">
|
|
505
|
-
<span class="absolute-voice-routing-status__eyebrow">${
|
|
506
|
-
<strong class="absolute-voice-routing-status__label">${
|
|
808
|
+
<span class="absolute-voice-routing-status__eyebrow">${escapeHtml3(model.title)}</span>
|
|
809
|
+
<strong class="absolute-voice-routing-status__label">${escapeHtml3(model.label)}</strong>
|
|
507
810
|
</header>
|
|
508
|
-
<p class="absolute-voice-routing-status__description">${
|
|
811
|
+
<p class="absolute-voice-routing-status__description">${escapeHtml3(model.description)}</p>
|
|
509
812
|
${rows}
|
|
510
|
-
${model.error ? `<p class="absolute-voice-routing-status__error">${
|
|
813
|
+
${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml3(model.error)}</p>` : ""}
|
|
511
814
|
</section>`;
|
|
512
815
|
};
|
|
513
816
|
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}`;
|
|
@@ -549,13 +852,13 @@ var defineVoiceRoutingStatusElement = (tagName = "absolute-voice-routing-status"
|
|
|
549
852
|
};
|
|
550
853
|
|
|
551
854
|
// src/vue/useVoiceRoutingStatus.ts
|
|
552
|
-
import { onUnmounted as
|
|
855
|
+
import { onUnmounted as onUnmounted3, ref as ref3, shallowRef as shallowRef3 } from "vue";
|
|
553
856
|
var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
|
|
554
857
|
const store = createVoiceRoutingStatusStore(path, options);
|
|
555
|
-
const decision =
|
|
556
|
-
const error =
|
|
557
|
-
const isLoading =
|
|
558
|
-
const updatedAt =
|
|
858
|
+
const decision = shallowRef3(null);
|
|
859
|
+
const error = ref3(null);
|
|
860
|
+
const isLoading = ref3(false);
|
|
861
|
+
const updatedAt = ref3(undefined);
|
|
559
862
|
const sync = () => {
|
|
560
863
|
const snapshot = store.getSnapshot();
|
|
561
864
|
decision.value = snapshot.decision;
|
|
@@ -566,7 +869,7 @@ var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
|
|
|
566
869
|
const unsubscribe = store.subscribe(sync);
|
|
567
870
|
sync();
|
|
568
871
|
store.refresh().catch(() => {});
|
|
569
|
-
|
|
872
|
+
onUnmounted3(() => {
|
|
570
873
|
unsubscribe();
|
|
571
874
|
store.close();
|
|
572
875
|
});
|
|
@@ -580,7 +883,7 @@ var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
|
|
|
580
883
|
};
|
|
581
884
|
|
|
582
885
|
// src/vue/VoiceRoutingStatus.ts
|
|
583
|
-
var VoiceRoutingStatus =
|
|
886
|
+
var VoiceRoutingStatus = defineComponent3({
|
|
584
887
|
name: "VoiceRoutingStatus",
|
|
585
888
|
props: {
|
|
586
889
|
class: {
|
|
@@ -611,34 +914,34 @@ var VoiceRoutingStatus = defineComponent2({
|
|
|
611
914
|
title: props.title
|
|
612
915
|
};
|
|
613
916
|
const status = useVoiceRoutingStatus(props.path, options);
|
|
614
|
-
const model =
|
|
917
|
+
const model = computed2(() => createVoiceRoutingStatusViewModel({
|
|
615
918
|
decision: status.decision.value,
|
|
616
919
|
error: status.error.value,
|
|
617
920
|
isLoading: status.isLoading.value,
|
|
618
921
|
updatedAt: status.updatedAt.value
|
|
619
922
|
}, options));
|
|
620
|
-
return () =>
|
|
923
|
+
return () => h3("section", {
|
|
621
924
|
class: [
|
|
622
925
|
"absolute-voice-routing-status",
|
|
623
926
|
`absolute-voice-routing-status--${model.value.status}`,
|
|
624
927
|
props.class
|
|
625
928
|
]
|
|
626
929
|
}, [
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
930
|
+
h3("header", { class: "absolute-voice-routing-status__header" }, [
|
|
931
|
+
h3("span", { class: "absolute-voice-routing-status__eyebrow" }, model.value.title),
|
|
932
|
+
h3("strong", { class: "absolute-voice-routing-status__label" }, model.value.label)
|
|
630
933
|
]),
|
|
631
|
-
|
|
632
|
-
model.value.rows.length ?
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
]))) :
|
|
636
|
-
model.value.error ?
|
|
934
|
+
h3("p", { class: "absolute-voice-routing-status__description" }, model.value.description),
|
|
935
|
+
model.value.rows.length ? h3("div", { class: "absolute-voice-routing-status__grid" }, model.value.rows.map((row) => h3("div", { key: row.label }, [
|
|
936
|
+
h3("span", row.label),
|
|
937
|
+
h3("strong", row.value)
|
|
938
|
+
]))) : h3("p", { class: "absolute-voice-routing-status__empty" }, "Start a voice session to see the selected provider."),
|
|
939
|
+
model.value.error ? h3("p", { class: "absolute-voice-routing-status__error" }, model.value.error) : null
|
|
637
940
|
]);
|
|
638
941
|
}
|
|
639
942
|
});
|
|
640
943
|
// src/vue/useVoiceStream.ts
|
|
641
|
-
import { onUnmounted as
|
|
944
|
+
import { onUnmounted as onUnmounted4, ref as ref4, shallowRef as shallowRef4 } from "vue";
|
|
642
945
|
|
|
643
946
|
// src/client/actions.ts
|
|
644
947
|
var normalizeErrorMessage = (value) => {
|
|
@@ -1156,15 +1459,15 @@ var createVoiceStream = (path, options = {}) => {
|
|
|
1156
1459
|
// src/vue/useVoiceStream.ts
|
|
1157
1460
|
var useVoiceStream = (path, options = {}) => {
|
|
1158
1461
|
const stream = createVoiceStream(path, options);
|
|
1159
|
-
const assistantAudio =
|
|
1160
|
-
const assistantTexts =
|
|
1161
|
-
const call =
|
|
1162
|
-
const error =
|
|
1163
|
-
const isConnected =
|
|
1164
|
-
const partial =
|
|
1165
|
-
const sessionId =
|
|
1166
|
-
const status =
|
|
1167
|
-
const turns =
|
|
1462
|
+
const assistantAudio = shallowRef4([]);
|
|
1463
|
+
const assistantTexts = shallowRef4([]);
|
|
1464
|
+
const call = shallowRef4(null);
|
|
1465
|
+
const error = ref4(null);
|
|
1466
|
+
const isConnected = ref4(false);
|
|
1467
|
+
const partial = ref4("");
|
|
1468
|
+
const sessionId = ref4(stream.sessionId);
|
|
1469
|
+
const status = ref4(stream.status);
|
|
1470
|
+
const turns = shallowRef4([]);
|
|
1168
1471
|
const sync = () => {
|
|
1169
1472
|
assistantAudio.value = [...stream.assistantAudio];
|
|
1170
1473
|
assistantTexts.value = [...stream.assistantTexts];
|
|
@@ -1182,7 +1485,7 @@ var useVoiceStream = (path, options = {}) => {
|
|
|
1182
1485
|
unsubscribe();
|
|
1183
1486
|
stream.close();
|
|
1184
1487
|
};
|
|
1185
|
-
|
|
1488
|
+
onUnmounted4(destroy);
|
|
1186
1489
|
return {
|
|
1187
1490
|
assistantAudio,
|
|
1188
1491
|
assistantTexts,
|
|
@@ -1200,7 +1503,7 @@ var useVoiceStream = (path, options = {}) => {
|
|
|
1200
1503
|
};
|
|
1201
1504
|
};
|
|
1202
1505
|
// src/vue/useVoiceController.ts
|
|
1203
|
-
import { onUnmounted as
|
|
1506
|
+
import { onUnmounted as onUnmounted5, ref as ref5, shallowRef as shallowRef5 } from "vue";
|
|
1204
1507
|
|
|
1205
1508
|
// src/client/htmx.ts
|
|
1206
1509
|
var DEFAULT_EVENT_NAME = "voice-refresh";
|
|
@@ -1835,16 +2138,16 @@ var createVoiceController = (path, options = {}) => {
|
|
|
1835
2138
|
// src/vue/useVoiceController.ts
|
|
1836
2139
|
var useVoiceController = (path, options = {}) => {
|
|
1837
2140
|
const controller = createVoiceController(path, options);
|
|
1838
|
-
const assistantAudio =
|
|
1839
|
-
const assistantTexts =
|
|
1840
|
-
const error =
|
|
1841
|
-
const isConnected =
|
|
1842
|
-
const isRecording =
|
|
1843
|
-
const partial =
|
|
1844
|
-
const recordingError =
|
|
1845
|
-
const sessionId =
|
|
1846
|
-
const status =
|
|
1847
|
-
const turns =
|
|
2141
|
+
const assistantAudio = shallowRef5([]);
|
|
2142
|
+
const assistantTexts = shallowRef5([]);
|
|
2143
|
+
const error = ref5(null);
|
|
2144
|
+
const isConnected = ref5(false);
|
|
2145
|
+
const isRecording = ref5(false);
|
|
2146
|
+
const partial = ref5("");
|
|
2147
|
+
const recordingError = ref5(null);
|
|
2148
|
+
const sessionId = ref5(controller.sessionId);
|
|
2149
|
+
const status = ref5(controller.status);
|
|
2150
|
+
const turns = shallowRef5([]);
|
|
1848
2151
|
const sync = () => {
|
|
1849
2152
|
assistantAudio.value = [...controller.assistantAudio];
|
|
1850
2153
|
assistantTexts.value = [...controller.assistantTexts];
|
|
@@ -1863,7 +2166,7 @@ var useVoiceController = (path, options = {}) => {
|
|
|
1863
2166
|
unsubscribe();
|
|
1864
2167
|
controller.close();
|
|
1865
2168
|
};
|
|
1866
|
-
|
|
2169
|
+
onUnmounted5(destroy);
|
|
1867
2170
|
return {
|
|
1868
2171
|
assistantAudio,
|
|
1869
2172
|
assistantTexts,
|
|
@@ -1884,118 +2187,6 @@ var useVoiceController = (path, options = {}) => {
|
|
|
1884
2187
|
turns
|
|
1885
2188
|
};
|
|
1886
2189
|
};
|
|
1887
|
-
// src/vue/useVoiceProviderStatus.ts
|
|
1888
|
-
import { onUnmounted as onUnmounted5, ref as ref5, shallowRef as shallowRef5 } from "vue";
|
|
1889
|
-
|
|
1890
|
-
// src/client/providerStatus.ts
|
|
1891
|
-
var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
|
|
1892
|
-
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1893
|
-
const response = await fetchImpl(path);
|
|
1894
|
-
if (!response.ok) {
|
|
1895
|
-
throw new Error(`Voice provider status failed: HTTP ${response.status}`);
|
|
1896
|
-
}
|
|
1897
|
-
return await response.json();
|
|
1898
|
-
};
|
|
1899
|
-
var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
|
|
1900
|
-
const listeners = new Set;
|
|
1901
|
-
let closed = false;
|
|
1902
|
-
let timer;
|
|
1903
|
-
let snapshot = {
|
|
1904
|
-
error: null,
|
|
1905
|
-
isLoading: false,
|
|
1906
|
-
providers: []
|
|
1907
|
-
};
|
|
1908
|
-
const emit = () => {
|
|
1909
|
-
for (const listener of listeners) {
|
|
1910
|
-
listener();
|
|
1911
|
-
}
|
|
1912
|
-
};
|
|
1913
|
-
const refresh = async () => {
|
|
1914
|
-
if (closed) {
|
|
1915
|
-
return snapshot.providers;
|
|
1916
|
-
}
|
|
1917
|
-
snapshot = {
|
|
1918
|
-
...snapshot,
|
|
1919
|
-
error: null,
|
|
1920
|
-
isLoading: true
|
|
1921
|
-
};
|
|
1922
|
-
emit();
|
|
1923
|
-
try {
|
|
1924
|
-
const providers = await fetchVoiceProviderStatus(path, options);
|
|
1925
|
-
snapshot = {
|
|
1926
|
-
error: null,
|
|
1927
|
-
isLoading: false,
|
|
1928
|
-
providers,
|
|
1929
|
-
updatedAt: Date.now()
|
|
1930
|
-
};
|
|
1931
|
-
emit();
|
|
1932
|
-
return providers;
|
|
1933
|
-
} catch (error) {
|
|
1934
|
-
snapshot = {
|
|
1935
|
-
...snapshot,
|
|
1936
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1937
|
-
isLoading: false
|
|
1938
|
-
};
|
|
1939
|
-
emit();
|
|
1940
|
-
throw error;
|
|
1941
|
-
}
|
|
1942
|
-
};
|
|
1943
|
-
const close = () => {
|
|
1944
|
-
closed = true;
|
|
1945
|
-
if (timer) {
|
|
1946
|
-
clearInterval(timer);
|
|
1947
|
-
timer = undefined;
|
|
1948
|
-
}
|
|
1949
|
-
listeners.clear();
|
|
1950
|
-
};
|
|
1951
|
-
if (options.intervalMs && options.intervalMs > 0) {
|
|
1952
|
-
timer = setInterval(() => {
|
|
1953
|
-
refresh().catch(() => {});
|
|
1954
|
-
}, options.intervalMs);
|
|
1955
|
-
}
|
|
1956
|
-
return {
|
|
1957
|
-
close,
|
|
1958
|
-
getServerSnapshot: () => snapshot,
|
|
1959
|
-
getSnapshot: () => snapshot,
|
|
1960
|
-
refresh,
|
|
1961
|
-
subscribe: (listener) => {
|
|
1962
|
-
listeners.add(listener);
|
|
1963
|
-
return () => {
|
|
1964
|
-
listeners.delete(listener);
|
|
1965
|
-
};
|
|
1966
|
-
}
|
|
1967
|
-
};
|
|
1968
|
-
};
|
|
1969
|
-
|
|
1970
|
-
// src/vue/useVoiceProviderStatus.ts
|
|
1971
|
-
var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
|
|
1972
|
-
const store = createVoiceProviderStatusStore(path, options);
|
|
1973
|
-
const error = ref5(null);
|
|
1974
|
-
const isLoading = ref5(false);
|
|
1975
|
-
const providers = shallowRef5([]);
|
|
1976
|
-
const updatedAt = ref5(undefined);
|
|
1977
|
-
const sync = () => {
|
|
1978
|
-
const snapshot = store.getSnapshot();
|
|
1979
|
-
error.value = snapshot.error;
|
|
1980
|
-
isLoading.value = snapshot.isLoading;
|
|
1981
|
-
providers.value = [...snapshot.providers];
|
|
1982
|
-
updatedAt.value = snapshot.updatedAt;
|
|
1983
|
-
};
|
|
1984
|
-
const unsubscribe = store.subscribe(sync);
|
|
1985
|
-
sync();
|
|
1986
|
-
store.refresh().catch(() => {});
|
|
1987
|
-
onUnmounted5(() => {
|
|
1988
|
-
unsubscribe();
|
|
1989
|
-
store.close();
|
|
1990
|
-
});
|
|
1991
|
-
return {
|
|
1992
|
-
error,
|
|
1993
|
-
isLoading,
|
|
1994
|
-
providers,
|
|
1995
|
-
refresh: store.refresh,
|
|
1996
|
-
updatedAt
|
|
1997
|
-
};
|
|
1998
|
-
};
|
|
1999
2190
|
// src/vue/useVoiceWorkflowStatus.ts
|
|
2000
2191
|
import { onUnmounted as onUnmounted6, ref as ref6, shallowRef as shallowRef6 } from "vue";
|
|
2001
2192
|
|
|
@@ -2117,5 +2308,6 @@ export {
|
|
|
2117
2308
|
useVoiceController,
|
|
2118
2309
|
useVoiceAppKitStatus,
|
|
2119
2310
|
VoiceRoutingStatus,
|
|
2311
|
+
VoiceProviderStatus,
|
|
2120
2312
|
VoiceOpsStatus
|
|
2121
2313
|
};
|