@urun-sh/openai 0.6.17 → 0.6.18
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/chunk-3U7YQ7LE.js +62 -0
- package/dist/chunk-MLJ66LPI.js +1 -0
- package/dist/chunk-NYSZ3RGB.js +4 -0
- package/dist/chunk-O4TKY7NE.js +7 -0
- package/dist/hosted/bin.cjs +31 -31
- package/dist/hosted/bin.js +1 -1
- package/dist/hosted/index.cjs +32 -32
- package/dist/hosted/index.d.cts +1 -1
- package/dist/hosted/index.d.ts +1 -1
- package/dist/hosted/index.js +1 -1
- package/dist/pi-extension/index.cjs +7 -7
- package/dist/pi-extension/index.js +1 -1
- package/dist/pi-extension/standalone.cjs +33 -33
- package/dist/proxy/cli.cjs +38 -38
- package/dist/proxy/cli.js +1 -1
- package/dist/proxy/index.cjs +25 -25
- package/dist/proxy/index.d.cts +89 -5
- package/dist/proxy/index.d.ts +18 -4
- package/dist/proxy/index.js +1 -1
- package/dist/{responses-turn-Dd-h46sQ.d.cts → responses-turn-B91w2YKn.d.cts} +125 -25
- package/dist/{responses-turn-B0zlsQGn.d.ts → responses-turn-FcEDaymF.d.ts} +18 -12
- package/package.json +2 -2
- package/dist/chunk-6SN4EJ7Q.js +0 -7
- package/dist/chunk-TMQJY56F.js +0 -4
- package/dist/chunk-U7EJSC2N.js +0 -62
- package/dist/chunk-VRCCLQ5P.js +0 -1
package/dist/proxy/index.d.cts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Server, IncomingMessage } from 'node:http';
|
|
2
|
-
import { f as ProxyHandlerOptions, P as ProxyClients } from '../responses-turn-
|
|
3
|
-
export { D as DialObserver, a as DialReport, g as ProxyIdentity, h as ProxyVideoOutLane, S as SessionGoneError, U as UnknownModelError } from '../responses-turn-
|
|
2
|
+
import { f as ProxyHandlerOptions, P as ProxyClients } from '../responses-turn-B91w2YKn.cjs';
|
|
3
|
+
export { D as DialObserver, a as DialReport, g as ProxyIdentity, h as ProxyVideoOutLane, S as SessionGoneError, U as UnknownModelError } from '../responses-turn-B91w2YKn.cjs';
|
|
4
4
|
export { h as GEMINI_LIVE_PATH, V as VIDEO_OUT_SETUP_KEY } from '../translator-CO8W_hmJ.cjs';
|
|
5
5
|
import { WebSocket } from 'ws';
|
|
6
6
|
import '../models-DUdx_Y6X.cjs';
|
|
@@ -339,6 +339,17 @@ interface RealtimeBinding<S = unknown> {
|
|
|
339
339
|
};
|
|
340
340
|
/** `model` the binding names for this conversation (overrides session config default). */
|
|
341
341
|
model?: string;
|
|
342
|
+
/**
|
|
343
|
+
* The GA transcription-session intent (`?intent=transcription`): this
|
|
344
|
+
* connection is a TRANSCRIPTION session — transcription events flow
|
|
345
|
+
* without the client opting in, and `response.create` answers the loud
|
|
346
|
+
* `unsupported` error (the connection stays open) because there are no
|
|
347
|
+
* model turns to create. Set by the shared binding composition (binding.ts)
|
|
348
|
+
* only when the resolver's URL carried the intent AND the model's catalog
|
|
349
|
+
* row vouches stt (the binding carries `turns`). Absent = the realtime
|
|
350
|
+
* (voice) session, today's behaviour.
|
|
351
|
+
*/
|
|
352
|
+
intent?: 'transcription';
|
|
342
353
|
/** True when `session` is a resumed authorized session (vs a fresh private one). */
|
|
343
354
|
resumed?: boolean;
|
|
344
355
|
/**
|
|
@@ -350,8 +361,14 @@ interface RealtimeBinding<S = unknown> {
|
|
|
350
361
|
* `turn_start` IS the native barge-in — the adapter drives server-VAD
|
|
351
362
|
* emulation, real transcription events, and model-visible committed audio
|
|
352
363
|
* from it. Absent = the backend wired no turn lane: manual mode only.
|
|
364
|
+
*
|
|
365
|
+
* The CONSUMER passes its own lifecycle `signal` (the realtime connection's
|
|
366
|
+
* turn-lane controller): the lane must END its stream subscription when it
|
|
367
|
+
* aborts — the transport races its pulls against it — so the subscription
|
|
368
|
+
* never outlives the consumer. A consumer that passes none gets an
|
|
369
|
+
* un-raced lane (its own teardown is then its own problem).
|
|
353
370
|
*/
|
|
354
|
-
turns?: () => AsyncIterable<{
|
|
371
|
+
turns?: (signal?: AbortSignal) => AsyncIterable<{
|
|
355
372
|
kind: 'delta' | 'final' | 'turn_start' | 'turn_end' | 'turn_eager_end' | 'turn_resumed';
|
|
356
373
|
text?: string;
|
|
357
374
|
confidence?: number;
|
|
@@ -446,10 +463,39 @@ declare class OpenAIRealtimeConnection<S = unknown> {
|
|
|
446
463
|
private closed;
|
|
447
464
|
/** The backend's turn/transcript lane consumer (binding.turns), if wired. */
|
|
448
465
|
private turnLaneAbort;
|
|
449
|
-
/**
|
|
466
|
+
/**
|
|
467
|
+
* The CONSUMER-SIDE turn-lane iterator — held so teardown can end the lane
|
|
468
|
+
* subscription through the async-iterator return() protocol when it is
|
|
469
|
+
* parked at a YIELD (no pending pull for the abort to unwind; a plain
|
|
470
|
+
* for-await has no handle to close). Null once the consumer loop exits.
|
|
471
|
+
*/
|
|
472
|
+
private turnLaneIterator;
|
|
473
|
+
/** Accumulated transcript of the OPEN user turn (delta/final events). The
|
|
474
|
+
* `final` flag says the engine finalized the accumulated text (its last
|
|
475
|
+
* transcript event was a `final`) — a manual commit may complete on it. */
|
|
450
476
|
private turnTranscript;
|
|
451
477
|
/** Transcript of the LAST committed turn (the model-visible commit). */
|
|
452
478
|
private lastCommittedTranscript;
|
|
479
|
+
/**
|
|
480
|
+
* A manual transcription-session commit whose item is still awaiting the
|
|
481
|
+
* lane's final text (the item id of the committed item): interim deltas
|
|
482
|
+
* meanwhile carry THIS id, and the lane's next `final`/`turn_end`
|
|
483
|
+
* discharges exactly one `…transcription.completed` for it. No timers.
|
|
484
|
+
*/
|
|
485
|
+
private pendingTranscriptItemId;
|
|
486
|
+
/**
|
|
487
|
+
* The engine turn a manual transcription-session commit ALREADY discharged
|
|
488
|
+
* (the discharged item's id): the lane's `final` completed the committed
|
|
489
|
+
* item (or the commit completed it on the lane's already-final text) while
|
|
490
|
+
* the engine's `turn_end` for THAT utterance is still to come. That
|
|
491
|
+
* `turn_end` is the boundary signal only — the server-side commit it
|
|
492
|
+
* would otherwise open mints a SECOND item for the same utterance and
|
|
493
|
+
* clears audio the client already appended for the next one. Consumed by
|
|
494
|
+
* that `turn_end` itself (one-shot); invalidated by the next `turn_start`
|
|
495
|
+
* (a new engine utterance opened — its boundary was not the discharged
|
|
496
|
+
* one).
|
|
497
|
+
*/
|
|
498
|
+
private manuallyDischargedTurnItem;
|
|
453
499
|
constructor(opts: {
|
|
454
500
|
ws: WebSocket;
|
|
455
501
|
binding: RealtimeBinding<S>;
|
|
@@ -464,8 +510,33 @@ declare class OpenAIRealtimeConnection<S = unknown> {
|
|
|
464
510
|
* is the native barge-in, turn_end is the native commit, deltas/finals are
|
|
465
511
|
* the REAL input transcription. This is the server-VAD primitive, not an
|
|
466
512
|
* emulation gap: the voice engine itself decides turn boundaries.
|
|
513
|
+
*
|
|
514
|
+
* TEARDOWN ENDS THE SUBSCRIPTION FROM EVERY PARKING STATE, which a plain
|
|
515
|
+
* `for await` cannot do (its `break` only runs after the next event — a
|
|
516
|
+
* quiet engine parks the pull forever, and a `return()` enqueued behind a
|
|
517
|
+
* pending `next()` waits for it to settle):
|
|
518
|
+
* - the `abort` threaded into `turns()` unwinds a PENDING pull (the
|
|
519
|
+
* transport races its `messages()` pulls against it);
|
|
520
|
+
* - the HELD consumer iterator's `return()` (teardown) closes a lane
|
|
521
|
+
* parked at a YIELD with no pending pull.
|
|
467
522
|
*/
|
|
468
523
|
private startTurnLane;
|
|
524
|
+
/** True when this connection IS a transcription session (the GA verdict:
|
|
525
|
+
* the binding's `?intent=transcription`, or the client's own
|
|
526
|
+
* `session.type: "transcription"` — the latter only accepted behind a
|
|
527
|
+
* turns lane, so a transcription session always has a transcript source). */
|
|
528
|
+
private isTranscriptionSession;
|
|
529
|
+
/** True when input-transcription events flow: the client opted in on a
|
|
530
|
+
* voice session, or ALWAYS on a transcription session (no opt-in needed). */
|
|
531
|
+
private transcriptionEventsOn;
|
|
532
|
+
/**
|
|
533
|
+
* Discharge a pending manual transcription commit: the ONE
|
|
534
|
+
* `…transcription.completed` for the committed item, carrying the text the
|
|
535
|
+
* lane delivered for its audio, and a reset shared accumulator (the
|
|
536
|
+
* utterance is client-delivered; the next utterance starts clean). The
|
|
537
|
+
* lane's own boundary events drive this — no timers anywhere.
|
|
538
|
+
*/
|
|
539
|
+
private completePendingTranscription;
|
|
469
540
|
/** One backend turn/transcript event → REAL GA protocol events. */
|
|
470
541
|
private onTurnEvent;
|
|
471
542
|
/** The committed turn's transcript, consumed once (null = none). */
|
|
@@ -647,8 +718,21 @@ declare function realtimeModelOf(req: IncomingMessage): string | undefined;
|
|
|
647
718
|
* (`sessionHandle` once → `openAudioOn(handle, signal)` +
|
|
648
719
|
* `createResponseOn(handle, …)`), so no turn on this connection can ever
|
|
649
720
|
* resolve a different pooled session than the bridge.
|
|
721
|
+
* - An stt verdict (the catalog `task` gate `supportsTranscription`, the
|
|
722
|
+
* SAME router-backed oracle the /v1/audio/transcriptions lane rides) with
|
|
723
|
+
* no `openTurnsOn` seam ⇒ 501 — a transcription-capable binding without
|
|
724
|
+
* its transcript source is never wired silently. A row that OVERRIDES
|
|
725
|
+
* the platform's default transcript stream (`engine_args.transcript_stream`,
|
|
726
|
+
* resolved through `transcriptStreamFor` when the seam is wired) is the
|
|
727
|
+
* same-shape 501 — the lane never listens on a stream the engine does
|
|
728
|
+
* not write.
|
|
729
|
+
* - `?intent=transcription` on anything but an stt row ⇒ 400 naming the
|
|
730
|
+
* model; any other intent value ⇒ 400 naming the accepted values. Every
|
|
731
|
+
* refusal lands BEFORE the pooled session is resolved (`sessionHandle`)
|
|
732
|
+
* and the bridge opens — a refused upgrade admits nothing. On an stt row
|
|
733
|
+
* the intent is recorded on the binding (a transcription session).
|
|
650
734
|
*/
|
|
651
|
-
declare function realtimeBindingFor(clients: Pick<ProxyClients, 'sessionHandle' | 'createResponseOn'> & Partial<Pick<ProxyClients, 'supportsAudio' | 'openAudioOn'>>, model: string | undefined, signal?: AbortSignal): Promise<RealtimeBinding<ProxyClients>>;
|
|
735
|
+
declare function realtimeBindingFor(clients: Pick<ProxyClients, 'sessionHandle' | 'createResponseOn'> & Partial<Pick<ProxyClients, 'supportsAudio' | 'openAudioOn' | 'supportsTranscription' | 'openTurnsOn' | 'transcriptStreamFor'>>, model: string | undefined, signal?: AbortSignal, intent?: string): Promise<RealtimeBinding<ProxyClients>>;
|
|
652
736
|
/**
|
|
653
737
|
* The LOCAL lane resolver: fixed pooled clients, optional fixed apiKey (the
|
|
654
738
|
* same shared-secret rule the local Gemini Live lane applies — a mismatched
|
package/dist/proxy/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Server, IncomingMessage } from 'node:http';
|
|
2
|
-
import { f as ProxyHandlerOptions, P as ProxyClients } from '../responses-turn-
|
|
3
|
-
export { D as DialObserver, a as DialReport, g as ProxyIdentity, h as ProxyVideoOutLane, S as SessionGoneError, U as UnknownModelError } from '../responses-turn-
|
|
2
|
+
import { f as ProxyHandlerOptions, P as ProxyClients } from '../responses-turn-FcEDaymF.js';
|
|
3
|
+
export { D as DialObserver, a as DialReport, g as ProxyIdentity, h as ProxyVideoOutLane, S as SessionGoneError, U as UnknownModelError } from '../responses-turn-FcEDaymF.js';
|
|
4
4
|
export { h as GEMINI_LIVE_PATH, V as VIDEO_OUT_SETUP_KEY } from '../translator-Ddd65sXR.js';
|
|
5
5
|
import { WebSocket } from 'ws';
|
|
6
6
|
import '../models-DUdx_Y6X.js';
|
|
@@ -81,9 +81,11 @@ interface RealtimeBinding<S = unknown> {
|
|
|
81
81
|
|
|
82
82
|
model?: string;
|
|
83
83
|
|
|
84
|
+
intent?: 'transcription';
|
|
85
|
+
|
|
84
86
|
resumed?: boolean;
|
|
85
87
|
|
|
86
|
-
turns?: () => AsyncIterable<{
|
|
88
|
+
turns?: (signal?: AbortSignal) => AsyncIterable<{
|
|
87
89
|
kind: 'delta' | 'final' | 'turn_start' | 'turn_end' | 'turn_eager_end' | 'turn_resumed';
|
|
88
90
|
text?: string;
|
|
89
91
|
confidence?: number;
|
|
@@ -128,9 +130,15 @@ declare class OpenAIRealtimeConnection<S = unknown> {
|
|
|
128
130
|
|
|
129
131
|
private turnLaneAbort;
|
|
130
132
|
|
|
133
|
+
private turnLaneIterator;
|
|
134
|
+
|
|
131
135
|
private turnTranscript;
|
|
132
136
|
|
|
133
137
|
private lastCommittedTranscript;
|
|
138
|
+
|
|
139
|
+
private pendingTranscriptItemId;
|
|
140
|
+
|
|
141
|
+
private manuallyDischargedTurnItem;
|
|
134
142
|
constructor(opts: {
|
|
135
143
|
ws: WebSocket;
|
|
136
144
|
binding: RealtimeBinding<S>;
|
|
@@ -142,6 +150,12 @@ declare class OpenAIRealtimeConnection<S = unknown> {
|
|
|
142
150
|
|
|
143
151
|
private startTurnLane;
|
|
144
152
|
|
|
153
|
+
private isTranscriptionSession;
|
|
154
|
+
|
|
155
|
+
private transcriptionEventsOn;
|
|
156
|
+
|
|
157
|
+
private completePendingTranscription;
|
|
158
|
+
|
|
145
159
|
private onTurnEvent;
|
|
146
160
|
|
|
147
161
|
private takeTurnTranscript;
|
|
@@ -188,7 +202,7 @@ declare class OpenAIRealtimeConnection<S = unknown> {
|
|
|
188
202
|
|
|
189
203
|
declare function realtimeModelOf(req: IncomingMessage): string | undefined;
|
|
190
204
|
|
|
191
|
-
declare function realtimeBindingFor(clients: Pick<ProxyClients, 'sessionHandle' | 'createResponseOn'> & Partial<Pick<ProxyClients, 'supportsAudio' | 'openAudioOn'>>, model: string | undefined, signal?: AbortSignal): Promise<RealtimeBinding<ProxyClients>>;
|
|
205
|
+
declare function realtimeBindingFor(clients: Pick<ProxyClients, 'sessionHandle' | 'createResponseOn'> & Partial<Pick<ProxyClients, 'supportsAudio' | 'openAudioOn' | 'supportsTranscription' | 'openTurnsOn' | 'transcriptStreamFor'>>, model: string | undefined, signal?: AbortSignal, intent?: string): Promise<RealtimeBinding<ProxyClients>>;
|
|
192
206
|
|
|
193
207
|
declare function fixedClientsResolver(clients: ProxyClients, apiKey?: string): {
|
|
194
208
|
resolve(req: IncomingMessage, signal?: AbortSignal): Promise<RealtimeBinding<ProxyClients>>;
|
package/dist/proxy/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{B as l,C as a,D as x,E as s,
|
|
1
|
+
import{B as l,C as a,D as x,E as s,G as O,H as f,T as I,a as n,b as p,c as m}from"../chunk-3U7YQ7LE.js";import{f as r,g as i}from"../chunk-NYSZ3RGB.js";import{a as o,h as t}from"../chunk-3JWYIKHM.js";import"../chunk-6M5JK4YC.js";import{c as e}from"../chunk-YSFSRI3D.js";e();export{o as GEMINI_LIVE_PATH,a as OPENAI_REALTIME_PATH,l as OpenAIRealtimeConnection,n as ResumptionRegistry,i as SessionGoneError,r as UnknownModelError,t as VIDEO_OUT_SETUP_KEY,p as attachGeminiLive,x as attachOpenAIRealtime,I as createOpenAIProxy,f as fixedClientsResolver,O as realtimeBindingFor,s as realtimeModelOf,m as serveGeminiLiveSocket};
|
|
@@ -381,7 +381,8 @@ interface ModelPriceRow {
|
|
|
381
381
|
}
|
|
382
382
|
|
|
383
383
|
/**
|
|
384
|
-
* THE OPENROUTER PROVIDER DOCUMENT — `GET /
|
|
384
|
+
* THE OPENROUTER PROVIDER DOCUMENT — `GET /partners/openrouter/models`
|
|
385
|
+
* (`GET /v1/models?format=openrouter` answers 308 here for one release).
|
|
385
386
|
*
|
|
386
387
|
* OpenRouter's provider monitor polls this document (schema 2.4, per
|
|
387
388
|
* openrouter.ai/docs/guides/community/for-providers §1) to list a provider's
|
|
@@ -610,28 +611,30 @@ declare class UnknownModelError extends Error {
|
|
|
610
611
|
declare class SessionGoneError extends Error {
|
|
611
612
|
}
|
|
612
613
|
/**
|
|
613
|
-
*
|
|
614
|
-
*
|
|
615
|
-
*
|
|
616
|
-
* the
|
|
617
|
-
*
|
|
614
|
+
* One `/v1/models` row — a CLOSED allow-list of externally contracted keys
|
|
615
|
+
* (owner ruling, ENG-472: publish what an external contract names, never the
|
|
616
|
+
* implementation). The four OpenAI `Model` fields, plus — only when known —
|
|
617
|
+
* the Hugging Face pair (`context_length`, `pricing`), the TTS `voices` and
|
|
618
|
+
* the `capabilities` object. `name`, `description`, `task`, `engine`,
|
|
619
|
+
* `gpu_spec` and `warm` are GONE from the type so they cannot be set; every
|
|
620
|
+
* row builder goes through `publicModelEntry` (openrouter-doc.ts), which
|
|
621
|
+
* copies exactly these keys.
|
|
618
622
|
*/
|
|
619
623
|
interface RouterModelEntry {
|
|
624
|
+
/** OpenAI `Model`: the model identifier callers reference in the API. */
|
|
620
625
|
id: string;
|
|
626
|
+
/** OpenAI `Model`: always "model". */
|
|
621
627
|
object: 'model';
|
|
628
|
+
/** OpenAI `Model`: the Unix time the model id first appeared in the uRun catalog (0 until the catalog carries it). */
|
|
622
629
|
created: number;
|
|
630
|
+
/** OpenAI `Model`: the organization that owns the model. */
|
|
623
631
|
owned_by: string;
|
|
624
|
-
/**
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
/** Serving engine (vllm | sglang | llamacpp | ...). */
|
|
631
|
-
engine?: string;
|
|
632
|
-
/** The catalog lane this app deploys, e.g. 'rtx6000:1'. */
|
|
633
|
-
gpu_spec?: string;
|
|
634
|
-
/** Context window in tokens (chat rows carry it in engine_args). */
|
|
632
|
+
/**
|
|
633
|
+
* Hugging Face provider comparison table — its top-level `context_length`
|
|
634
|
+
* read (HF also gates `:fastest`/`:cheapest` on it). Only when the catalog
|
|
635
|
+
* states it and the placements agree (openrouter-doc.ts
|
|
636
|
+
* `placementsContextLength`).
|
|
637
|
+
*/
|
|
635
638
|
context_length?: number;
|
|
636
639
|
/**
|
|
637
640
|
* Per-token price, USD per MILLION tokens — the exact shape Hugging Face
|
|
@@ -641,8 +644,6 @@ interface RouterModelEntry {
|
|
|
641
644
|
* a zero or an estimate here would be a silent lie on a public table.
|
|
642
645
|
*/
|
|
643
646
|
pricing?: TokenPricing;
|
|
644
|
-
/** Idle-floor replica count for a shared-lane model (U6 reconciler). */
|
|
645
|
-
warm?: number;
|
|
646
647
|
/**
|
|
647
648
|
* The voices a `tts`-task model synthesizes under (ENG-331 — OpenAI's
|
|
648
649
|
* speech API names a voice per request, so a caller needs the list).
|
|
@@ -664,7 +665,7 @@ interface RouterModelEntry {
|
|
|
664
665
|
* whose placements disagree, a malformed value, or a slug with no catalog
|
|
665
666
|
* row omit the object entirely — unknown, never guessed
|
|
666
667
|
* (openrouter-doc.ts `placementsReasoning`, applied by both listing
|
|
667
|
-
* lanes).
|
|
668
|
+
* lanes). The conformance kit's voice_profile check reads it.
|
|
668
669
|
*/
|
|
669
670
|
capabilities?: {
|
|
670
671
|
reasoning: boolean;
|
|
@@ -674,10 +675,11 @@ interface RouterModelList {
|
|
|
674
675
|
object: 'list';
|
|
675
676
|
data: RouterModelEntry[];
|
|
676
677
|
}
|
|
677
|
-
/** One `/v1/images/models` entry: the
|
|
678
|
+
/** One `/v1/images/models` entry: the allow-listed model row plus the image
|
|
678
679
|
* modes the catalog rows vouch for (the SAME per-placement consensus
|
|
679
680
|
* {@link ModelRouter.supportsImages} applies — a row set that vouches
|
|
680
|
-
* nothing is NOT listed).
|
|
681
|
+
* nothing is NOT listed). `image_modes` is allow-listed for THIS listing
|
|
682
|
+
* only (publicModelEntry's image overload). */
|
|
681
683
|
interface RouterImageModelEntry extends RouterModelEntry {
|
|
682
684
|
image_modes: ImageCapabilityMode[];
|
|
683
685
|
}
|
|
@@ -1102,7 +1104,8 @@ declare class ModelRouter<S> {
|
|
|
1102
1104
|
*/
|
|
1103
1105
|
modelList(): Promise<RouterModelList>;
|
|
1104
1106
|
/**
|
|
1105
|
-
* The OpenRouter PROVIDER document (`GET /
|
|
1107
|
+
* The OpenRouter PROVIDER document (`GET /partners/openrouter/models`;
|
|
1108
|
+
* `GET /v1/models?format=openrouter` 308-redirects there for one release):
|
|
1106
1109
|
* schema 2.4 per openrouter.ai/docs/guides/community/for-providers §1 —
|
|
1107
1110
|
* "an endpoint that returns all models that should be served by
|
|
1108
1111
|
* OpenRouter". That is the SHARED block's sellable endpoints (the models
|
|
@@ -1218,6 +1221,25 @@ declare class ModelRouter<S> {
|
|
|
1218
1221
|
* an infrastructure outage.
|
|
1219
1222
|
*/
|
|
1220
1223
|
supportsTranscription(model: string | undefined): Promise<boolean>;
|
|
1224
|
+
/**
|
|
1225
|
+
* THE REALTIME TRANSCRIPTION LANE'S STREAM-NAME RESOLUTION (openai-realtime
|
|
1226
|
+
* binding.ts, ENG-461): the §5 stream the model's engine emits its own
|
|
1227
|
+
* transcript events on — `engine_args.transcript_stream`, defaulting to
|
|
1228
|
+
* the platform's {@link STT_TRANSCRIPT_STREAM} when no row carries the
|
|
1229
|
+
* field, over the SAME catalog join {@link supportsTranscription} uses
|
|
1230
|
+
* (exact `(model_id, variant)` for a shared-lane hit,
|
|
1231
|
+
* {@link catalogRowsForSlug} for a caller-org app). Deliberately NOT
|
|
1232
|
+
* task-gated: the binding consults it only behind a `transcribes` verdict,
|
|
1233
|
+
* and the resolution itself is a pure field consensus. The lane's consume
|
|
1234
|
+
* side reads the PLATFORM DEFAULT only — a row that OVERRIDES the default
|
|
1235
|
+
* is the binding's loud 501 naming the field, never a silent listen on a
|
|
1236
|
+
* stream the engine does not write; a catalog that cannot vow ONE name
|
|
1237
|
+
* (disagreeing placements, a non-string or empty value) is the loud
|
|
1238
|
+
* defect, never a guess. Undeployed uRun-namespace models throw
|
|
1239
|
+
* {@link UnknownModelError}, and a CONFIGURED catalog oracle that FAILS
|
|
1240
|
+
* propagates — never a fabricated default.
|
|
1241
|
+
*/
|
|
1242
|
+
transcriptStreamFor(model: string | undefined): Promise<string>;
|
|
1221
1243
|
/**
|
|
1222
1244
|
* THE SPEECH LANE'S VOICE-SET RESOLUTION (proxy/speech.ts): the voice set
|
|
1223
1245
|
* `/v1/models` publishes for `model` — resolved by the SAME resolver the
|
|
@@ -1268,6 +1290,43 @@ declare class ModelRouter<S> {
|
|
|
1268
1290
|
closeAll(): Promise<void>;
|
|
1269
1291
|
}
|
|
1270
1292
|
|
|
1293
|
+
/**
|
|
1294
|
+
* THE TURN/TRANSCRIPT LANE over the named §5 `stt` data stream — the
|
|
1295
|
+
* engine's OWN transcript source for the OpenAI Realtime transcription
|
|
1296
|
+
* intent (ENG-461).
|
|
1297
|
+
*
|
|
1298
|
+
* The delivery path is the platform's existing one (urun-python
|
|
1299
|
+
* `urun.serve.transcribe_bridge`): the streaming STT engine emits its own
|
|
1300
|
+
* transcript events on the session-scoped named stream {@link
|
|
1301
|
+
* STT_TRANSCRIPT_STREAM} — the SAME §5 named-DATA mechanism the text-lane
|
|
1302
|
+
* token deltas ride, in the text lane's own wire shape (`{"t": kind,
|
|
1303
|
+
* "delta": text, "start_ms": int}`). Nothing here invents a framing: the
|
|
1304
|
+
* lane is `session.stream('stt').messages()` (core `SessionStream.messages`,
|
|
1305
|
+
* contract §5 — the downstream twin of the `emit` produce seam), and this
|
|
1306
|
+
* module only DECODES each payload onto the `RealtimeBinding.turns` event
|
|
1307
|
+
* shape the realtime adapter already consumes.
|
|
1308
|
+
*
|
|
1309
|
+
* `confidence` in the wire payload is SEMANTIC END-OF-TURN confidence (the
|
|
1310
|
+
* engine's own VAD verdict that the user finished speaking), NOT
|
|
1311
|
+
* transcription confidence — carrying it onto the GA surface would
|
|
1312
|
+
* misrepresent it, so the decoder DROPS it by design (the adapter reads the
|
|
1313
|
+
* boundary from `turn_end`, which is the same verdict as an event).
|
|
1314
|
+
*/
|
|
1315
|
+
|
|
1316
|
+
/** The turn kinds the transcribe bridge emits (TranscriptEvent kinds). */
|
|
1317
|
+
type SessionTurnKind = 'delta' | 'final' | 'turn_start' | 'turn_end' | 'turn_eager_end' | 'turn_resumed';
|
|
1318
|
+
/**
|
|
1319
|
+
* One decoded transcript-lane event — the `RealtimeBinding.turns` event
|
|
1320
|
+
* shape. `text` is the wire `delta` field (a transcript PIECE — the adapter
|
|
1321
|
+
* accumulates), `startMs` the additive `start_ms` clock. The wire
|
|
1322
|
+
* `confidence` (semantic end-of-turn confidence) is deliberately absent.
|
|
1323
|
+
*/
|
|
1324
|
+
interface SessionTurnLaneEvent {
|
|
1325
|
+
kind: SessionTurnKind;
|
|
1326
|
+
text?: string;
|
|
1327
|
+
startMs?: number;
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1271
1330
|
/** One ready replica row from the presence read. */
|
|
1272
1331
|
interface RuntimePresenceRow {
|
|
1273
1332
|
runtime_id: string;
|
|
@@ -2106,9 +2165,10 @@ interface ProxyClients {
|
|
|
2106
2165
|
/** `listModels(...)` result (an OpenAI model list object). */
|
|
2107
2166
|
listModels(): Promise<unknown>;
|
|
2108
2167
|
/**
|
|
2109
|
-
* The OpenRouter provider document (`GET /
|
|
2168
|
+
* The OpenRouter provider document (`GET /partners/openrouter/models`; the
|
|
2169
|
+
* old `GET /v1/models?format=openrouter` spelling 308-redirects there).
|
|
2110
2170
|
* Optional at the seam: hand-built test clients may omit it, in which case
|
|
2111
|
-
*
|
|
2171
|
+
* that route answers 501 — loud, never a silent empty.
|
|
2112
2172
|
*/
|
|
2113
2173
|
openRouterModels?(): Promise<unknown>;
|
|
2114
2174
|
/**
|
|
@@ -2286,6 +2346,46 @@ interface ProxyClients {
|
|
|
2286
2346
|
* queued admission going back only at the LAST waiter's abort.
|
|
2287
2347
|
*/
|
|
2288
2348
|
openAudioOn?(handle: string, signal?: AbortSignal): Promise<PinnedAudioLane>;
|
|
2349
|
+
/**
|
|
2350
|
+
* THE TURN/TRANSCRIPT LANE OPEN (the realtime transcription intent,
|
|
2351
|
+
* ENG-461): the STT engine's OWN transcript events — the §5 `stt` named
|
|
2352
|
+
* data stream (urun.serve.transcribe_bridge's wire shape: `t` =
|
|
2353
|
+
* delta/final/turn_start/turn_end/turn_eager_end/turn_resumed, `delta`
|
|
2354
|
+
* text pieces, additive `start_ms`), consumed off the EXACT pooled session
|
|
2355
|
+
* the handle names (ModelRouter.sessionForHandle — a gone or replaced
|
|
2356
|
+
* session throws {@link SessionGoneError} LOUDLY, never a fresh session
|
|
2357
|
+
* opened behind the handle's back) and decoded onto the
|
|
2358
|
+
* `RealtimeBinding.turns` event shape (the wire's semantic-VAD
|
|
2359
|
+
* `confidence` is end-of-turn confidence, NOT transcription confidence —
|
|
2360
|
+
* the lane deliberately does not carry it). One lane per call; the
|
|
2361
|
+
* realtime binding calls it once per connection. The returned iterable is
|
|
2362
|
+
* lazy. `signal` is the CONSUMER's own lifecycle (the realtime
|
|
2363
|
+
* connection's turn-lane controller, aborted at its teardown): the
|
|
2364
|
+
* transport races every `messages()` pull against it and ends the stream
|
|
2365
|
+
* subscription on every exit path, so the lane never outlives its
|
|
2366
|
+
* consumer — a quiet engine cannot pin it open behind a never-settling
|
|
2367
|
+
* pull. Optional at the seam exactly like
|
|
2368
|
+
* {@link openAudioOn}: the binding's stt verdict answers its absence with
|
|
2369
|
+
* the loud 501 — never a transcription-capable binding with no transcript
|
|
2370
|
+
* source. An already-aborted signal rejects on the first pull — never
|
|
2371
|
+
* opens a lane for a vanished consumer.
|
|
2372
|
+
*/
|
|
2373
|
+
openTurnsOn?(handle: string, signal?: AbortSignal): AsyncIterable<SessionTurnLaneEvent>;
|
|
2374
|
+
/**
|
|
2375
|
+
* THE REALTIME TRANSCRIPTION LANE'S STREAM-NAME RESOLUTION (openai-realtime
|
|
2376
|
+
* binding.ts): the §5 transcript stream the model's catalog rows vow
|
|
2377
|
+
* (ModelRouter.transcriptStreamFor — `engine_args.transcript_stream`,
|
|
2378
|
+
* defaulting to the platform's `stt`). The lane's consume side reads the
|
|
2379
|
+
* platform DEFAULT stream name only, so the binding answers a row that
|
|
2380
|
+
* OVERRIDES the default with the loud 501 naming the field — never a
|
|
2381
|
+
* silent listen on a stream the engine does not write, and never a
|
|
2382
|
+
* transcript-less transcription session. Optional at the seam exactly
|
|
2383
|
+
* like {@link supportsTranscription}: hand-built clients own their own
|
|
2384
|
+
* lane (their `openTurnsOn` decides what to read — no catalog row exists
|
|
2385
|
+
* to check), so its absence skips the catalog check; every router-backed
|
|
2386
|
+
* backhaul wires it.
|
|
2387
|
+
*/
|
|
2388
|
+
transcriptStreamFor?(model: string | undefined): Promise<string>;
|
|
2289
2389
|
/**
|
|
2290
2390
|
* Open (or reuse) the NATIVE video FRAME lane on the pooled session `model`
|
|
2291
2391
|
* routes to (transport/media.ts `enableSessionVideo`: discrete JPEG frames →
|
|
@@ -178,27 +178,19 @@ declare class SessionGoneError extends Error {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
interface RouterModelEntry {
|
|
181
|
-
id: string;
|
|
182
|
-
object: 'model';
|
|
183
|
-
created: number;
|
|
184
|
-
owned_by: string;
|
|
185
181
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
description?: string;
|
|
182
|
+
id: string;
|
|
189
183
|
|
|
190
|
-
|
|
184
|
+
object: 'model';
|
|
191
185
|
|
|
192
|
-
|
|
186
|
+
created: number;
|
|
193
187
|
|
|
194
|
-
|
|
188
|
+
owned_by: string;
|
|
195
189
|
|
|
196
190
|
context_length?: number;
|
|
197
191
|
|
|
198
192
|
pricing?: TokenPricing;
|
|
199
193
|
|
|
200
|
-
warm?: number;
|
|
201
|
-
|
|
202
194
|
voices?: string[];
|
|
203
195
|
|
|
204
196
|
capabilities?: {
|
|
@@ -346,6 +338,8 @@ declare class ModelRouter<S> {
|
|
|
346
338
|
|
|
347
339
|
supportsTranscription(model: string | undefined): Promise<boolean>;
|
|
348
340
|
|
|
341
|
+
transcriptStreamFor(model: string | undefined): Promise<string>;
|
|
342
|
+
|
|
349
343
|
speechVoices(model: string | undefined): Promise<SpeechVoicesResolution>;
|
|
350
344
|
|
|
351
345
|
imageModelList(): Promise<RouterImageModelList>;
|
|
@@ -355,6 +349,14 @@ declare class ModelRouter<S> {
|
|
|
355
349
|
closeAll(): Promise<void>;
|
|
356
350
|
}
|
|
357
351
|
|
|
352
|
+
type SessionTurnKind = 'delta' | 'final' | 'turn_start' | 'turn_end' | 'turn_eager_end' | 'turn_resumed';
|
|
353
|
+
|
|
354
|
+
interface SessionTurnLaneEvent {
|
|
355
|
+
kind: SessionTurnKind;
|
|
356
|
+
text?: string;
|
|
357
|
+
startMs?: number;
|
|
358
|
+
}
|
|
359
|
+
|
|
358
360
|
interface RuntimePresenceRow {
|
|
359
361
|
runtime_id: string;
|
|
360
362
|
|
|
@@ -615,6 +617,10 @@ interface ProxyClients {
|
|
|
615
617
|
|
|
616
618
|
openAudioOn?(handle: string, signal?: AbortSignal): Promise<PinnedAudioLane>;
|
|
617
619
|
|
|
620
|
+
openTurnsOn?(handle: string, signal?: AbortSignal): AsyncIterable<SessionTurnLaneEvent>;
|
|
621
|
+
|
|
622
|
+
transcriptStreamFor?(model: string | undefined): Promise<string>;
|
|
623
|
+
|
|
618
624
|
openVideo?(model: string | undefined): Promise<ProxyVideoLane>;
|
|
619
625
|
|
|
620
626
|
openVideoOut?(model: string | undefined): Promise<ProxyVideoOutLane>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@urun-sh/openai",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.18",
|
|
4
4
|
"description": "OpenAI-compatible Realtime + Responses SDK over uRun session primitives (not websockets).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"build:bin": "bun build --compile --external @roamhq/wrtc src/proxy/bin.ts --outfile dist/bin/urun-openai && node scripts/assert-bin-transport.mjs"
|
|
71
71
|
},
|
|
72
72
|
"peerDependencies": {
|
|
73
|
-
"@urun-sh/core": "^0.6.
|
|
73
|
+
"@urun-sh/core": "^0.6.18"
|
|
74
74
|
},
|
|
75
75
|
"dependencies": {
|
|
76
76
|
"@prometheus-io/client": "^0.16.1",
|
package/dist/chunk-6SN4EJ7Q.js
DELETED
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import{a as j,b as W}from"./chunk-VRCCLQ5P.js";import{C as oe,D as se,E as ie,F as ae,H as le,I as A,J as ce,K as ue,L as de,M as he,N as pe,O as ge,P as fe,Q as me,R as ye,b as ee,d as te,e as ne,q as re,r as O}from"./chunk-U7EJSC2N.js";import{e as X,g as z}from"./chunk-TMQJY56F.js";import{a as Z}from"./chunk-3JWYIKHM.js";import{c as p}from"./chunk-YSFSRI3D.js";p();import{Counter as S,Gauge as we,Histogram as be,Registry as Ue,collectDefaultMetrics as Pe}from"@prometheus-io/client";import{createServer as $e}from"http";var Tt=9464,ve="/metrics",I="(other)",Le="(none)",He=64,Me=128,De=["/v1/responses","/v1/chat/completions","/v1/systemone","/v1/messages","/v1/messages/count_tokens","/v1/models","/v1/images/models","/v1/images/generations","/v1/images/edits","/v1/audio/speech","/v1/audio/models","/v1/audio/transcriptions",le,ue],Fe=new Set(De);function Ke(t){return Fe.has(t)?t:I}function Ge(t){return t===null?"none":t>=500?"5xx":t>=400?"4xx":t>=300?"3xx":t>=200?"2xx":"1xx"}var T=class{constructor(e){this.max=e}max;admitted=new Set;overflows=0;of(e,n=!0){return e===null||e===""?Le:this.admitted.has(e)?e:n?this.admitted.size>=this.max?(this.overflows+=1,I):(this.admitted.add(e),e):I}get size(){return this.admitted.size}get overflowCount(){return this.overflows}},N=class{registry=new Ue;modelLabel=new T(He);orgLabel=new T(Me);requests;duration;ttft;streamErrors;undelivered;ledgerWrites;ledgerQueue;labelCardinality;labelOverflows;constructor(e={}){this.requests=new S({name:"urun_inference_requests_total",help:"Completed /v1 requests, by lane, method, HTTP status class, streaming, model and org.",labelNames:["lane","method","status_class","stream","model","org"],registers:[this.registry]}),this.duration=new be({name:"urun_inference_request_duration_seconds",help:"Wall time from request receipt to response close. For a stream this is the WHOLE generation, not the time to the head.",labelNames:["lane","stream","model","org"],buckets:[.1,.25,.5,1,2.5,5,10,30,60,120,300,600],registers:[this.registry]}),this.ttft=new be({name:"urun_inference_ttft_seconds",help:"Time to first output-text delta on a streamed response. The metric Hugging Face gates provider listing on (<5s for conversational models).",labelNames:["lane","model","org"],buckets:[.1,.25,.5,1,2,3,5,8,15,30,60],registers:[this.registry]}),this.streamErrors=new S({name:"urun_inference_stream_errors_total",help:"Responses that emitted an error frame AFTER the head flushed. These are HTTP 200 with a complete body, so no status-based metric can see them.",labelNames:["lane","model"],registers:[this.registry]}),this.undelivered=new S({name:"urun_inference_undelivered_total",help:"Responses whose socket did not finish flushing \u2014 the client vanished mid-answer.",labelNames:["lane","model"],registers:[this.registry]}),this.ledgerWrites=new S({name:"urun_inference_ledger_writes_total",help:"Terminal fate of the per-request ledger row for each billable /v1 request. outcome=written is the row landing (reason=priced|unpriced); outcome=skipped and outcome=failed are BOTH revenue that went unrecorded. failed/duplicate is the ENG-410 fabricated-row signal.",labelNames:["outcome","reason"],registers:[this.registry]});for(let n of de)this.ledgerWrites.inc({outcome:n.outcome,reason:n.reason},0);this.ledgerQueue=new we({name:"urun_inference_ledger_queue",help:'The ledger write queue right now. measure="depth" is rows waiting for a worker; measure="oldest_age_seconds" is how long the head has waited \u2014 approaching the dwell bound means rows are about to be given up on, which is revenue lost.',labelNames:["measure"],registers:[this.registry],collect:()=>{this.ledgerQueue.set({measure:"depth"},he()),this.ledgerQueue.set({measure:"oldest_age_seconds"},pe()/1e3)}}),this.labelCardinality=new we({name:"urun_inference_label_cardinality",help:"Distinct values admitted for a bounded metric label. At its ceiling, further values report as (other).",labelNames:["label"],registers:[this.registry],collect:()=>{this.labelCardinality.set({label:"model"},this.modelLabel.size),this.labelCardinality.set({label:"org"},this.orgLabel.size)}}),this.labelOverflows=new S({name:"urun_inference_label_overflows_total",help:"Observations that would have taken a new label value but found the budget spent. Non-zero means a metric dimension is now partly blind.",labelNames:["label"],registers:[this.registry]}),e.defaultMetrics!==!1&&Pe({register:this.registry,prefix:"urun_inference_proxy_"})}observeLedger(e){this.ledgerWrites.inc({outcome:e.outcome,reason:e.reason})}observe(e){let n=Ke(e.path),o=e.status!==null&&!(e.status>=400&&e.status<500),r=this.modelLabel.overflowCount+this.orgLabel.overflowCount,a=this.modelLabel.of(e.model,o),s=this.orgLabel.of(e.org,o);this.modelLabel.overflowCount+this.orgLabel.overflowCount-r>0&&(this.modelLabel.overflowCount>r?this.labelOverflows.inc({label:"model"}):this.labelOverflows.inc({label:"org"}));let l=e.stream===!0?"true":"false";this.requests.inc({lane:n,method:e.method===""?I:e.method,status_class:Ge(e.status),stream:l,model:a,org:s}),this.duration.observe({lane:n,stream:l,model:a,org:s},e.duration_ms/1e3),e.ttft_ms!==null&&this.ttft.observe({lane:n,model:a,org:s},e.ttft_ms/1e3),e.stream_error&&this.streamErrors.inc({lane:n,model:a}),e.delivered||this.undelivered.inc({lane:n,model:a})}scrape(){return this.registry.metrics()}get contentType(){return this.registry.contentType}};function _e(t){return $e((e,n)=>{(async()=>{let o=(e.url??"/").split("?")[0];if(e.method!=="GET"||o!==ve){n.writeHead(404,{"content-type":"text/plain"}),n.end(`no route for ${e.method} ${o} \u2014 this port serves only GET ${ve}
|
|
2
|
-
`);return}let r=await t.scrape();n.writeHead(200,{"content-type":t.contentType}),n.end(r)})().catch(o=>{if(!n.headersSent){n.writeHead(500,{"content-type":"text/plain"}),n.end(`${String(o instanceof Error?o.message:o)}
|
|
3
|
-
`);return}n.end()})})}p();import{createHash as Se}from"crypto";import{createServer as Be}from"http";var Lt=9465,ze="x-urun-key-id",je="x-urun-key-fingerprint",We="urun-ratelimit-bucket:",Je="urun-ratelimit-tier:",Ee="anonymous",qe=/^urun_sk_[A-Za-z0-9_-]{43}$/;function Ve(t){return Se("sha256").update(`${We}${t}`).digest("hex")}function Ye(t){return Se("sha256").update(`${Je}${t}`).digest("hex")}function Qe(t){let e=/^Bearer[ \t]+(\S.*)$/i.exec((t??"").trim());if(!e)return null;let n=e[1].trim();return qe.test(n)?n:null}function Re(){return Be((t,e)=>{let n=Qe(t.headers.authorization);e.setHeader(ze,n===null?Ee:Ve(n)),e.setHeader(je,n===null?Ee:Ye(n)),e.writeHead(200),e.end()})}p();import{createClientToken as Xe,ClientTokenError as Ze}from"@urun-sh/core";var m=class extends Error{constructor(n,o=401){super(n);this.status=o;this.name="ProxyAuthError"}status},_=class extends Error{constructor(e){super(e),this.name="ControlPlaneUnavailableError"}};function R(t){let e=(t??"").trim();if(e==="")throw new m("missing Authorization header \u2014 send your uRun org API key as `Authorization: Bearer <key>`");let n=/^Bearer[ \t]+(\S.*)$/i.exec(e);if(!n)throw new m("malformed Authorization header \u2014 expected `Authorization: Bearer <urun org api key>`");return n[1].trim()}var et=60,tt=6e4,nt=4096;function rt(t){return t===401||t===403}var U=class{constructor(e){this.opts=e;this.mint=e.mint??Xe,this.now=e.now??Date.now}opts;cache=new Map;mint;now;async resolve(e){if(e==="")throw new m("empty Authorization bearer token");let n=this.cache.get(e);if(n&&this.now()-n.at<tt)return{apiKey:e,orgId:n.orgId};let o;try{o=(await this.mint(e,{baseUrl:this.opts.gatewayUrl,expiresIn:et})).orgId}catch(r){throw r instanceof Ze&&rt(r.status)?(this.cache.delete(e),new m("the uRun org API key was rejected by the control plane \u2014 check the key and that it is not revoked")):new _(`could not verify the API key against the uRun control plane at ${this.opts.gatewayUrl}: ${r instanceof Error?r.message:String(r)}`)}if(!o)throw new _("the control plane verified the API key but returned no org_id \u2014 refusing to route a request with no tenancy (control-plane contract violation)");if(this.cache.size>=nt){let r=this.cache.keys().next();r.done||this.cache.delete(r.value)}return this.cache.set(e,{orgId:o,at:this.now()}),{apiKey:e,orgId:o}}clear(){this.cache.clear()}};p();var ot=8080,P="serve",Bt="0.0.0.0";function st(t,e,n){let o=(t[e]??"").trim();if(!o)throw new Error(`${e} is not set \u2014 ${n}`);return o}function zt(t=process.env){let e=(t.PORT??"").trim(),n=e===""?ot:Number(e);if(!Number.isInteger(n)||n<0||n>65535)throw new Error(`PORT must be an integer in 0..65535, got ${JSON.stringify(e)}`);let o=re(O(st(t,"URUN_BASE_URL","the hosted proxy needs the uRun session-gateway base it opens sessions and verifies API keys against (e.g. https://api.urun.sh)"),"URUN_BASE_URL")),r=(t.URUN_API_URL??"").trim(),a=r?O(r,"URUN_API_URL"):`${o.replace(/\/+$/,"")}/v1`,s=(t.URUN_CATALOG_URL??"").trim(),i=(t.URUN_CATALOG_ANON_KEY??"").trim();if(!!s!=!!i)throw new Error("URUN_CATALOG_URL and URUN_CATALOG_ANON_KEY must be set together (or both left unset) \u2014 exactly one of them is set, which would silently disable the model-catalog oracle");let l=s?{catalogUrl:O(s,"URUN_CATALOG_URL"),anonKey:i}:null,u=(t.URUN_SESSION_TOKEN_SECRET??"").trim(),f=(t.URUN_SHARED_ORG_API_KEY??"").trim(),x=(t.URUN_SHARED_ORG_ID??"").trim();if(!!f!=!!x)throw new Error("URUN_SHARED_ORG_API_KEY and URUN_SHARED_ORG_ID must be set together (or both left unset) \u2014 exactly one of them is set, which would silently leave the shared endpoints on the session lane");return{port:n,baseUrl:o,apiUrl:a,catalog:l,decisionSecret:u,sharedOrgKey:f,sharedOrgId:x}}p();import{createHash as it,randomUUID as at}from"crypto";var lt=512;function M(t){return`urun-inference-proxy-${it("sha256").update(t).digest("hex").slice(0,32)}`}var $=class{constructor(e){this.opts=e}opts;tenants=new Map;label(e,n){return Object.assign(Object.create(e),{tenant:n.orgId})}build(e){if(this.opts.build){let i=this.opts.build(e);return{router:i.router,clients:this.label(i.clients,e)}}let n=0,o=M(e.apiKey),r=()=>{n+=1},a=j({baseUrl:this.opts.baseUrl,orgId:e.orgId,defaultApp:null,fnName:P,sharedOnlyListing:!0,apiUrl:this.opts.apiUrl,auth:{lane:"api-key",apiKey:e.apiKey,gatewayUrl:this.opts.baseUrl},catalog:this.opts.catalog,subject:()=>n===0?o:`${o}#${n}`,onPoolWedged:r,release:"end"}),s=this.opts.decisionIntake?new me({targetFor:i=>a.targetFor(i),apiUrl:this.opts.apiUrl,apiKey:e.apiKey,orgId:e.orgId,fnName:P,secret:this.opts.decisionIntake.secret,...this.opts.decisionIntake.shared?{shared:this.opts.decisionIntake.shared}:{}}):void 0;return{router:a,clients:this.label(W(a,void 0,r,s),e)}}clientsFor(e){let n=this.tenants.get(e.apiKey);if(n)return this.tenants.delete(e.apiKey),this.tenants.set(e.apiKey,n),n.clients;for(;this.tenants.size>=lt;){let r=this.tenants.entries().next();if(r.done)break;let[a,s]=r.value;this.tenants.delete(a),s.router.closeAll().catch(()=>{})}let o=this.build(e);return this.tenants.set(e.apiKey,o),o.clients}get size(){return this.tenants.size}async closeAll(){let e=[...this.tenants.values()].map(o=>o.router);this.tenants.clear();let n=[];if(await Promise.all(e.map(async o=>{try{await o.closeAll()}catch(r){n.push(String(r instanceof Error?r.message:r))}})),n.length>0)throw new Error(`failed to close ${n.length} tenant backhaul(s): ${n.join("; ")}`)}},xe=512,ct=1024,L=class extends Error{constructor(){super(`conversation capacity reached (${xe} conversations on this replica) \u2014 every slot holds an active or resumable conversation; retry later or scale out`),this.name="ConversationCapacityError"}};function ut(t){return{sessionStartOptions:e=>{if(e.startsWith(z))return;let n=t.get(e);return n===void 0?void 0:{sessionId:n}},onSessionOpen:(e,n)=>{e.startsWith(z)||t.set(e,n)}}}function dt(t,e,n,o){let r=j({baseUrl:t.baseUrl,orgId:e.orgId,defaultApp:null,fnName:P,sharedOnlyListing:!0,apiUrl:t.apiUrl,auth:{lane:"api-key",apiKey:e.apiKey,gatewayUrl:t.baseUrl},catalog:t.catalog,subject:n.subject,release:"detach",...ut(o)});return{clients:W(r),release:async()=>{await r.closeAll()}}}var H=class{constructor(e){this.opts=e}opts;byCaller=new Map;byHandle=new Map;build(e,n){let o={id:n,subject:`${M(e.apiKey)}:live:${n}`},r=new Map,a=this.opts.build?this.opts.build(e,o):dt(this.opts,e,o,r);return{id:n,backhaul:a,attachments:0,nativeRefs:r}}attachEntry(e,n){let o=this.byCaller.get(e.apiKey);o===void 0&&(o=new Map,this.byCaller.set(e.apiKey,o));let r=o.get(n);if(r!==void 0)return o.delete(n),o.set(n,r),r.attachments+=1,r;this.evictOverflow();let a=this.build(e,n);return a.attachments=1,o.set(n,a),a}get conversationCount(){let e=0;for(let n of this.byCaller.values())e+=n.size;return e}evictOverflow(){for(;this.conversationCount>=xe;){let e=this.byCaller.keys().next();if(e.done)throw new L;let n=e.value,o=this.byCaller.get(n),r=!1;for(let[a,s]of o)if(!(s.attachments>0)){o.delete(a),this.dropHandleIndexFor(n,a),s.backhaul.release().catch(()=>{}),r=!0;break}if(!r)throw o.size===0&&this.byCaller.delete(n),new L;o.size===0&&this.byCaller.delete(n)}}dropHandleIndexFor(e,n){for(let[o,r]of this.byHandle)r.apiKey===e&&r.conversationId===n&&this.byHandle.delete(o)}connectionFor(e){let n=null,o=!1,r=()=>(n===null&&(n=this.attachEntry(e,at())),n),a={createResponse:(s,i)=>r().backhaul.clients.createResponse(s,i),catalogIdentity:s=>r().backhaul.clients.catalogIdentity(s),servingAppSlug:s=>{let i=r().backhaul.clients.servingAppSlug;if(i===void 0)throw new Error("the conversation backhaul wires no servingAppSlug seam \u2014 the caller-org app a request resolves to cannot be known");return i(s)},listModels:()=>r().backhaul.clients.listModels(),sessionHandle:s=>r().backhaul.clients.sessionHandle(s),createResponseOn:(s,i)=>r().backhaul.clients.createResponseOn(s,i),onSessionEnd:(s,i)=>r().backhaul.clients.onSessionEnd(s,i)};return a.openAudio=async(s,i)=>{let l=r().backhaul.clients.openAudio;if(l===void 0)throw new Error("the conversation backhaul wires no openAudio seam \u2014 voice is unavailable on this deployment");return l(s,i)},a.openVideo=async s=>{let i=r().backhaul.clients.openVideo;if(i===void 0)throw new Error("the conversation backhaul wires no openVideo seam \u2014 video-in is unavailable on this deployment");return i(s)},a.openVideoOut=async s=>{let i=r().backhaul.clients.openVideoOut;if(i===void 0)throw new Error("the conversation backhaul wires no openVideoOut seam \u2014 video-out is unavailable on this deployment");return i(s)},a.supportsAudio=async s=>{let i=r().backhaul.clients.supportsAudio;if(i===void 0)throw new Error("the conversation backhaul wires no supportsAudio seam \u2014 the realtime lane cannot gate audio modality on this deployment");return i(s)},a.supportsSpeech=async s=>{let i=r().backhaul.clients.supportsSpeech;if(i===void 0)throw new Error("the conversation backhaul wires no supportsSpeech seam \u2014 the speech lane cannot gate model capability on this deployment");return i(s)},a.speechVoices=async s=>{let i=r().backhaul.clients.speechVoices;if(i===void 0)throw new Error("the conversation backhaul wires no speechVoices seam \u2014 the speech lane cannot resolve the voice set /v1/models publishes on this deployment");return i(s)},a.speechSessionHandle=async s=>{let i=r().backhaul.clients.speechSessionHandle;if(i===void 0)throw new Error("the conversation backhaul wires no speechSessionHandle seam \u2014 the speech lane cannot resolve its own pooled session on this deployment");return i(s)},a.openAudioOn=async(s,i)=>{let l=r().backhaul.clients.openAudioOn;if(l===void 0)throw new Error("the conversation backhaul wires no openAudioOn seam \u2014 the speech and realtime lanes cannot open their pinned native session audio on this deployment");return l(s,i)},a.evictSpeechSession=async s=>{let i=r().backhaul.clients.evictSpeechSession;if(i===void 0)throw new Error("the conversation backhaul wires no evictSpeechSession seam \u2014 the speech lane cannot retire its pinned speech session after a failed turn on this deployment");return i(s)},a.supportsTranscription=async s=>{let i=r().backhaul.clients.supportsTranscription;if(i===void 0)throw new Error("the conversation backhaul wires no supportsTranscription seam \u2014 the transcription lane cannot gate model capability on this deployment");return i(s)},a.listSpeechToTextModels=async()=>{let s=r().backhaul.clients.listSpeechToTextModels;if(s===void 0)throw new Error("the conversation backhaul wires no listSpeechToTextModels seam \u2014 the transcription lane cannot list the stt models on this deployment");return s()},{clients:a,bindResumeHandle:s=>{let i=n;if(i===null)throw new Error("bindResumeHandle before the conversation opened \u2014 no conversation to bind to");for(;this.byHandle.size>=ct;){let l=this.byHandle.keys().next();if(l.done)break;this.byHandle.delete(l.value)}this.byHandle.set(s,{apiKey:e.apiKey,conversationId:i.id})},clientsForResume:async s=>{let i=this.byHandle.get(s),l=i===void 0?void 0:this.byCaller.get(i.apiKey),u=i===void 0||l===void 0?void 0:l.get(i.conversationId);if(i===void 0||u===void 0||i.apiKey!==e.apiKey)throw new X("the conversation behind this resume handle is gone (evicted at capacity, or the replica restarted) or the handle is not yours \u2014 reconnect without a handle to start a new conversation");return n=u,u.attachments+=1,u.backhaul.clients},release:async()=>{if(o)return;o=!0;let s=n;n=null,s!==null&&(s.attachments-=1,!(s.attachments>0)&&await s.backhaul.release().catch(()=>{}))}}}async detachAll(){let e=[];for(let o of this.byCaller.values())for(let r of o.values())e.push(r.backhaul.release());this.byCaller.clear(),this.byHandle.clear();let n=[];if(await Promise.all(e.map(async o=>{try{await o}catch(r){n.push(String(r instanceof Error?r.message:r))}})),n.length>0)throw new Error(`failed to detach ${n.length} conversation backhaul(s): ${n.join("; ")}`)}};p();var ht="/inference-ledger",pt=1e4,D=class extends Error{ledgerFailureReason;constructor(e,n="duplicate"){super(e),this.name="LedgerDuplicateError",this.ledgerFailureReason=n}},V=class extends Error{constructor(n,o){super(o);this.ledgerAlreadyWritten=n;this.name="LedgerAlreadyWrittenError"}ledgerAlreadyWritten},g=class extends Error{ledgerFailureReason="surface";constructor(e){super(e),this.name="LedgerSurfaceError"}},Y=class extends m{ledgerFailureReason="auth";constructor(e){super(e),this.name="LedgerAuthError"}},gt=[["inference_id",t=>typeof t=="string"&&/[^\s]/.test(t)],["model_id",t=>typeof t=="string"&&/[^\s]/.test(t)],["variant",t=>t===null||typeof t=="string"],["task",t=>typeof t=="string"&&/[^\s]/.test(t)],["http_status",t=>typeof t=="number"&&Number.isInteger(t)],["started_at",t=>typeof t=="string"&&!Number.isNaN(Date.parse(t))],["completed_at",t=>typeof t=="string"&&!Number.isNaN(Date.parse(t))],["prompt_tokens",t=>t===null||typeof t=="number"&&Number.isInteger(t)],["completion_tokens",t=>t===null||typeof t=="number"&&Number.isInteger(t)],["output_units",J],["output_minutes",J],["cost_nano_usd",J],["price_version",t=>t===null||typeof t=="string"]];function ft(t){for(let[e,n]of gt){if(!(e in t))return`${e} (absent)`;if(!n(t[e]))return`${e} (${JSON.stringify(t[e])})`}return null}function mt(t){let e;try{e=JSON.parse(t)}catch{return{status:"unavailable"}}if(e===null||typeof e!="object"||Array.isArray(e))return{status:"unavailable"};let n=e.existing_status;if(n==="not_in_this_org")return{status:"not_in_this_org"};if(n!=="found")return{status:"unavailable"};let o=e.existing;return o===null||typeof o!="object"||Array.isArray(o)?{status:"unavailable"}:ft(o)!==null?{status:"unavailable"}:{status:"found",row:o}}function J(t){return t===null?!0:typeof t=="number"?Number.isFinite(t):typeof t!="string"?!1:/[^\s]/.test(t)&&Number.isFinite(Number(t))}function q(t,e){return t===null?e===null:e===null?!1:Number(e)===t}function yt(t,e){return t===null?e===null:e===null?!1:Number(t.toFixed(9))===Number(e)}function Ce(t,e){if(typeof e!="string")return!1;let n=Date.parse(t),o=Date.parse(e);return Number.isFinite(n)&&Number.isFinite(o)&&n===o}function wt(t,e){let n=e;return n.inference_id===t.inference_id&&n.model_id===t.model_id&&(n.variant??null)===t.variant&&n.task===t.task&&n.http_status===t.http_status&&Ce(t.started_at,n.started_at)&&Ce(t.completed_at,n.completed_at)&&q(t.prompt_tokens,n.prompt_tokens)&&q(t.completion_tokens,n.completion_tokens)&&q(t.output_units,n.output_units)&&yt(t.output_minutes,n.output_minutes)}function bt(t,e){if(t===null||typeof t!="object"||Array.isArray(t))throw new g(`${e} answered a "recorded" that is not an object: ${JSON.stringify(t)}`);let n=t,o=n.inference_id;if(typeof o!="string"||o==="")throw new g(`${e} answered a row with no inference_id: ${JSON.stringify(t)}`);if(!("cost_nano_usd"in n))throw new g(`${e} answered a row with no cost_nano_usd field: ${JSON.stringify(t)}`);let r=n.cost_nano_usd;if(r!=null&&!(typeof r=="number"&&Number.isInteger(r)&&r>=0))throw new g(`${e} answered cost_nano_usd ${JSON.stringify(r)}, which is neither a non-negative integer nor null`);let a=u=>{if(!(u in n))throw new g(`${e} answered a row with no ${u} field: ${JSON.stringify(t)}`);let f=n[u];if(f===null)return null;if(typeof f!="string")throw new g(`${e} answered a non-string ${u}: ${JSON.stringify(f)}`);if(!/[^\s]/.test(f))throw new g(`${e} answered a BLANK ${u} (${JSON.stringify(f)}) \u2014 it satisfies "is a string" while naming nothing, which is exactly what inference_requests_priced_attribution_check exists to forbid`);return f},s=a("price_version"),i=a("unpriced_reason"),l=r!=null;if(l&&(s===null||i!==null))throw new g(`${e} answered a PRICED row (cost_nano_usd ${String(r)}) that is not cleanly attributed \u2014 price_version ${JSON.stringify(s)}, unpriced_reason ${JSON.stringify(i)}`);if(!l&&(s!==null||i===null))throw new g(`${e} answered an UNPRICED row that does not say why \u2014 price_version ${JSON.stringify(s)}, unpriced_reason ${JSON.stringify(i)}. NULL means NOT YET PRICED, and the reason is how an operator learns that traffic is being served which nobody can bill.`);return{inference_id:o,cost_nano_usd:r===void 0?null:r,price_version:s,unpriced_reason:i}}var F=class{constructor(e){this.opts=e;this.fetchImpl=e.fetchImpl??fetch}opts;fetchImpl;async write(e,n){let o=`${this.opts.apiUrl.replace(/\/+$/,"")}${ht}`,r;try{r=await this.fetchImpl(o,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify(n),signal:AbortSignal.timeout(pt)})}catch(l){throw new g(`the uRun ledger surface at ${o} could not be reached: ${l instanceof Error?l.message:String(l)}`)}if(r.status===409){let l=await r.text().catch(()=>""),u=mt(l);throw u.status==="found"&&wt(n,u.row)?new V(u.row,`the uRun control plane already holds THIS PROXY'S OWN row for inference_id ${n.inference_id} \u2014 every billable field matches the row being re-sent, so an earlier attempt landed and this retry is a no-op. Nothing was written and nothing was overwritten.`):u.status==="not_in_this_org"?new D(`inference_id ${n.inference_id} is ALREADY TAKEN by a ledger row this org cannot read. The id space is a global primary key, so this is not a retry and cannot be this proxy's own write \u2014 either two orgs collided on a uuid, or a row was written for this id by somebody else.`,"duplicate_foreign"):new D(`the uRun control plane already holds a ledger row for inference_id ${n.inference_id} that is NOT the one being written${u.status==="unavailable"?" (and it could not be read back to compare)":""} \u2014 nothing was written and nothing was overwritten. Either this proxy wrote twice, or a row was fabricated ahead of it (this write is authenticated by the CALLER'S OWN org key). ${l.slice(0,400)}`)}if(r.status===401||r.status===403)throw await r.text().catch(()=>{}),new Y("the uRun org API key was rejected by the uRun control plane when writing the inference ledger \u2014 the request was served but produced no billable row");if(!r.ok){let l=await r.text().catch(u=>`<body unreadable: ${u instanceof Error?u.message:String(u)}>`);throw new g(`the uRun ledger surface at ${o} answered HTTP ${r.status}: ${l.slice(0,400)}`)}let a;try{a=await r.json()}catch(l){throw new g(`the uRun ledger surface at ${o} answered a body that is not JSON: ${l instanceof Error?l.message:String(l)}`)}let s=a?.recorded;if(s===void 0)throw new g(`the uRun ledger surface at ${o} answered a body with no "recorded" field`);let i=bt(s,`the uRun ledger surface at ${o}`);if(i.inference_id!==n.inference_id)throw new g(`the uRun ledger surface at ${o} answered for inference_id ${i.inference_id} when asked about ${n.inference_id}`);return i}};p();import{createServer as _t}from"http";p();var K="/inference-usage",ke=25e3;var vt=new Set(["Error","TypeError","SyntaxError","TimeoutError"]);function Oe(t){if(!(t instanceof Error))return`a ${typeof t}`;let e=vt.has(t.name)?t.name:"Error",n=t.cause?.code;return typeof n=="string"&&/^[A-Z][A-Z0-9_]{0,31}$/.test(n)?`${e} (${n})`:e}var G=class{constructor(e){this.opts=e;this.fetchImpl=e.fetchImpl??fetch}opts;fetchImpl;async lookup(e,n){let o=`${this.opts.apiUrl.replace(/\/+$/,"")}${K}`,r;try{r=await this.fetchImpl(o,{method:"POST",headers:{"content-type":"application/json",authorization:`Bearer ${e}`},body:JSON.stringify({inference_ids:n}),signal:AbortSignal.timeout(ke)})}catch(s){throw new A(`the uRun usage surface (POST ${K}) could not be reached within ${ke}ms: ${Oe(s)}`)}if(r.status===401||r.status===403)throw await r.text().catch(()=>{}),new m("the uRun org API key was rejected by the uRun control plane \u2014 check the key and that it is not revoked");if(!r.ok){let s=await r.text().catch(()=>null),i=s===null?"body unreadable":`${s.length}-character body`;throw new A(`the uRun usage surface (POST ${K}) answered HTTP ${r.status} (${i})`)}let a;try{a=await r.json()}catch(s){throw new A(`the uRun usage surface (POST ${K}) answered HTTP ${r.status} with a body that is not JSON: ${Oe(s)}`)}return ce(a,this.opts.onRejectedRow)}};var Et=5e3,St=3e3,Rt="/api/client-tokens";function Q(t,e,n){t.writeHead(e,{"content-type":"application/json"}),t.end(JSON.stringify(n))}function Ae(t,e,n,o){Q(t,e,{error:{message:n,type:o}})}async function xt(t,e=fetch){let n=`${t.replace(/\/+$/,"")}${Rt}`;try{let o=await e(n,{method:"POST",headers:{"content-type":"application/json"},body:"{}",signal:AbortSignal.timeout(St)});return o.status===404?{ready:!1,detail:`${n} answered 404 \u2014 this is not a uRun control plane`}:o.status>=500?{ready:!1,detail:`${n} answered HTTP ${o.status}`}:{ready:!0,detail:`${n} answered HTTP ${o.status}`}}catch(o){return{ready:!1,detail:`${n} unreachable: ${o instanceof Error?o.message:String(o)}`}}}function En(t){let e=t.resolver??new U({gatewayUrl:t.baseUrl}),n=t.decisionSecret??"",o=t.sharedOrgKey&&t.sharedOrgId?{apiKey:t.sharedOrgKey}:void 0,r=t.registry??new $({baseUrl:t.baseUrl,apiUrl:t.apiUrl,catalog:t.catalog,...n!==""?{decisionIntake:{secret:n,...o?{shared:o}:{}}}:{}});n!==""&&process.stderr.write(`inference-proxy: stateless direct path ENABLED \u2014 caller-org decisions dial ready runtimes directly (no session mint)
|
|
4
|
-
`);let a=t.probeControlPlane??(()=>xt(t.baseUrl)),s=t.usageClient??new G({apiUrl:t.apiUrl}),i=t.ledgerClient??new F({apiUrl:t.apiUrl}),l=new N(t.defaultMetrics===void 0?{}:{defaultMetrics:t.defaultMetrics}),u=async c=>e.resolve(R(c.headers.authorization)),f=c=>c instanceof m?{status:c.status,openaiType:"authentication_error",anthropicType:"authentication_error"}:c instanceof _?{status:502,openaiType:"upstream_error",anthropicType:"api_error"}:null,x=ye({clients:async c=>r.clientsFor(await u(c)),usage:{authorize:c=>R(c.headers.authorization),lookup:(c,d)=>s.lookup(c,d)},statusOf:f,...t.requestLog?{requestLog:t.requestLog}:{},observe:c=>l.observe(c),ledger:{write:(c,d)=>i.write(R(c.headers.authorization),d),observe:c=>l.observeLedger(c),...t.ledgerLog?{report:t.ledgerLog}:{}}}),v=null,E=_t((c,d)=>{let h=new URL(c.url??"/","http://localhost");(async()=>{if(c.method==="GET"&&h.pathname==="/healthz"){Q(d,200,{ok:!0});return}if(c.method==="GET"&&h.pathname==="/readyz"){let k=Date.now();if(!v||k-v.at>=Et){let Ne=await a();v={at:k,...Ne}}Q(d,v.ready?200:503,{ok:v.ready,control_plane:v.detail});return}let y=h.pathname==="/v1"||h.pathname.startsWith("/v1/"),C=h.pathname.startsWith("/partners/");if(!y&&!C){Ae(d,404,`no route for ${c.method} ${h.pathname} \u2014 this endpoint serves the OpenAI-compatible /v1 surface, the partner adapter surface (/partners/...), the WS upgrade paths (/ws/google.ai.generativelanguage.v1beta.GenerativeService.BidiGenerateContent and /v1/realtime), plus /healthz and /readyz`,"invalid_request_error");return}x(c,d)})().catch(y=>{if(!d.headersSent){Ae(d,500,String(y instanceof Error?y.message:y),"proxy_error");return}d.end()})}),B=t.conversations??new H({baseUrl:t.baseUrl,apiUrl:t.apiUrl,catalog:t.catalog}),Ie=[ee(E,{authenticate:async c=>{let d=await e.resolve(c),h=B.connectionFor(d);return{clients:h.clients,scope:M(d.apiKey),onBindResumeHandle:h.bindResumeHandle,clientsForResume:h.clientsForResume,release:h.release}}}),ne(E,{clients:async c=>r.clientsFor(await u(c)),statusOf:f}).close,se(E,{resolver:{resolve:async(c,d)=>{let h=await e.resolve(R(c.headers.authorization)),y=B.connectionFor(h),C;try{C=await ae(y.clients,ie(c),d)}catch(k){throw y.release().catch(()=>{}),k}return{...C,release:y.release}}}})];E.on("upgrade",(c,d)=>{let h=new URL(c.url??"/","http://localhost");h.pathname===Z||h.pathname===te||h.pathname===oe||d.destroyed||(d.write(`HTTP/1.1 404 Not Found\r
|
|
5
|
-
Connection: close\r
|
|
6
|
-
\r
|
|
7
|
-
no WS route for ${h.pathname} \u2014 this endpoint serves only the Gemini Live, /v1/responses, and /v1/realtime upgrade paths`),d.destroy())});let Te=async()=>{for(let c of Ie)await c();await B.detachAll()};return{server:E,metricsServer:_e(l),metrics:l,edgeIdentityServer:Re(),registry:r,closeAll:()=>r.closeAll(),closeWebSockets:Te}}async function Sn(t,e,n){n&&await n();let o=new Promise(r=>t.close(()=>r()));t.closeAllConnections(),await o,await ge()||fe(),await e()}export{Tt as a,ve as b,I as c,Le as d,He as e,Me as f,Ke as g,Ge as h,T as i,N as j,_e as k,Lt as l,ze as m,je as n,We as o,Je as p,Ee as q,Ve as r,Ye as s,Qe as t,Re as u,m as v,_ as w,R as x,tt as y,nt as z,U as A,ot as B,P as C,Bt as D,zt as E,lt as F,M as G,$ as H,xe as I,ct as J,L as K,H as L,ht as M,pt as N,D as O,g as P,bt as Q,F as R,Et as S,St as T,En as U,Sn as V};
|