@voicethere/client 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/README.md +74 -0
- package/dist/browser/audio-visualizer.d.ts +13 -0
- package/dist/browser/audio-visualizer.d.ts.map +1 -0
- package/dist/browser/audio-visualizer.js +86 -0
- package/dist/browser/audio-visualizer.js.map +1 -0
- package/dist/browser/browser-chat-session.d.ts +14 -0
- package/dist/browser/browser-chat-session.d.ts.map +1 -0
- package/dist/browser/browser-chat-session.js +12 -0
- package/dist/browser/browser-chat-session.js.map +1 -0
- package/dist/browser/browser-session.d.ts +23 -0
- package/dist/browser/browser-session.d.ts.map +1 -0
- package/dist/browser/browser-session.js +18 -0
- package/dist/browser/browser-session.js.map +1 -0
- package/dist/browser/browser-voice-session.d.ts +20 -0
- package/dist/browser/browser-voice-session.d.ts.map +1 -0
- package/dist/browser/browser-voice-session.js +174 -0
- package/dist/browser/browser-voice-session.js.map +1 -0
- package/dist/browser/debug-console.d.ts +21 -0
- package/dist/browser/debug-console.d.ts.map +1 -0
- package/dist/browser/debug-console.js +29 -0
- package/dist/browser/debug-console.js.map +1 -0
- package/dist/browser/session-provision.d.ts +52 -0
- package/dist/browser/session-provision.d.ts.map +1 -0
- package/dist/browser/session-provision.js +92 -0
- package/dist/browser/session-provision.js.map +1 -0
- package/dist/embed/index.d.ts +21 -0
- package/dist/embed/index.d.ts.map +1 -0
- package/dist/embed/index.js +131 -0
- package/dist/embed/index.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -0
- package/dist/resolve-connection.d.ts +9 -0
- package/dist/resolve-connection.d.ts.map +1 -0
- package/dist/resolve-connection.js +23 -0
- package/dist/resolve-connection.js.map +1 -0
- package/dist/types.d.ts +36 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +67 -0
- package/templates/debug-page.html +56 -0
- package/templates/embed.html +25 -0
- package/templates/react-use-voicethere.ts +52 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.3.0 — 2026-06-15
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- `@voicethere/client/browser` — voice + DC text chat, async session provisioning poll, debug console, mic visualizer
|
|
8
|
+
- `@voicethere/client/embed` — `createVoiceThereWidget` floating chat launcher
|
|
9
|
+
- `templates/` — React hook, embed HTML, debug page starters
|
|
10
|
+
- CI workflow and release tag publish (mirror `@voicethere/cli`)
|
|
11
|
+
|
|
12
|
+
## 0.1.0
|
|
13
|
+
|
|
14
|
+
- Initial `connectVoiceSession` cloud/local signaling helper
|
package/README.md
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# @voicethere/client
|
|
2
|
+
|
|
3
|
+
Browser and Node client for VoiceThere voice sessions.
|
|
4
|
+
|
|
5
|
+
## Modes
|
|
6
|
+
|
|
7
|
+
| Mode | When | Signaling URL |
|
|
8
|
+
|------|------|---------------|
|
|
9
|
+
| **local** | M1 dev — runner on localhost | `ws://127.0.0.1:8080/ws` |
|
|
10
|
+
| **cloud** | M4 — platform session mint | `wss://signaling…/ws?token=<joinToken>` |
|
|
11
|
+
|
|
12
|
+
Wire protocol: [`@node-webrtc-rust/signaling`](https://www.npmjs.com/package/@node-webrtc-rust/signaling).
|
|
13
|
+
|
|
14
|
+
## Local (runner direct)
|
|
15
|
+
|
|
16
|
+
```typescript
|
|
17
|
+
import { connectVoiceSession } from "@voicethere/client";
|
|
18
|
+
|
|
19
|
+
const client = await connectVoiceSession({
|
|
20
|
+
mode: "local",
|
|
21
|
+
signalingUrl: "ws://127.0.0.1:8080/ws",
|
|
22
|
+
sessionId: "local-dev",
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
client.on("peer-joined", (peerId) => console.log("peer", peerId));
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## Cloud (platform + gateway)
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
// Use a **client** API key (prefix vthc_) — safe to embed in web/mobile apps.
|
|
32
|
+
// Create one in the dashboard (/api-keys) or: voicethere api-keys create --kind client --project-id <uuid> --name "Web app"
|
|
33
|
+
const res = await fetch("https://app.voicethere.dev/api/v1/sessions", {
|
|
34
|
+
method: "POST",
|
|
35
|
+
headers: {
|
|
36
|
+
Authorization: "Bearer vthc_…",
|
|
37
|
+
"Content-Type": "application/json",
|
|
38
|
+
},
|
|
39
|
+
body: JSON.stringify({ project_id: "<uuid>" }),
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
const credentials = await res.json();
|
|
43
|
+
|
|
44
|
+
const client = await connectVoiceSession({
|
|
45
|
+
mode: "cloud",
|
|
46
|
+
credentials: {
|
|
47
|
+
sessionId: credentials.session_id,
|
|
48
|
+
joinToken: credentials.join_token,
|
|
49
|
+
signalingUrl: credentials.signaling_url,
|
|
50
|
+
roomId: credentials.room_id,
|
|
51
|
+
iceServers: credentials.ice_servers,
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Local stack verify
|
|
57
|
+
|
|
58
|
+
From workspace root (after M4 smoke infra is up):
|
|
59
|
+
|
|
60
|
+
```bash
|
|
61
|
+
cd client && npm run demo:cloud
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
npm publish via GitHub Actions on `release/*` tags — see [`scripts/RELEASE.md`](scripts/RELEASE.md).
|
|
65
|
+
|
|
66
|
+
## Browser test page
|
|
67
|
+
|
|
68
|
+
For manual cloud testing with a client API key in the browser:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
npm run demo:browser
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
See [`examples/browser-test/README.md`](examples/browser-test/README.md).
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export type AudioVisualizerOptions = {
|
|
2
|
+
canvas: HTMLCanvasElement;
|
|
3
|
+
audioElement?: HTMLAudioElement;
|
|
4
|
+
mediaStream?: MediaStream;
|
|
5
|
+
waveColor?: string;
|
|
6
|
+
barColor?: string;
|
|
7
|
+
};
|
|
8
|
+
export type AudioVisualizer = {
|
|
9
|
+
stop: () => void;
|
|
10
|
+
resume: () => void;
|
|
11
|
+
};
|
|
12
|
+
export declare function attachAudioVisualizer(options: AudioVisualizerOptions): AudioVisualizer;
|
|
13
|
+
//# sourceMappingURL=audio-visualizer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-visualizer.d.ts","sourceRoot":"","sources":["../../src/browser/audio-visualizer.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,sBAAsB,GAAG;IACnC,MAAM,EAAE,iBAAiB,CAAC;IAC1B,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,MAAM,IAAI,CAAC;IACjB,MAAM,EAAE,MAAM,IAAI,CAAC;CACpB,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,OAAO,EAAE,sBAAsB,GAC9B,eAAe,CAwGjB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
export function attachAudioVisualizer(options) {
|
|
2
|
+
const { canvas, audioElement, mediaStream, waveColor = "#38bdf8", barColor = "#818cf8", } = options;
|
|
3
|
+
if (!audioElement && !mediaStream) {
|
|
4
|
+
throw new Error("attachAudioVisualizer requires audioElement or mediaStream");
|
|
5
|
+
}
|
|
6
|
+
const ctx = canvas.getContext("2d");
|
|
7
|
+
if (!ctx) {
|
|
8
|
+
throw new Error("canvas 2d context unavailable");
|
|
9
|
+
}
|
|
10
|
+
const audioCtx = new AudioContext();
|
|
11
|
+
const analyser = audioCtx.createAnalyser();
|
|
12
|
+
analyser.fftSize = 2048;
|
|
13
|
+
analyser.smoothingTimeConstant = 0.75;
|
|
14
|
+
const streamSource = mediaStream ??
|
|
15
|
+
(audioElement?.srcObject instanceof MediaStream
|
|
16
|
+
? audioElement.srcObject
|
|
17
|
+
: null);
|
|
18
|
+
if (streamSource) {
|
|
19
|
+
audioCtx.createMediaStreamSource(streamSource).connect(analyser);
|
|
20
|
+
}
|
|
21
|
+
else if (audioElement) {
|
|
22
|
+
const source = audioCtx.createMediaElementSource(audioElement);
|
|
23
|
+
source.connect(analyser);
|
|
24
|
+
analyser.connect(audioCtx.destination);
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
throw new Error("attachAudioVisualizer requires audioElement or mediaStream");
|
|
28
|
+
}
|
|
29
|
+
const ensureRunning = () => {
|
|
30
|
+
if (audioCtx.state === "suspended") {
|
|
31
|
+
void audioCtx.resume();
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
if (audioElement) {
|
|
35
|
+
audioElement.addEventListener("playing", ensureRunning);
|
|
36
|
+
}
|
|
37
|
+
for (const track of streamSource?.getAudioTracks() ?? []) {
|
|
38
|
+
track.addEventListener("unmute", ensureRunning);
|
|
39
|
+
}
|
|
40
|
+
const timeData = new Uint8Array(analyser.fftSize);
|
|
41
|
+
const freqData = new Uint8Array(analyser.frequencyBinCount);
|
|
42
|
+
let rafId = 0;
|
|
43
|
+
let stopped = false;
|
|
44
|
+
const draw = () => {
|
|
45
|
+
if (stopped)
|
|
46
|
+
return;
|
|
47
|
+
rafId = requestAnimationFrame(draw);
|
|
48
|
+
const width = canvas.width;
|
|
49
|
+
const height = canvas.height;
|
|
50
|
+
ctx.clearRect(0, 0, width, height);
|
|
51
|
+
analyser.getByteTimeDomainData(timeData);
|
|
52
|
+
ctx.lineWidth = 2;
|
|
53
|
+
ctx.strokeStyle = waveColor;
|
|
54
|
+
ctx.beginPath();
|
|
55
|
+
const sliceWidth = width / timeData.length;
|
|
56
|
+
let x = 0;
|
|
57
|
+
for (let i = 0; i < timeData.length; i++) {
|
|
58
|
+
const v = timeData[i] / 128.0;
|
|
59
|
+
const y = (v * height) / 2;
|
|
60
|
+
if (i === 0)
|
|
61
|
+
ctx.moveTo(x, y);
|
|
62
|
+
else
|
|
63
|
+
ctx.lineTo(x, y);
|
|
64
|
+
x += sliceWidth;
|
|
65
|
+
}
|
|
66
|
+
ctx.lineTo(width, height / 2);
|
|
67
|
+
ctx.stroke();
|
|
68
|
+
analyser.getByteFrequencyData(freqData);
|
|
69
|
+
const barWidth = width / freqData.length;
|
|
70
|
+
for (let i = 0; i < freqData.length; i++) {
|
|
71
|
+
const barHeight = (freqData[i] / 255) * (height * 0.45);
|
|
72
|
+
ctx.fillStyle = barColor;
|
|
73
|
+
ctx.fillRect(i * barWidth, height - barHeight, barWidth - 1, barHeight);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
draw();
|
|
77
|
+
return {
|
|
78
|
+
stop: () => {
|
|
79
|
+
stopped = true;
|
|
80
|
+
cancelAnimationFrame(rafId);
|
|
81
|
+
void audioCtx.close();
|
|
82
|
+
},
|
|
83
|
+
resume: ensureRunning,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=audio-visualizer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"audio-visualizer.js","sourceRoot":"","sources":["../../src/browser/audio-visualizer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { SessionCredentials } from "./session-provision.js";
|
|
2
|
+
import type { DebugConsole } from "./debug-console.js";
|
|
3
|
+
import { type BrowserVoiceSession } from "./browser-voice-session.js";
|
|
4
|
+
export declare const VOICETHERE_CHAT_CHANNEL_LABEL = "voicethere";
|
|
5
|
+
export type BrowserChatSessionOptions = {
|
|
6
|
+
credentials: SessionCredentials;
|
|
7
|
+
peerId?: string;
|
|
8
|
+
onDebugEvent?: DebugConsole;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* DC-only session — connects WebRTC without microphone for text chat debugging.
|
|
12
|
+
*/
|
|
13
|
+
export declare function connectBrowserChatSession(options: BrowserChatSessionOptions): Promise<BrowserVoiceSession>;
|
|
14
|
+
//# sourceMappingURL=browser-chat-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-chat-session.d.ts","sourceRoot":"","sources":["../../src/browser/browser-chat-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AAEpC,eAAO,MAAM,6BAA6B,eAAe,CAAC;AAE1D,MAAM,MAAM,yBAAyB,GAAG;IACtC,WAAW,EAAE,kBAAkB,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF;;GAEG;AACH,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,mBAAmB,CAAC,CAK9B"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { connectBrowserVoiceSession, } from "./browser-voice-session.js";
|
|
2
|
+
export const VOICETHERE_CHAT_CHANNEL_LABEL = "voicethere";
|
|
3
|
+
/**
|
|
4
|
+
* DC-only session — connects WebRTC without microphone for text chat debugging.
|
|
5
|
+
*/
|
|
6
|
+
export async function connectBrowserChatSession(options) {
|
|
7
|
+
return connectBrowserVoiceSession({
|
|
8
|
+
...options,
|
|
9
|
+
requestMic: false,
|
|
10
|
+
});
|
|
11
|
+
}
|
|
12
|
+
//# sourceMappingURL=browser-chat-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-chat-session.js","sourceRoot":"","sources":["../../src/browser/browser-chat-session.ts"],"names":[],"mappings":"AAEA,OAAO,EACL,0BAA0B,GAE3B,MAAM,4BAA4B,CAAC;AAEpC,MAAM,CAAC,MAAM,6BAA6B,GAAG,YAAY,CAAC;AAQ1D;;GAEG;AACH,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,OAAkC;IAElC,OAAO,0BAA0B,CAAC;QAChC,GAAG,OAAO;QACV,UAAU,EAAE,KAAK;KAClB,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { DebugConsole } from "./debug-console.js";
|
|
2
|
+
import type { SessionCredentials } from "./session-provision.js";
|
|
3
|
+
import { type BrowserVoiceSession } from "./browser-voice-session.js";
|
|
4
|
+
export type BrowserSessionMode = "voice" | "chat" | "both";
|
|
5
|
+
export type ConnectBrowserSessionOptions = {
|
|
6
|
+
mode: BrowserSessionMode;
|
|
7
|
+
credentials: SessionCredentials;
|
|
8
|
+
peerId?: string;
|
|
9
|
+
audioElement?: HTMLAudioElement;
|
|
10
|
+
onDebugEvent?: DebugConsole;
|
|
11
|
+
};
|
|
12
|
+
export type BrowserSession = BrowserVoiceSession & {
|
|
13
|
+
mode: BrowserSessionMode;
|
|
14
|
+
};
|
|
15
|
+
export declare function connectBrowserSession(options: ConnectBrowserSessionOptions): Promise<BrowserSession>;
|
|
16
|
+
export { connectBrowserChatSession } from "./browser-chat-session.js";
|
|
17
|
+
export { connectBrowserVoiceSession } from "./browser-voice-session.js";
|
|
18
|
+
export { startSession } from "./session-provision.js";
|
|
19
|
+
export { attachAudioVisualizer } from "./audio-visualizer.js";
|
|
20
|
+
export { createDebugConsole } from "./debug-console.js";
|
|
21
|
+
export type { SessionCredentials, SessionStatusResponse, StartSessionOptions, StartSessionResult, } from "./session-provision.js";
|
|
22
|
+
export type { DebugConsole, DebugEvent } from "./debug-console.js";
|
|
23
|
+
//# sourceMappingURL=browser-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-session.d.ts","sourceRoot":"","sources":["../../src/browser/browser-session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAEjE,OAAO,EAEL,KAAK,mBAAmB,EACzB,MAAM,4BAA4B,CAAC;AAEpC,MAAM,MAAM,kBAAkB,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,CAAC;AAE3D,MAAM,MAAM,4BAA4B,GAAG;IACzC,IAAI,EAAE,kBAAkB,CAAC;IACzB,WAAW,EAAE,kBAAkB,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,mBAAmB,GAAG;IACjD,IAAI,EAAE,kBAAkB,CAAC;CAC1B,CAAC;AAEF,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,4BAA4B,GACpC,OAAO,CAAC,cAAc,CAAC,CAWzB;AAED,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC;AAExD,YAAY,EACV,kBAAkB,EAClB,qBAAqB,EACrB,mBAAmB,EACnB,kBAAkB,GACnB,MAAM,wBAAwB,CAAC;AAEhC,YAAY,EAAE,YAAY,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { connectBrowserVoiceSession, } from "./browser-voice-session.js";
|
|
2
|
+
export async function connectBrowserSession(options) {
|
|
3
|
+
const requestMic = options.mode === "voice" || options.mode === "both";
|
|
4
|
+
const session = await connectBrowserVoiceSession({
|
|
5
|
+
credentials: options.credentials,
|
|
6
|
+
peerId: options.peerId,
|
|
7
|
+
requestMic,
|
|
8
|
+
audioElement: options.audioElement,
|
|
9
|
+
onDebugEvent: options.onDebugEvent,
|
|
10
|
+
});
|
|
11
|
+
return { ...session, mode: options.mode };
|
|
12
|
+
}
|
|
13
|
+
export { connectBrowserChatSession } from "./browser-chat-session.js";
|
|
14
|
+
export { connectBrowserVoiceSession } from "./browser-voice-session.js";
|
|
15
|
+
export { startSession } from "./session-provision.js";
|
|
16
|
+
export { attachAudioVisualizer } from "./audio-visualizer.js";
|
|
17
|
+
export { createDebugConsole } from "./debug-console.js";
|
|
18
|
+
//# sourceMappingURL=browser-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-session.js","sourceRoot":"","sources":["../../src/browser/browser-session.ts"],"names":[],"mappings":"AAGA,OAAO,EACL,0BAA0B,GAE3B,MAAM,4BAA4B,CAAC;AAgBpC,MAAM,CAAC,KAAK,UAAU,qBAAqB,CACzC,OAAqC;IAErC,MAAM,UAAU,GAAG,OAAO,CAAC,IAAI,KAAK,OAAO,IAAI,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC;IACvE,MAAM,OAAO,GAAG,MAAM,0BAA0B,CAAC;QAC/C,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,MAAM,EAAE,OAAO,CAAC,MAAM;QACtB,UAAU;QACV,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,YAAY,EAAE,OAAO,CAAC,YAAY;KACnC,CAAC,CAAC;IAEH,OAAO,EAAE,GAAG,OAAO,EAAE,IAAI,EAAE,OAAO,CAAC,IAAI,EAAE,CAAC;AAC5C,CAAC;AAED,OAAO,EAAE,yBAAyB,EAAE,MAAM,2BAA2B,CAAC;AACtE,OAAO,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AACxE,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAC"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { SessionCredentials } from "./session-provision.js";
|
|
2
|
+
import type { DebugConsole } from "./debug-console.js";
|
|
3
|
+
export declare const VOICE_AGENT_SERVER_PEER_ID = "voice-agent-server";
|
|
4
|
+
export declare const VOICE_CONTROL_CHANNEL_LABEL = "voice-control";
|
|
5
|
+
export type BrowserVoiceSessionOptions = {
|
|
6
|
+
credentials: SessionCredentials;
|
|
7
|
+
peerId?: string;
|
|
8
|
+
requestMic?: boolean;
|
|
9
|
+
audioElement?: HTMLAudioElement;
|
|
10
|
+
onDebugEvent?: DebugConsole;
|
|
11
|
+
};
|
|
12
|
+
export type BrowserVoiceSession = {
|
|
13
|
+
peerId: string;
|
|
14
|
+
disconnect: () => void;
|
|
15
|
+
sendSpeak: (text: string) => void;
|
|
16
|
+
sendChat: (text: string) => void;
|
|
17
|
+
getMicStream: () => MediaStream | null;
|
|
18
|
+
};
|
|
19
|
+
export declare function connectBrowserVoiceSession(options: BrowserVoiceSessionOptions): Promise<BrowserVoiceSession>;
|
|
20
|
+
//# sourceMappingURL=browser-voice-session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-voice-session.d.ts","sourceRoot":"","sources":["../../src/browser/browser-voice-session.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,eAAO,MAAM,0BAA0B,uBAAuB,CAAC;AAC/D,eAAO,MAAM,2BAA2B,kBAAkB,CAAC;AAE3D,MAAM,MAAM,0BAA0B,GAAG;IACvC,WAAW,EAAE,kBAAkB,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,gBAAgB,CAAC;IAChC,YAAY,CAAC,EAAE,YAAY,CAAC;CAC7B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,IAAI,CAAC;IACvB,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAClC,QAAQ,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IACjC,YAAY,EAAE,MAAM,WAAW,GAAG,IAAI,CAAC;CACxC,CAAC;AAMF,wBAAsB,0BAA0B,CAC9C,OAAO,EAAE,0BAA0B,GAClC,OAAO,CAAC,mBAAmB,CAAC,CAkM9B"}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import { appendJoinToken } from "../resolve-connection.js";
|
|
2
|
+
export const VOICE_AGENT_SERVER_PEER_ID = "voice-agent-server";
|
|
3
|
+
export const VOICE_CONTROL_CHANNEL_LABEL = "voice-control";
|
|
4
|
+
function defaultPeerId() {
|
|
5
|
+
return `client-${Math.random().toString(36).slice(2, 10)}`;
|
|
6
|
+
}
|
|
7
|
+
export async function connectBrowserVoiceSession(options) {
|
|
8
|
+
const debug = options.onDebugEvent;
|
|
9
|
+
const peerId = options.peerId ?? defaultPeerId();
|
|
10
|
+
const roomId = options.credentials.room_id;
|
|
11
|
+
const signalingUrl = appendJoinToken(options.credentials.signaling_url, options.credentials.join_token);
|
|
12
|
+
const iceServers = options.credentials.ice_servers?.length
|
|
13
|
+
? options.credentials.ice_servers
|
|
14
|
+
: [{ urls: "stun:stun.l.google.com:19302" }];
|
|
15
|
+
let ws = null;
|
|
16
|
+
let pc = null;
|
|
17
|
+
let controlChannel = null;
|
|
18
|
+
let micStream = null;
|
|
19
|
+
const pendingIce = [];
|
|
20
|
+
const sendSignal = (message) => {
|
|
21
|
+
if (ws?.readyState === WebSocket.OPEN) {
|
|
22
|
+
ws.send(JSON.stringify(message));
|
|
23
|
+
}
|
|
24
|
+
};
|
|
25
|
+
const sendToServer = (payload) => {
|
|
26
|
+
sendSignal({ room: roomId, peerId, ...payload });
|
|
27
|
+
};
|
|
28
|
+
if (options.requestMic !== false) {
|
|
29
|
+
micStream = await navigator.mediaDevices.getUserMedia({
|
|
30
|
+
audio: true,
|
|
31
|
+
video: false,
|
|
32
|
+
});
|
|
33
|
+
debug?.info("voice", "mic_granted");
|
|
34
|
+
}
|
|
35
|
+
ws = new WebSocket(signalingUrl);
|
|
36
|
+
await new Promise((resolve, reject) => {
|
|
37
|
+
if (!ws)
|
|
38
|
+
return reject(new Error("WebSocket missing"));
|
|
39
|
+
ws.onopen = () => {
|
|
40
|
+
sendSignal({ type: "join", room: roomId, peerId });
|
|
41
|
+
debug?.info("signaling", "joined", roomId);
|
|
42
|
+
resolve();
|
|
43
|
+
};
|
|
44
|
+
ws.onerror = () => reject(new Error("WebSocket error"));
|
|
45
|
+
});
|
|
46
|
+
const wireControl = (channel) => {
|
|
47
|
+
controlChannel = channel;
|
|
48
|
+
channel.onopen = () => debug?.info("dc", "open", VOICE_CONTROL_CHANNEL_LABEL);
|
|
49
|
+
channel.onclose = () => {
|
|
50
|
+
debug?.info("dc", "close", VOICE_CONTROL_CHANNEL_LABEL);
|
|
51
|
+
controlChannel = null;
|
|
52
|
+
};
|
|
53
|
+
channel.onmessage = (event) => {
|
|
54
|
+
debug?.debug("dc", "message", String(event.data));
|
|
55
|
+
try {
|
|
56
|
+
const message = JSON.parse(String(event.data));
|
|
57
|
+
if (message.type === "speech_event") {
|
|
58
|
+
debug?.info("speech", message.event ?? "event", message.text);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
debug?.info("dc", message.type ?? "json", message.text);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
catch {
|
|
65
|
+
debug?.warn("dc", "malformed", String(event.data));
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
};
|
|
69
|
+
const onServerOffer = async (sdp) => {
|
|
70
|
+
if (pc) {
|
|
71
|
+
pc.close();
|
|
72
|
+
pc = null;
|
|
73
|
+
pendingIce.length = 0;
|
|
74
|
+
}
|
|
75
|
+
pc = new RTCPeerConnection({ iceServers });
|
|
76
|
+
pc.ontrack = (event) => {
|
|
77
|
+
if (event.track.kind !== "audio")
|
|
78
|
+
return;
|
|
79
|
+
const stream = event.streams[0] ?? new MediaStream([event.track]);
|
|
80
|
+
if (options.audioElement) {
|
|
81
|
+
options.audioElement.srcObject = stream;
|
|
82
|
+
void options.audioElement.play().catch(() => undefined);
|
|
83
|
+
}
|
|
84
|
+
debug?.info("webrtc", "agent_audio_track");
|
|
85
|
+
};
|
|
86
|
+
pc.ondatachannel = (event) => {
|
|
87
|
+
if (event.channel.label === VOICE_CONTROL_CHANNEL_LABEL) {
|
|
88
|
+
wireControl(event.channel);
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
pc.onicecandidate = (event) => {
|
|
92
|
+
if (event.candidate) {
|
|
93
|
+
sendToServer({
|
|
94
|
+
type: "ice-candidate",
|
|
95
|
+
targetPeerId: VOICE_AGENT_SERVER_PEER_ID,
|
|
96
|
+
candidate: event.candidate.toJSON(),
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
pc.onconnectionstatechange = () => {
|
|
101
|
+
debug?.info("webrtc", "connection_state", pc?.connectionState);
|
|
102
|
+
};
|
|
103
|
+
await pc.setRemoteDescription(sdp);
|
|
104
|
+
for (const candidate of pendingIce.splice(0)) {
|
|
105
|
+
await pc.addIceCandidate(candidate);
|
|
106
|
+
}
|
|
107
|
+
if (micStream) {
|
|
108
|
+
for (const track of micStream.getAudioTracks()) {
|
|
109
|
+
pc.addTrack(track, micStream);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const answer = await pc.createAnswer();
|
|
113
|
+
await pc.setLocalDescription(answer);
|
|
114
|
+
sendToServer({
|
|
115
|
+
type: "answer",
|
|
116
|
+
targetPeerId: VOICE_AGENT_SERVER_PEER_ID,
|
|
117
|
+
sdp: pc.localDescription,
|
|
118
|
+
});
|
|
119
|
+
debug?.info("signaling", "answer_sent");
|
|
120
|
+
};
|
|
121
|
+
ws.onmessage = (event) => {
|
|
122
|
+
const message = JSON.parse(String(event.data));
|
|
123
|
+
debug?.debug("signaling", message.type, message.peerId);
|
|
124
|
+
switch (message.type) {
|
|
125
|
+
case "offer":
|
|
126
|
+
if (message.peerId === VOICE_AGENT_SERVER_PEER_ID && message.sdp) {
|
|
127
|
+
void onServerOffer(message.sdp);
|
|
128
|
+
}
|
|
129
|
+
break;
|
|
130
|
+
case "ice-candidate":
|
|
131
|
+
if (message.peerId === VOICE_AGENT_SERVER_PEER_ID &&
|
|
132
|
+
message.candidate &&
|
|
133
|
+
pc) {
|
|
134
|
+
if (!pc.remoteDescription) {
|
|
135
|
+
pendingIce.push(message.candidate);
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
void pc.addIceCandidate(message.candidate);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
break;
|
|
142
|
+
default:
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
};
|
|
146
|
+
return {
|
|
147
|
+
peerId,
|
|
148
|
+
getMicStream: () => micStream,
|
|
149
|
+
sendSpeak: (text) => {
|
|
150
|
+
if (!controlChannel || controlChannel.readyState !== "open") {
|
|
151
|
+
debug?.error("dc", "speak_failed", "control channel not open");
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
controlChannel.send(JSON.stringify({ type: "speak", text }));
|
|
155
|
+
debug?.info("dc", "speak", text);
|
|
156
|
+
},
|
|
157
|
+
sendChat: (text) => {
|
|
158
|
+
if (!controlChannel || controlChannel.readyState !== "open") {
|
|
159
|
+
debug?.error("dc", "chat_failed", "control channel not open");
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
controlChannel.send(JSON.stringify({ type: "chat", text }));
|
|
163
|
+
debug?.info("dc", "chat", text);
|
|
164
|
+
},
|
|
165
|
+
disconnect: () => {
|
|
166
|
+
controlChannel?.close();
|
|
167
|
+
pc?.close();
|
|
168
|
+
micStream?.getTracks().forEach((track) => track.stop());
|
|
169
|
+
ws?.close();
|
|
170
|
+
debug?.info("session", "disconnected");
|
|
171
|
+
},
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=browser-voice-session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"browser-voice-session.js","sourceRoot":"","sources":["../../src/browser/browser-voice-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type DebugEventLevel = "info" | "warn" | "error" | "debug";
|
|
2
|
+
export type DebugEvent = {
|
|
3
|
+
level: DebugEventLevel;
|
|
4
|
+
source: string;
|
|
5
|
+
name: string;
|
|
6
|
+
detail?: string;
|
|
7
|
+
raw?: unknown;
|
|
8
|
+
ts: string;
|
|
9
|
+
};
|
|
10
|
+
export type DebugEventSink = (event: DebugEvent) => void;
|
|
11
|
+
export declare function createDebugConsole(onEvent?: DebugEventSink): {
|
|
12
|
+
events: () => DebugEvent[];
|
|
13
|
+
clear: () => void;
|
|
14
|
+
info: (source: string, name: string, detail?: string, raw?: unknown) => DebugEvent;
|
|
15
|
+
warn: (source: string, name: string, detail?: string, raw?: unknown) => DebugEvent;
|
|
16
|
+
error: (source: string, name: string, detail?: string, raw?: unknown) => DebugEvent;
|
|
17
|
+
debug: (source: string, name: string, detail?: string, raw?: unknown) => DebugEvent;
|
|
18
|
+
exportText: () => string;
|
|
19
|
+
};
|
|
20
|
+
export type DebugConsole = ReturnType<typeof createDebugConsole>;
|
|
21
|
+
//# sourceMappingURL=debug-console.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-console.d.ts","sourceRoot":"","sources":["../../src/browser/debug-console.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,eAAe,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,OAAO,CAAC;AAElE,MAAM,MAAM,UAAU,GAAG;IACvB,KAAK,EAAE,eAAe,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,EAAE,EAAE,MAAM,CAAC;CACZ,CAAC;AAEF,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;AAEzD,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,cAAc;;;mBAkBxC,MAAM,QAAQ,MAAM,WAAW,MAAM,QAAQ,OAAO;mBAEpD,MAAM,QAAQ,MAAM,WAAW,MAAM,QAAQ,OAAO;oBAEnD,MAAM,QAAQ,MAAM,WAAW,MAAM,QAAQ,OAAO;oBAEpD,MAAM,QAAQ,MAAM,WAAW,MAAM,QAAQ,OAAO;;EAUvE;AAED,MAAM,MAAM,YAAY,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
export function createDebugConsole(onEvent) {
|
|
2
|
+
const events = [];
|
|
3
|
+
const emit = (partial) => {
|
|
4
|
+
const event = {
|
|
5
|
+
...partial,
|
|
6
|
+
ts: new Date().toISOString(),
|
|
7
|
+
};
|
|
8
|
+
events.push(event);
|
|
9
|
+
onEvent?.(event);
|
|
10
|
+
return event;
|
|
11
|
+
};
|
|
12
|
+
return {
|
|
13
|
+
events: () => [...events],
|
|
14
|
+
clear: () => {
|
|
15
|
+
events.length = 0;
|
|
16
|
+
},
|
|
17
|
+
info: (source, name, detail, raw) => emit({ level: "info", source, name, detail, raw }),
|
|
18
|
+
warn: (source, name, detail, raw) => emit({ level: "warn", source, name, detail, raw }),
|
|
19
|
+
error: (source, name, detail, raw) => emit({ level: "error", source, name, detail, raw }),
|
|
20
|
+
debug: (source, name, detail, raw) => emit({ level: "debug", source, name, detail, raw }),
|
|
21
|
+
exportText: () => events
|
|
22
|
+
.map((event) => {
|
|
23
|
+
const detail = event.detail ? `: ${event.detail}` : "";
|
|
24
|
+
return `${event.ts} [${event.level}] ${event.source}/${event.name}${detail}`;
|
|
25
|
+
})
|
|
26
|
+
.join("\n"),
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
//# sourceMappingURL=debug-console.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"debug-console.js","sourceRoot":"","sources":["../../src/browser/debug-console.ts"],"names":[],"mappings":"AAaA,MAAM,UAAU,kBAAkB,CAAC,OAAwB;IACzD,MAAM,MAAM,GAAiB,EAAE,CAAC;IAEhC,MAAM,IAAI,GAAG,CAAC,OAA+B,EAAE,EAAE;QAC/C,MAAM,KAAK,GAAe;YACxB,GAAG,OAAO;YACV,EAAE,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SAC7B,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnB,OAAO,EAAE,CAAC,KAAK,CAAC,CAAC;QACjB,OAAO,KAAK,CAAC;IACf,CAAC,CAAC;IAEF,OAAO;QACL,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC,GAAG,MAAM,CAAC;QACzB,KAAK,EAAE,GAAG,EAAE;YACV,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QACpB,CAAC;QACD,IAAI,EAAE,CAAC,MAAc,EAAE,IAAY,EAAE,MAAe,EAAE,GAAa,EAAE,EAAE,CACrE,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;QACpD,IAAI,EAAE,CAAC,MAAc,EAAE,IAAY,EAAE,MAAe,EAAE,GAAa,EAAE,EAAE,CACrE,IAAI,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;QACpD,KAAK,EAAE,CAAC,MAAc,EAAE,IAAY,EAAE,MAAe,EAAE,GAAa,EAAE,EAAE,CACtE,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;QACrD,KAAK,EAAE,CAAC,MAAc,EAAE,IAAY,EAAE,MAAe,EAAE,GAAa,EAAE,EAAE,CACtE,IAAI,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;QACrD,UAAU,EAAE,GAAG,EAAE,CACf,MAAM;aACH,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;YACb,MAAM,MAAM,GAAG,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,KAAK,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,OAAO,GAAG,KAAK,CAAC,EAAE,KAAK,KAAK,CAAC,KAAK,KAAK,KAAK,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,GAAG,MAAM,EAAE,CAAC;QAC/E,CAAC,CAAC;aACD,IAAI,CAAC,IAAI,CAAC;KAChB,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { DebugConsole } from "./debug-console.js";
|
|
2
|
+
export type SessionFailureCode = "NOT_DEPLOYED" | "RUNNER_UNAVAILABLE" | "ORCHESTRATOR_ERROR" | "JOIN_TOKEN_ERROR" | "TIMEOUT";
|
|
3
|
+
export type SessionJobStatus = "queued" | "provisioning" | "ready" | "failed";
|
|
4
|
+
export type SessionCredentials = {
|
|
5
|
+
session_id: string;
|
|
6
|
+
join_token: string;
|
|
7
|
+
signaling_url: string;
|
|
8
|
+
room_id: string;
|
|
9
|
+
ice_servers: Array<{
|
|
10
|
+
urls: string | string[];
|
|
11
|
+
username?: string;
|
|
12
|
+
credential?: string;
|
|
13
|
+
}>;
|
|
14
|
+
expires_at: string;
|
|
15
|
+
};
|
|
16
|
+
export type SessionStatusResponse = {
|
|
17
|
+
session_id: string;
|
|
18
|
+
status: SessionJobStatus;
|
|
19
|
+
project_id: string;
|
|
20
|
+
build_id: string | null;
|
|
21
|
+
failure_code?: SessionFailureCode | null;
|
|
22
|
+
failure_message?: string | null;
|
|
23
|
+
credentials?: SessionCredentials | null;
|
|
24
|
+
created_at: string;
|
|
25
|
+
updated_at: string;
|
|
26
|
+
completed_at?: string | null;
|
|
27
|
+
};
|
|
28
|
+
export type StartSessionOptions = {
|
|
29
|
+
apiBase: string;
|
|
30
|
+
projectId: string;
|
|
31
|
+
buildId?: string;
|
|
32
|
+
async?: boolean;
|
|
33
|
+
headers?: Record<string, string>;
|
|
34
|
+
pollIntervalMs?: number;
|
|
35
|
+
pollTimeoutMs?: number;
|
|
36
|
+
onStatus?: (status: SessionStatusResponse) => void;
|
|
37
|
+
debug?: DebugConsole;
|
|
38
|
+
};
|
|
39
|
+
export type StartSessionResult = {
|
|
40
|
+
ok: true;
|
|
41
|
+
credentials: SessionCredentials;
|
|
42
|
+
jobId: string;
|
|
43
|
+
} | {
|
|
44
|
+
ok: false;
|
|
45
|
+
code: SessionFailureCode | "TIMEOUT" | "HTTP_ERROR";
|
|
46
|
+
message: string;
|
|
47
|
+
};
|
|
48
|
+
/**
|
|
49
|
+
* POST /api/v1/sessions with async=true and poll GET until ready or failed.
|
|
50
|
+
*/
|
|
51
|
+
export declare function startSession(options: StartSessionOptions): Promise<StartSessionResult>;
|
|
52
|
+
//# sourceMappingURL=session-provision.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-provision.d.ts","sourceRoot":"","sources":["../../src/browser/session-provision.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAEvD,MAAM,MAAM,kBAAkB,GAC1B,cAAc,GACd,oBAAoB,GACpB,oBAAoB,GACpB,kBAAkB,GAClB,SAAS,CAAC;AAEd,MAAM,MAAM,gBAAgB,GAAG,QAAQ,GAAG,cAAc,GAAG,OAAO,GAAG,QAAQ,CAAC;AAE9E,MAAM,MAAM,kBAAkB,GAAG;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,KAAK,CAAC;QACjB,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;QACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,UAAU,CAAC,EAAE,MAAM,CAAC;KACrB,CAAC,CAAC;IACH,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,YAAY,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACzC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,WAAW,CAAC,EAAE,kBAAkB,GAAG,IAAI,CAAC;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B,CAAC;AAEF,MAAM,MAAM,mBAAmB,GAAG;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,KAAK,IAAI,CAAC;IACnD,KAAK,CAAC,EAAE,YAAY,CAAC;CACtB,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAC1B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,WAAW,EAAE,kBAAkB,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC5D;IACE,EAAE,EAAE,KAAK,CAAC;IACV,IAAI,EAAE,kBAAkB,GAAG,SAAS,GAAG,YAAY,CAAC;IACpD,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AA4DN;;GAEG;AACH,wBAAsB,YAAY,CAChC,OAAO,EAAE,mBAAmB,GAC3B,OAAO,CAAC,kBAAkB,CAAC,CA2D7B"}
|