@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/react/index.js
CHANGED
|
@@ -387,9 +387,306 @@ var VoiceOpsStatus = ({
|
|
|
387
387
|
]
|
|
388
388
|
}, undefined, true, undefined, this);
|
|
389
389
|
};
|
|
390
|
-
// src/react/
|
|
390
|
+
// src/react/useVoiceProviderStatus.tsx
|
|
391
391
|
import { useEffect as useEffect2, useRef as useRef2, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
392
392
|
|
|
393
|
+
// src/client/providerStatus.ts
|
|
394
|
+
var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
|
|
395
|
+
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
396
|
+
const response = await fetchImpl(path);
|
|
397
|
+
if (!response.ok) {
|
|
398
|
+
throw new Error(`Voice provider status failed: HTTP ${response.status}`);
|
|
399
|
+
}
|
|
400
|
+
return await response.json();
|
|
401
|
+
};
|
|
402
|
+
var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
|
|
403
|
+
const listeners = new Set;
|
|
404
|
+
let closed = false;
|
|
405
|
+
let timer;
|
|
406
|
+
let snapshot = {
|
|
407
|
+
error: null,
|
|
408
|
+
isLoading: false,
|
|
409
|
+
providers: []
|
|
410
|
+
};
|
|
411
|
+
const emit = () => {
|
|
412
|
+
for (const listener of listeners) {
|
|
413
|
+
listener();
|
|
414
|
+
}
|
|
415
|
+
};
|
|
416
|
+
const refresh = async () => {
|
|
417
|
+
if (closed) {
|
|
418
|
+
return snapshot.providers;
|
|
419
|
+
}
|
|
420
|
+
snapshot = {
|
|
421
|
+
...snapshot,
|
|
422
|
+
error: null,
|
|
423
|
+
isLoading: true
|
|
424
|
+
};
|
|
425
|
+
emit();
|
|
426
|
+
try {
|
|
427
|
+
const providers = await fetchVoiceProviderStatus(path, options);
|
|
428
|
+
snapshot = {
|
|
429
|
+
error: null,
|
|
430
|
+
isLoading: false,
|
|
431
|
+
providers,
|
|
432
|
+
updatedAt: Date.now()
|
|
433
|
+
};
|
|
434
|
+
emit();
|
|
435
|
+
return providers;
|
|
436
|
+
} catch (error) {
|
|
437
|
+
snapshot = {
|
|
438
|
+
...snapshot,
|
|
439
|
+
error: error instanceof Error ? error.message : String(error),
|
|
440
|
+
isLoading: false
|
|
441
|
+
};
|
|
442
|
+
emit();
|
|
443
|
+
throw error;
|
|
444
|
+
}
|
|
445
|
+
};
|
|
446
|
+
const close = () => {
|
|
447
|
+
closed = true;
|
|
448
|
+
if (timer) {
|
|
449
|
+
clearInterval(timer);
|
|
450
|
+
timer = undefined;
|
|
451
|
+
}
|
|
452
|
+
listeners.clear();
|
|
453
|
+
};
|
|
454
|
+
if (options.intervalMs && options.intervalMs > 0) {
|
|
455
|
+
timer = setInterval(() => {
|
|
456
|
+
refresh().catch(() => {});
|
|
457
|
+
}, options.intervalMs);
|
|
458
|
+
}
|
|
459
|
+
return {
|
|
460
|
+
close,
|
|
461
|
+
getServerSnapshot: () => snapshot,
|
|
462
|
+
getSnapshot: () => snapshot,
|
|
463
|
+
refresh,
|
|
464
|
+
subscribe: (listener) => {
|
|
465
|
+
listeners.add(listener);
|
|
466
|
+
return () => {
|
|
467
|
+
listeners.delete(listener);
|
|
468
|
+
};
|
|
469
|
+
}
|
|
470
|
+
};
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
// src/react/useVoiceProviderStatus.tsx
|
|
474
|
+
var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
|
|
475
|
+
const storeRef = useRef2(null);
|
|
476
|
+
if (!storeRef.current) {
|
|
477
|
+
storeRef.current = createVoiceProviderStatusStore(path, options);
|
|
478
|
+
}
|
|
479
|
+
const store = storeRef.current;
|
|
480
|
+
useEffect2(() => {
|
|
481
|
+
store.refresh().catch(() => {});
|
|
482
|
+
return () => store.close();
|
|
483
|
+
}, [store]);
|
|
484
|
+
return {
|
|
485
|
+
...useSyncExternalStore2(store.subscribe, store.getSnapshot, store.getServerSnapshot),
|
|
486
|
+
refresh: store.refresh
|
|
487
|
+
};
|
|
488
|
+
};
|
|
489
|
+
|
|
490
|
+
// src/client/providerStatusWidget.ts
|
|
491
|
+
var DEFAULT_TITLE2 = "Voice Providers";
|
|
492
|
+
var DEFAULT_DESCRIPTION2 = "Live provider health, fallback counts, latency, and suppression state from your self-hosted trace store.";
|
|
493
|
+
var escapeHtml2 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
494
|
+
var formatProvider = (provider) => provider.split(/[-_\s]+/).filter(Boolean).map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ") || provider;
|
|
495
|
+
var formatStatus = (status) => status.split("-").map((part) => `${part[0]?.toUpperCase() ?? ""}${part.slice(1)}`).join(" ");
|
|
496
|
+
var formatLatency = (value) => typeof value === "number" ? `${value}ms` : "No samples";
|
|
497
|
+
var formatSuppression = (value) => typeof value === "number" ? `${Math.ceil(value / 1000)}s` : "None";
|
|
498
|
+
var getProviderDetail = (provider) => {
|
|
499
|
+
if (provider.status === "suppressed") {
|
|
500
|
+
return provider.lastError ? `Suppressed for ${formatSuppression(provider.suppressionRemainingMs)} after ${provider.lastError}.` : `Suppressed for ${formatSuppression(provider.suppressionRemainingMs)}.`;
|
|
501
|
+
}
|
|
502
|
+
if (provider.status === "recoverable") {
|
|
503
|
+
return "Cooldown expired; ready for recovery traffic.";
|
|
504
|
+
}
|
|
505
|
+
if (provider.status === "rate-limited") {
|
|
506
|
+
return "Rate limit detected; router should avoid this provider.";
|
|
507
|
+
}
|
|
508
|
+
if (provider.status === "degraded") {
|
|
509
|
+
return provider.lastError ?? "Recent provider errors detected.";
|
|
510
|
+
}
|
|
511
|
+
if (provider.status === "healthy") {
|
|
512
|
+
return provider.recommended ? "Healthy and currently recommended." : "Healthy and available for routing.";
|
|
513
|
+
}
|
|
514
|
+
return "No provider traffic observed yet.";
|
|
515
|
+
};
|
|
516
|
+
var isWarningStatus = (status) => status === "degraded" || status === "rate-limited" || status === "recoverable" || status === "suppressed";
|
|
517
|
+
var createVoiceProviderStatusViewModel = (snapshot, options = {}) => {
|
|
518
|
+
const providers = snapshot.providers.map((provider) => ({
|
|
519
|
+
...provider,
|
|
520
|
+
detail: getProviderDetail(provider),
|
|
521
|
+
label: `${formatProvider(provider.provider)}${provider.recommended ? " recommended" : ""}`,
|
|
522
|
+
rows: [
|
|
523
|
+
{ label: "Runs", value: String(provider.runCount) },
|
|
524
|
+
{ label: "Avg latency", value: formatLatency(provider.averageElapsedMs) },
|
|
525
|
+
{ label: "Errors", value: String(provider.errorCount) },
|
|
526
|
+
{ label: "Timeouts", value: String(provider.timeoutCount) },
|
|
527
|
+
{ label: "Fallbacks", value: String(provider.fallbackCount) },
|
|
528
|
+
{
|
|
529
|
+
label: "Suppression",
|
|
530
|
+
value: formatSuppression(provider.suppressionRemainingMs)
|
|
531
|
+
}
|
|
532
|
+
]
|
|
533
|
+
}));
|
|
534
|
+
const warningCount = providers.filter((provider) => isWarningStatus(provider.status)).length;
|
|
535
|
+
const healthyCount = providers.filter((provider) => provider.status === "healthy").length;
|
|
536
|
+
return {
|
|
537
|
+
description: options.description ?? DEFAULT_DESCRIPTION2,
|
|
538
|
+
error: snapshot.error,
|
|
539
|
+
isLoading: snapshot.isLoading,
|
|
540
|
+
label: snapshot.error ? "Unavailable" : providers.length ? warningCount > 0 ? `${warningCount} needs attention` : `${healthyCount} healthy` : snapshot.isLoading ? "Checking" : "No provider traffic",
|
|
541
|
+
providers,
|
|
542
|
+
status: snapshot.error ? "error" : providers.length ? warningCount > 0 ? "warning" : "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
543
|
+
title: options.title ?? DEFAULT_TITLE2,
|
|
544
|
+
updatedAt: snapshot.updatedAt
|
|
545
|
+
};
|
|
546
|
+
};
|
|
547
|
+
var renderVoiceProviderStatusHTML = (snapshot, options = {}) => {
|
|
548
|
+
const model = createVoiceProviderStatusViewModel(snapshot, options);
|
|
549
|
+
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)}">
|
|
550
|
+
<header>
|
|
551
|
+
<strong>${escapeHtml2(provider.label)}</strong>
|
|
552
|
+
<span>${escapeHtml2(formatStatus(provider.status))}</span>
|
|
553
|
+
</header>
|
|
554
|
+
<p>${escapeHtml2(provider.detail)}</p>
|
|
555
|
+
<dl>${provider.rows.map((row) => `<div>
|
|
556
|
+
<dt>${escapeHtml2(row.label)}</dt>
|
|
557
|
+
<dd>${escapeHtml2(row.value)}</dd>
|
|
558
|
+
</div>`).join("")}</dl>
|
|
559
|
+
</article>`).join("")}</div>` : '<p class="absolute-voice-provider-status__empty">Run voice traffic to see provider health.</p>';
|
|
560
|
+
return `<section class="absolute-voice-provider-status absolute-voice-provider-status--${escapeHtml2(model.status)}">
|
|
561
|
+
<header class="absolute-voice-provider-status__header">
|
|
562
|
+
<span class="absolute-voice-provider-status__eyebrow">${escapeHtml2(model.title)}</span>
|
|
563
|
+
<strong class="absolute-voice-provider-status__label">${escapeHtml2(model.label)}</strong>
|
|
564
|
+
</header>
|
|
565
|
+
<p class="absolute-voice-provider-status__description">${escapeHtml2(model.description)}</p>
|
|
566
|
+
${providers}
|
|
567
|
+
${model.error ? `<p class="absolute-voice-provider-status__error">${escapeHtml2(model.error)}</p>` : ""}
|
|
568
|
+
</section>`;
|
|
569
|
+
};
|
|
570
|
+
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}`;
|
|
571
|
+
var mountVoiceProviderStatus = (element, path = "/api/provider-status", options = {}) => {
|
|
572
|
+
const store = createVoiceProviderStatusStore(path, options);
|
|
573
|
+
const render = () => {
|
|
574
|
+
element.innerHTML = renderVoiceProviderStatusHTML(store.getSnapshot(), options);
|
|
575
|
+
};
|
|
576
|
+
const unsubscribe = store.subscribe(render);
|
|
577
|
+
render();
|
|
578
|
+
store.refresh().catch(() => {});
|
|
579
|
+
return {
|
|
580
|
+
close: () => {
|
|
581
|
+
unsubscribe();
|
|
582
|
+
store.close();
|
|
583
|
+
},
|
|
584
|
+
refresh: store.refresh
|
|
585
|
+
};
|
|
586
|
+
};
|
|
587
|
+
var defineVoiceProviderStatusElement = (tagName = "absolute-voice-provider-status") => {
|
|
588
|
+
if (typeof window === "undefined" || typeof customElements === "undefined" || customElements.get(tagName)) {
|
|
589
|
+
return;
|
|
590
|
+
}
|
|
591
|
+
customElements.define(tagName, class AbsoluteVoiceProviderStatusElement extends HTMLElement {
|
|
592
|
+
mounted;
|
|
593
|
+
connectedCallback() {
|
|
594
|
+
const intervalMs = Number(this.getAttribute("interval-ms") ?? 5000);
|
|
595
|
+
this.mounted = mountVoiceProviderStatus(this, this.getAttribute("path") ?? "/api/provider-status", {
|
|
596
|
+
description: this.getAttribute("description") ?? undefined,
|
|
597
|
+
intervalMs: Number.isFinite(intervalMs) ? intervalMs : 5000,
|
|
598
|
+
title: this.getAttribute("title") ?? undefined
|
|
599
|
+
});
|
|
600
|
+
}
|
|
601
|
+
disconnectedCallback() {
|
|
602
|
+
this.mounted?.close();
|
|
603
|
+
this.mounted = undefined;
|
|
604
|
+
}
|
|
605
|
+
});
|
|
606
|
+
};
|
|
607
|
+
|
|
608
|
+
// src/react/VoiceProviderStatus.tsx
|
|
609
|
+
import { jsxDEV as jsxDEV2 } from "react/jsx-dev-runtime";
|
|
610
|
+
var VoiceProviderStatus = ({
|
|
611
|
+
className,
|
|
612
|
+
path = "/api/provider-status",
|
|
613
|
+
...options
|
|
614
|
+
}) => {
|
|
615
|
+
const snapshot = useVoiceProviderStatus(path, options);
|
|
616
|
+
const model = createVoiceProviderStatusViewModel(snapshot, options);
|
|
617
|
+
return /* @__PURE__ */ jsxDEV2("section", {
|
|
618
|
+
className: [
|
|
619
|
+
"absolute-voice-provider-status",
|
|
620
|
+
`absolute-voice-provider-status--${model.status}`,
|
|
621
|
+
className
|
|
622
|
+
].filter(Boolean).join(" "),
|
|
623
|
+
children: [
|
|
624
|
+
/* @__PURE__ */ jsxDEV2("header", {
|
|
625
|
+
className: "absolute-voice-provider-status__header",
|
|
626
|
+
children: [
|
|
627
|
+
/* @__PURE__ */ jsxDEV2("span", {
|
|
628
|
+
className: "absolute-voice-provider-status__eyebrow",
|
|
629
|
+
children: model.title
|
|
630
|
+
}, undefined, false, undefined, this),
|
|
631
|
+
/* @__PURE__ */ jsxDEV2("strong", {
|
|
632
|
+
className: "absolute-voice-provider-status__label",
|
|
633
|
+
children: model.label
|
|
634
|
+
}, undefined, false, undefined, this)
|
|
635
|
+
]
|
|
636
|
+
}, undefined, true, undefined, this),
|
|
637
|
+
/* @__PURE__ */ jsxDEV2("p", {
|
|
638
|
+
className: "absolute-voice-provider-status__description",
|
|
639
|
+
children: model.description
|
|
640
|
+
}, undefined, false, undefined, this),
|
|
641
|
+
model.providers.length ? /* @__PURE__ */ jsxDEV2("div", {
|
|
642
|
+
className: "absolute-voice-provider-status__providers",
|
|
643
|
+
children: model.providers.map((provider) => /* @__PURE__ */ jsxDEV2("article", {
|
|
644
|
+
className: [
|
|
645
|
+
"absolute-voice-provider-status__provider",
|
|
646
|
+
`absolute-voice-provider-status__provider--${provider.status}`
|
|
647
|
+
].join(" "),
|
|
648
|
+
children: [
|
|
649
|
+
/* @__PURE__ */ jsxDEV2("header", {
|
|
650
|
+
children: [
|
|
651
|
+
/* @__PURE__ */ jsxDEV2("strong", {
|
|
652
|
+
children: provider.label
|
|
653
|
+
}, undefined, false, undefined, this),
|
|
654
|
+
/* @__PURE__ */ jsxDEV2("span", {
|
|
655
|
+
children: provider.status
|
|
656
|
+
}, undefined, false, undefined, this)
|
|
657
|
+
]
|
|
658
|
+
}, undefined, true, undefined, this),
|
|
659
|
+
/* @__PURE__ */ jsxDEV2("p", {
|
|
660
|
+
children: provider.detail
|
|
661
|
+
}, undefined, false, undefined, this),
|
|
662
|
+
/* @__PURE__ */ jsxDEV2("dl", {
|
|
663
|
+
children: provider.rows.map((row) => /* @__PURE__ */ jsxDEV2("div", {
|
|
664
|
+
children: [
|
|
665
|
+
/* @__PURE__ */ jsxDEV2("dt", {
|
|
666
|
+
children: row.label
|
|
667
|
+
}, undefined, false, undefined, this),
|
|
668
|
+
/* @__PURE__ */ jsxDEV2("dd", {
|
|
669
|
+
children: row.value
|
|
670
|
+
}, undefined, false, undefined, this)
|
|
671
|
+
]
|
|
672
|
+
}, row.label, true, undefined, this))
|
|
673
|
+
}, undefined, false, undefined, this)
|
|
674
|
+
]
|
|
675
|
+
}, provider.provider, true, undefined, this))
|
|
676
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV2("p", {
|
|
677
|
+
className: "absolute-voice-provider-status__empty",
|
|
678
|
+
children: "Run voice traffic to see provider health."
|
|
679
|
+
}, undefined, false, undefined, this),
|
|
680
|
+
model.error ? /* @__PURE__ */ jsxDEV2("p", {
|
|
681
|
+
className: "absolute-voice-provider-status__error",
|
|
682
|
+
children: model.error
|
|
683
|
+
}, undefined, false, undefined, this) : null
|
|
684
|
+
]
|
|
685
|
+
}, undefined, true, undefined, this);
|
|
686
|
+
};
|
|
687
|
+
// src/react/useVoiceRoutingStatus.tsx
|
|
688
|
+
import { useEffect as useEffect3, useRef as useRef3, useSyncExternalStore as useSyncExternalStore3 } from "react";
|
|
689
|
+
|
|
393
690
|
// src/client/routingStatus.ts
|
|
394
691
|
var fetchVoiceRoutingStatus = async (path = "/api/routing/latest", options = {}) => {
|
|
395
692
|
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
@@ -472,25 +769,25 @@ var createVoiceRoutingStatusStore = (path = "/api/routing/latest", options = {})
|
|
|
472
769
|
|
|
473
770
|
// src/react/useVoiceRoutingStatus.tsx
|
|
474
771
|
var useVoiceRoutingStatus = (path = "/api/routing/latest", options = {}) => {
|
|
475
|
-
const storeRef =
|
|
772
|
+
const storeRef = useRef3(null);
|
|
476
773
|
if (!storeRef.current) {
|
|
477
774
|
storeRef.current = createVoiceRoutingStatusStore(path, options);
|
|
478
775
|
}
|
|
479
776
|
const store = storeRef.current;
|
|
480
|
-
|
|
777
|
+
useEffect3(() => {
|
|
481
778
|
store.refresh().catch(() => {});
|
|
482
779
|
return () => store.close();
|
|
483
780
|
}, [store]);
|
|
484
781
|
return {
|
|
485
|
-
...
|
|
782
|
+
...useSyncExternalStore3(store.subscribe, store.getSnapshot, store.getServerSnapshot),
|
|
486
783
|
refresh: store.refresh
|
|
487
784
|
};
|
|
488
785
|
};
|
|
489
786
|
|
|
490
787
|
// src/client/routingStatusWidget.ts
|
|
491
|
-
var
|
|
492
|
-
var
|
|
493
|
-
var
|
|
788
|
+
var DEFAULT_TITLE3 = "Voice Routing";
|
|
789
|
+
var DEFAULT_DESCRIPTION3 = "Latest provider routing decision from the self-hosted trace store.";
|
|
790
|
+
var escapeHtml3 = (value) => value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
494
791
|
var formatValue = (value, fallback = "None") => typeof value === "string" && value.trim() ? value : typeof value === "number" && Number.isFinite(value) ? String(value) : fallback;
|
|
495
792
|
var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
496
793
|
const decision = snapshot.decision;
|
|
@@ -514,30 +811,30 @@ var createVoiceRoutingStatusViewModel = (snapshot, options = {}) => {
|
|
|
514
811
|
] : [];
|
|
515
812
|
return {
|
|
516
813
|
decision,
|
|
517
|
-
description: options.description ??
|
|
814
|
+
description: options.description ?? DEFAULT_DESCRIPTION3,
|
|
518
815
|
error: snapshot.error,
|
|
519
816
|
isLoading: snapshot.isLoading,
|
|
520
817
|
label: snapshot.error ? "Unavailable" : decision ? `${formatValue(decision.kind).toUpperCase()} ${formatValue(decision.status, "unknown")}` : snapshot.isLoading ? "Checking" : "No routing yet",
|
|
521
818
|
rows,
|
|
522
819
|
status: snapshot.error ? "error" : decision ? "ready" : snapshot.isLoading ? "loading" : "empty",
|
|
523
|
-
title: options.title ??
|
|
820
|
+
title: options.title ?? DEFAULT_TITLE3,
|
|
524
821
|
updatedAt: snapshot.updatedAt
|
|
525
822
|
};
|
|
526
823
|
};
|
|
527
824
|
var renderVoiceRoutingStatusHTML = (snapshot, options = {}) => {
|
|
528
825
|
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
529
826
|
const rows = model.rows.length ? `<div class="absolute-voice-routing-status__grid">${model.rows.map((row) => `<div>
|
|
530
|
-
<span>${
|
|
531
|
-
<strong>${
|
|
827
|
+
<span>${escapeHtml3(row.label)}</span>
|
|
828
|
+
<strong>${escapeHtml3(row.value)}</strong>
|
|
532
829
|
</div>`).join("")}</div>` : '<p class="absolute-voice-routing-status__empty">Start a voice session to see the selected provider.</p>';
|
|
533
|
-
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${
|
|
830
|
+
return `<section class="absolute-voice-routing-status absolute-voice-routing-status--${escapeHtml3(model.status)}">
|
|
534
831
|
<header class="absolute-voice-routing-status__header">
|
|
535
|
-
<span class="absolute-voice-routing-status__eyebrow">${
|
|
536
|
-
<strong class="absolute-voice-routing-status__label">${
|
|
832
|
+
<span class="absolute-voice-routing-status__eyebrow">${escapeHtml3(model.title)}</span>
|
|
833
|
+
<strong class="absolute-voice-routing-status__label">${escapeHtml3(model.label)}</strong>
|
|
537
834
|
</header>
|
|
538
|
-
<p class="absolute-voice-routing-status__description">${
|
|
835
|
+
<p class="absolute-voice-routing-status__description">${escapeHtml3(model.description)}</p>
|
|
539
836
|
${rows}
|
|
540
|
-
${model.error ? `<p class="absolute-voice-routing-status__error">${
|
|
837
|
+
${model.error ? `<p class="absolute-voice-routing-status__error">${escapeHtml3(model.error)}</p>` : ""}
|
|
541
838
|
</section>`;
|
|
542
839
|
};
|
|
543
840
|
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}`;
|
|
@@ -579,7 +876,7 @@ var defineVoiceRoutingStatusElement = (tagName = "absolute-voice-routing-status"
|
|
|
579
876
|
};
|
|
580
877
|
|
|
581
878
|
// src/react/VoiceRoutingStatus.tsx
|
|
582
|
-
import { jsxDEV as
|
|
879
|
+
import { jsxDEV as jsxDEV3 } from "react/jsx-dev-runtime";
|
|
583
880
|
var VoiceRoutingStatus = ({
|
|
584
881
|
className,
|
|
585
882
|
path = "/api/routing/latest",
|
|
@@ -587,47 +884,47 @@ var VoiceRoutingStatus = ({
|
|
|
587
884
|
}) => {
|
|
588
885
|
const snapshot = useVoiceRoutingStatus(path, options);
|
|
589
886
|
const model = createVoiceRoutingStatusViewModel(snapshot, options);
|
|
590
|
-
return /* @__PURE__ */
|
|
887
|
+
return /* @__PURE__ */ jsxDEV3("section", {
|
|
591
888
|
className: [
|
|
592
889
|
"absolute-voice-routing-status",
|
|
593
890
|
`absolute-voice-routing-status--${model.status}`,
|
|
594
891
|
className
|
|
595
892
|
].filter(Boolean).join(" "),
|
|
596
893
|
children: [
|
|
597
|
-
/* @__PURE__ */
|
|
894
|
+
/* @__PURE__ */ jsxDEV3("header", {
|
|
598
895
|
className: "absolute-voice-routing-status__header",
|
|
599
896
|
children: [
|
|
600
|
-
/* @__PURE__ */
|
|
897
|
+
/* @__PURE__ */ jsxDEV3("span", {
|
|
601
898
|
className: "absolute-voice-routing-status__eyebrow",
|
|
602
899
|
children: model.title
|
|
603
900
|
}, undefined, false, undefined, this),
|
|
604
|
-
/* @__PURE__ */
|
|
901
|
+
/* @__PURE__ */ jsxDEV3("strong", {
|
|
605
902
|
className: "absolute-voice-routing-status__label",
|
|
606
903
|
children: model.label
|
|
607
904
|
}, undefined, false, undefined, this)
|
|
608
905
|
]
|
|
609
906
|
}, undefined, true, undefined, this),
|
|
610
|
-
/* @__PURE__ */
|
|
907
|
+
/* @__PURE__ */ jsxDEV3("p", {
|
|
611
908
|
className: "absolute-voice-routing-status__description",
|
|
612
909
|
children: model.description
|
|
613
910
|
}, undefined, false, undefined, this),
|
|
614
|
-
model.rows.length ? /* @__PURE__ */
|
|
911
|
+
model.rows.length ? /* @__PURE__ */ jsxDEV3("div", {
|
|
615
912
|
className: "absolute-voice-routing-status__grid",
|
|
616
|
-
children: model.rows.map((row) => /* @__PURE__ */
|
|
913
|
+
children: model.rows.map((row) => /* @__PURE__ */ jsxDEV3("div", {
|
|
617
914
|
children: [
|
|
618
|
-
/* @__PURE__ */
|
|
915
|
+
/* @__PURE__ */ jsxDEV3("span", {
|
|
619
916
|
children: row.label
|
|
620
917
|
}, undefined, false, undefined, this),
|
|
621
|
-
/* @__PURE__ */
|
|
918
|
+
/* @__PURE__ */ jsxDEV3("strong", {
|
|
622
919
|
children: row.value
|
|
623
920
|
}, undefined, false, undefined, this)
|
|
624
921
|
]
|
|
625
922
|
}, row.label, true, undefined, this))
|
|
626
|
-
}, undefined, false, undefined, this) : /* @__PURE__ */
|
|
923
|
+
}, undefined, false, undefined, this) : /* @__PURE__ */ jsxDEV3("p", {
|
|
627
924
|
className: "absolute-voice-routing-status__empty",
|
|
628
925
|
children: "Start a voice session to see the selected provider."
|
|
629
926
|
}, undefined, false, undefined, this),
|
|
630
|
-
model.error ? /* @__PURE__ */
|
|
927
|
+
model.error ? /* @__PURE__ */ jsxDEV3("p", {
|
|
631
928
|
className: "absolute-voice-routing-status__error",
|
|
632
929
|
children: model.error
|
|
633
930
|
}, undefined, false, undefined, this) : null
|
|
@@ -635,7 +932,7 @@ var VoiceRoutingStatus = ({
|
|
|
635
932
|
}, undefined, true, undefined, this);
|
|
636
933
|
};
|
|
637
934
|
// src/react/useVoiceStream.tsx
|
|
638
|
-
import { useEffect as
|
|
935
|
+
import { useEffect as useEffect4, useRef as useRef4, useSyncExternalStore as useSyncExternalStore4 } from "react";
|
|
639
936
|
|
|
640
937
|
// src/client/actions.ts
|
|
641
938
|
var normalizeErrorMessage = (value) => {
|
|
@@ -1163,13 +1460,13 @@ var EMPTY_SNAPSHOT = {
|
|
|
1163
1460
|
turns: []
|
|
1164
1461
|
};
|
|
1165
1462
|
var useVoiceStream = (path, options = {}) => {
|
|
1166
|
-
const streamRef =
|
|
1463
|
+
const streamRef = useRef4(null);
|
|
1167
1464
|
if (!streamRef.current) {
|
|
1168
1465
|
streamRef.current = createVoiceStream(path, options);
|
|
1169
1466
|
}
|
|
1170
1467
|
const stream = streamRef.current;
|
|
1171
|
-
|
|
1172
|
-
const snapshot =
|
|
1468
|
+
useEffect4(() => () => stream.close(), [stream]);
|
|
1469
|
+
const snapshot = useSyncExternalStore4(stream.subscribe, stream.getSnapshot, stream.getServerSnapshot) ?? EMPTY_SNAPSHOT;
|
|
1173
1470
|
return {
|
|
1174
1471
|
...snapshot,
|
|
1175
1472
|
callControl: (message) => stream.callControl(message),
|
|
@@ -1179,7 +1476,7 @@ var useVoiceStream = (path, options = {}) => {
|
|
|
1179
1476
|
};
|
|
1180
1477
|
};
|
|
1181
1478
|
// src/react/useVoiceController.tsx
|
|
1182
|
-
import { useEffect as
|
|
1479
|
+
import { useEffect as useEffect5, useRef as useRef5, useSyncExternalStore as useSyncExternalStore5 } from "react";
|
|
1183
1480
|
|
|
1184
1481
|
// src/client/htmx.ts
|
|
1185
1482
|
var DEFAULT_EVENT_NAME = "voice-refresh";
|
|
@@ -1826,13 +2123,13 @@ var EMPTY_SNAPSHOT2 = {
|
|
|
1826
2123
|
turns: []
|
|
1827
2124
|
};
|
|
1828
2125
|
var useVoiceController = (path, options = {}) => {
|
|
1829
|
-
const controllerRef =
|
|
2126
|
+
const controllerRef = useRef5(null);
|
|
1830
2127
|
if (!controllerRef.current) {
|
|
1831
2128
|
controllerRef.current = createVoiceController(path, options);
|
|
1832
2129
|
}
|
|
1833
2130
|
const controller = controllerRef.current;
|
|
1834
|
-
|
|
1835
|
-
const snapshot =
|
|
2131
|
+
useEffect5(() => () => controller.close(), [controller]);
|
|
2132
|
+
const snapshot = useSyncExternalStore5(controller.subscribe, controller.getSnapshot, controller.getServerSnapshot) ?? EMPTY_SNAPSHOT2;
|
|
1836
2133
|
return {
|
|
1837
2134
|
...snapshot,
|
|
1838
2135
|
bindHTMX: controller.bindHTMX,
|
|
@@ -1845,105 +2142,6 @@ var useVoiceController = (path, options = {}) => {
|
|
|
1845
2142
|
toggleRecording: () => controller.toggleRecording()
|
|
1846
2143
|
};
|
|
1847
2144
|
};
|
|
1848
|
-
// src/react/useVoiceProviderStatus.tsx
|
|
1849
|
-
import { useEffect as useEffect5, useRef as useRef5, useSyncExternalStore as useSyncExternalStore5 } from "react";
|
|
1850
|
-
|
|
1851
|
-
// src/client/providerStatus.ts
|
|
1852
|
-
var fetchVoiceProviderStatus = async (path = "/api/provider-status", options = {}) => {
|
|
1853
|
-
const fetchImpl = options.fetch ?? globalThis.fetch;
|
|
1854
|
-
const response = await fetchImpl(path);
|
|
1855
|
-
if (!response.ok) {
|
|
1856
|
-
throw new Error(`Voice provider status failed: HTTP ${response.status}`);
|
|
1857
|
-
}
|
|
1858
|
-
return await response.json();
|
|
1859
|
-
};
|
|
1860
|
-
var createVoiceProviderStatusStore = (path = "/api/provider-status", options = {}) => {
|
|
1861
|
-
const listeners = new Set;
|
|
1862
|
-
let closed = false;
|
|
1863
|
-
let timer;
|
|
1864
|
-
let snapshot = {
|
|
1865
|
-
error: null,
|
|
1866
|
-
isLoading: false,
|
|
1867
|
-
providers: []
|
|
1868
|
-
};
|
|
1869
|
-
const emit = () => {
|
|
1870
|
-
for (const listener of listeners) {
|
|
1871
|
-
listener();
|
|
1872
|
-
}
|
|
1873
|
-
};
|
|
1874
|
-
const refresh = async () => {
|
|
1875
|
-
if (closed) {
|
|
1876
|
-
return snapshot.providers;
|
|
1877
|
-
}
|
|
1878
|
-
snapshot = {
|
|
1879
|
-
...snapshot,
|
|
1880
|
-
error: null,
|
|
1881
|
-
isLoading: true
|
|
1882
|
-
};
|
|
1883
|
-
emit();
|
|
1884
|
-
try {
|
|
1885
|
-
const providers = await fetchVoiceProviderStatus(path, options);
|
|
1886
|
-
snapshot = {
|
|
1887
|
-
error: null,
|
|
1888
|
-
isLoading: false,
|
|
1889
|
-
providers,
|
|
1890
|
-
updatedAt: Date.now()
|
|
1891
|
-
};
|
|
1892
|
-
emit();
|
|
1893
|
-
return providers;
|
|
1894
|
-
} catch (error) {
|
|
1895
|
-
snapshot = {
|
|
1896
|
-
...snapshot,
|
|
1897
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1898
|
-
isLoading: false
|
|
1899
|
-
};
|
|
1900
|
-
emit();
|
|
1901
|
-
throw error;
|
|
1902
|
-
}
|
|
1903
|
-
};
|
|
1904
|
-
const close = () => {
|
|
1905
|
-
closed = true;
|
|
1906
|
-
if (timer) {
|
|
1907
|
-
clearInterval(timer);
|
|
1908
|
-
timer = undefined;
|
|
1909
|
-
}
|
|
1910
|
-
listeners.clear();
|
|
1911
|
-
};
|
|
1912
|
-
if (options.intervalMs && options.intervalMs > 0) {
|
|
1913
|
-
timer = setInterval(() => {
|
|
1914
|
-
refresh().catch(() => {});
|
|
1915
|
-
}, options.intervalMs);
|
|
1916
|
-
}
|
|
1917
|
-
return {
|
|
1918
|
-
close,
|
|
1919
|
-
getServerSnapshot: () => snapshot,
|
|
1920
|
-
getSnapshot: () => snapshot,
|
|
1921
|
-
refresh,
|
|
1922
|
-
subscribe: (listener) => {
|
|
1923
|
-
listeners.add(listener);
|
|
1924
|
-
return () => {
|
|
1925
|
-
listeners.delete(listener);
|
|
1926
|
-
};
|
|
1927
|
-
}
|
|
1928
|
-
};
|
|
1929
|
-
};
|
|
1930
|
-
|
|
1931
|
-
// src/react/useVoiceProviderStatus.tsx
|
|
1932
|
-
var useVoiceProviderStatus = (path = "/api/provider-status", options = {}) => {
|
|
1933
|
-
const storeRef = useRef5(null);
|
|
1934
|
-
if (!storeRef.current) {
|
|
1935
|
-
storeRef.current = createVoiceProviderStatusStore(path, options);
|
|
1936
|
-
}
|
|
1937
|
-
const store = storeRef.current;
|
|
1938
|
-
useEffect5(() => {
|
|
1939
|
-
store.refresh().catch(() => {});
|
|
1940
|
-
return () => store.close();
|
|
1941
|
-
}, [store]);
|
|
1942
|
-
return {
|
|
1943
|
-
...useSyncExternalStore5(store.subscribe, store.getSnapshot, store.getServerSnapshot),
|
|
1944
|
-
refresh: store.refresh
|
|
1945
|
-
};
|
|
1946
|
-
};
|
|
1947
2145
|
// src/react/useVoiceWorkflowStatus.tsx
|
|
1948
2146
|
import { useEffect as useEffect6, useRef as useRef6, useSyncExternalStore as useSyncExternalStore6 } from "react";
|
|
1949
2147
|
|
|
@@ -2050,5 +2248,6 @@ export {
|
|
|
2050
2248
|
useVoiceController,
|
|
2051
2249
|
useVoiceAppKitStatus,
|
|
2052
2250
|
VoiceRoutingStatus,
|
|
2251
|
+
VoiceProviderStatus,
|
|
2053
2252
|
VoiceOpsStatus
|
|
2054
2253
|
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type
|
|
2
|
-
export declare const createVoiceProviderStatus: <TProvider extends string = string>(path?: string, options?:
|
|
1
|
+
import { type VoiceProviderStatusWidgetOptions } from '../client/providerStatusWidget';
|
|
2
|
+
export declare const createVoiceProviderStatus: <TProvider extends string = string>(path?: string, options?: VoiceProviderStatusWidgetOptions) => {
|
|
3
|
+
getHTML: () => string;
|
|
4
|
+
getViewModel: () => import("../client").VoiceProviderStatusViewModel<TProvider>;
|
|
3
5
|
close: () => void;
|
|
4
6
|
getServerSnapshot: () => import("../client").VoiceProviderStatusSnapshot<TProvider>;
|
|
5
7
|
getSnapshot: () => import("../client").VoiceProviderStatusSnapshot<TProvider>;
|