@pollen-robotics/reachy-mini-sdk 1.7.3-main.7457b2b
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 +188 -0
- package/LICENSE +201 -0
- package/README.md +129 -0
- package/host/APP_AUTHOR_GUIDE.md +646 -0
- package/host/README.md +155 -0
- package/host/SPEC.md +618 -0
- package/host/dist/ReachyHost.d.ts +39 -0
- package/host/dist/ReachyHost.d.ts.map +1 -0
- package/host/dist/assets/index.d.ts +16 -0
- package/host/dist/assets/index.d.ts.map +1 -0
- package/host/dist/chunks/index-C3B3FuBC.js +402 -0
- package/host/dist/chunks/index-C3B3FuBC.js.map +1 -0
- package/host/dist/chunks/mountHost-8f-laxwI.js +48280 -0
- package/host/dist/chunks/mountHost-8f-laxwI.js.map +1 -0
- package/host/dist/chunks/reachy-mini-sdk-T3Zr7Hwc.js +1726 -0
- package/host/dist/chunks/reachy-mini-sdk-T3Zr7Hwc.js.map +1 -0
- package/host/dist/components/ConnectingView.d.ts +13 -0
- package/host/dist/components/ConnectingView.d.ts.map +1 -0
- package/host/dist/components/EmbedFrame.d.ts +16 -0
- package/host/dist/components/EmbedFrame.d.ts.map +1 -0
- package/host/dist/components/ErrorView.d.ts +21 -0
- package/host/dist/components/ErrorView.d.ts.map +1 -0
- package/host/dist/components/LeavingView.d.ts +33 -0
- package/host/dist/components/LeavingView.d.ts.map +1 -0
- package/host/dist/components/PickerView.d.ts +51 -0
- package/host/dist/components/PickerView.d.ts.map +1 -0
- package/host/dist/components/ReachyHostShell.d.ts +21 -0
- package/host/dist/components/ReachyHostShell.d.ts.map +1 -0
- package/host/dist/components/SignInView.d.ts +32 -0
- package/host/dist/components/SignInView.d.ts.map +1 -0
- package/host/dist/components/StepsProgressIndicator.d.ts +41 -0
- package/host/dist/components/StepsProgressIndicator.d.ts.map +1 -0
- package/host/dist/components/TopBar.d.ts +52 -0
- package/host/dist/components/TopBar.d.ts.map +1 -0
- package/host/dist/components/WelcomeBackOverlay.d.ts +28 -0
- package/host/dist/components/WelcomeBackOverlay.d.ts.map +1 -0
- package/host/dist/embed/index.d.ts +150 -0
- package/host/dist/embed/index.d.ts.map +1 -0
- package/host/dist/entry/auto.d.ts +12 -0
- package/host/dist/entry/auto.d.ts.map +1 -0
- package/host/dist/entry/auto.js +7 -0
- package/host/dist/entry/auto.js.map +1 -0
- package/host/dist/entry/embed.d.ts +12 -0
- package/host/dist/entry/embed.d.ts.map +1 -0
- package/host/dist/entry/embed.js +7 -0
- package/host/dist/entry/embed.js.map +1 -0
- package/host/dist/hooks/useHfProfile.d.ts +7 -0
- package/host/dist/hooks/useHfProfile.d.ts.map +1 -0
- package/host/dist/hooks/useHostBridge.d.ts +37 -0
- package/host/dist/hooks/useHostBridge.d.ts.map +1 -0
- package/host/dist/hooks/useOAuth.d.ts +16 -0
- package/host/dist/hooks/useOAuth.d.ts.map +1 -0
- package/host/dist/hooks/useRobots.d.ts +17 -0
- package/host/dist/hooks/useRobots.d.ts.map +1 -0
- package/host/dist/hooks/useSdk.d.ts +16 -0
- package/host/dist/hooks/useSdk.d.ts.map +1 -0
- package/host/dist/index.d.ts +25 -0
- package/host/dist/index.d.ts.map +1 -0
- package/host/dist/index.js +15 -0
- package/host/dist/index.js.map +1 -0
- package/host/dist/lib/centralListener.d.ts +73 -0
- package/host/dist/lib/centralListener.d.ts.map +1 -0
- package/host/dist/lib/centralRest.d.ts +35 -0
- package/host/dist/lib/centralRest.d.ts.map +1 -0
- package/host/dist/lib/protocol.d.ts +230 -0
- package/host/dist/lib/protocol.d.ts.map +1 -0
- package/host/dist/lib/protocol.js +48 -0
- package/host/dist/lib/protocol.js.map +1 -0
- package/host/dist/lib/sdk-types.d.ts +46 -0
- package/host/dist/lib/sdk-types.d.ts.map +1 -0
- package/host/dist/lib/settings.d.ts +69 -0
- package/host/dist/lib/settings.d.ts.map +1 -0
- package/host/dist/lib/signalingUrl.d.ts +28 -0
- package/host/dist/lib/signalingUrl.d.ts.map +1 -0
- package/host/dist/lib/theme.d.ts +4 -0
- package/host/dist/lib/theme.d.ts.map +1 -0
- package/host/dist/lib/themeMode.d.ts +9 -0
- package/host/dist/lib/themeMode.d.ts.map +1 -0
- package/host/dist/lib/tokens.d.ts +64 -0
- package/host/dist/lib/tokens.d.ts.map +1 -0
- package/host/dist/mountHost.d.ts +36 -0
- package/host/dist/mountHost.d.ts.map +1 -0
- package/package.json +109 -0
- package/reachy-mini-sdk.d.ts +285 -0
- package/reachy-mini-sdk.js +2625 -0
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { decodeCredsFromHash as E, PROTOCOL_VERSION as w, PROTOCOL_SOURCE as y, isProtocolMessage as N } from "../lib/protocol.js";
|
|
2
|
+
const O = 8e3, P = 8e3, M = 900 * 1e3;
|
|
3
|
+
let I = null, v = "*";
|
|
4
|
+
function R() {
|
|
5
|
+
try {
|
|
6
|
+
if (typeof document < "u" && document.referrer)
|
|
7
|
+
return new URL(document.referrer).origin;
|
|
8
|
+
} catch {
|
|
9
|
+
}
|
|
10
|
+
return "*";
|
|
11
|
+
}
|
|
12
|
+
async function J(t = {}) {
|
|
13
|
+
return I || (I = C(t)), await I;
|
|
14
|
+
}
|
|
15
|
+
async function C(t) {
|
|
16
|
+
const o = t.expectedOrigin ?? window.location.origin;
|
|
17
|
+
v = R();
|
|
18
|
+
const s = E(window.location.hash);
|
|
19
|
+
if (A(), !s)
|
|
20
|
+
throw new Error(
|
|
21
|
+
"[reachy-mini-sdk/host/embed] no creds bundle found in URL hash. Was the embed mounted directly without ?embedded=1#creds=...?"
|
|
22
|
+
);
|
|
23
|
+
if (!await U(O))
|
|
24
|
+
throw new Error(
|
|
25
|
+
`[reachy-mini-sdk/host/embed] window.ReachyMini did not become available within ${O}ms - check the SDK CDN tag.`
|
|
26
|
+
);
|
|
27
|
+
const e = s.userName ?? s.username ?? null;
|
|
28
|
+
s.hfToken && e && D(s.hfToken, e);
|
|
29
|
+
const a = new window.ReachyMini({
|
|
30
|
+
appName: s.appName,
|
|
31
|
+
signalingUrl: s.signalingUrl,
|
|
32
|
+
...t.sdkOptions
|
|
33
|
+
}), c = H(o);
|
|
34
|
+
S({
|
|
35
|
+
source: y,
|
|
36
|
+
type: "embed:ready",
|
|
37
|
+
version: w
|
|
38
|
+
}), c.start();
|
|
39
|
+
const l = await c.awaitHostInit(P, s);
|
|
40
|
+
g("connecting", "link"), u("boot:link:start", { robotPeerId: l.robotPeerId }), await a.authenticate(), u("boot:authenticate:ok", { state: a.state }), await a.connect(), u("boot:connect:ok", {
|
|
41
|
+
state: a.state,
|
|
42
|
+
robots: (a.robots ?? []).length
|
|
43
|
+
}), g("connecting", "session"), u("boot:session:start", { robotPeerId: l.robotPeerId }), F(a);
|
|
44
|
+
try {
|
|
45
|
+
await a.startSession(l.robotPeerId), u("boot:session:ok");
|
|
46
|
+
} catch (d) {
|
|
47
|
+
throw u("boot:session:error", {
|
|
48
|
+
message: d?.message ?? String(d)
|
|
49
|
+
}), d;
|
|
50
|
+
}
|
|
51
|
+
return g("connecting", "wake"), u("boot:wake:start"), await a.ensureAwake(), u("boot:wake:ok"), c.attachPageHide(a), g("live", null), c.buildHandle(a, l);
|
|
52
|
+
}
|
|
53
|
+
function _(t) {
|
|
54
|
+
return {
|
|
55
|
+
theme: t.theme,
|
|
56
|
+
config: t.config,
|
|
57
|
+
appName: t.appName,
|
|
58
|
+
hostName: t.hostName,
|
|
59
|
+
userName: t.userName ?? null,
|
|
60
|
+
robotPeerId: t.robotPeerId
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
function L(t) {
|
|
64
|
+
return {
|
|
65
|
+
theme: t.theme,
|
|
66
|
+
config: t.config,
|
|
67
|
+
appName: t.appName,
|
|
68
|
+
hostName: t.hostName,
|
|
69
|
+
userName: t.userName ?? null,
|
|
70
|
+
robotPeerId: t.robotPeerId
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
function H(t) {
|
|
74
|
+
const o = /* @__PURE__ */ new Set(), s = /* @__PURE__ */ new Set(), f = /* @__PURE__ */ new Set();
|
|
75
|
+
let e = null, a = !1, c = !1, l = null;
|
|
76
|
+
function d(n) {
|
|
77
|
+
switch (n.type) {
|
|
78
|
+
case "host:init": {
|
|
79
|
+
e = L(n), s.forEach((r) => r(e.theme)), f.forEach((r) => r(e.config));
|
|
80
|
+
break;
|
|
81
|
+
}
|
|
82
|
+
case "host:theme-changed": {
|
|
83
|
+
e && (e.theme = n.theme), s.forEach((r) => r(n.theme));
|
|
84
|
+
break;
|
|
85
|
+
}
|
|
86
|
+
case "host:config-changed": {
|
|
87
|
+
e && (e.config = n.config), f.forEach((r) => r(n.config));
|
|
88
|
+
break;
|
|
89
|
+
}
|
|
90
|
+
case "host:leaving": {
|
|
91
|
+
i();
|
|
92
|
+
break;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function i() {
|
|
97
|
+
a || (a = !0, o.forEach((n) => {
|
|
98
|
+
try {
|
|
99
|
+
n();
|
|
100
|
+
} catch (r) {
|
|
101
|
+
console.warn("[reachy-mini-sdk/host/embed] onLeave threw", r);
|
|
102
|
+
}
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
return {
|
|
106
|
+
start() {
|
|
107
|
+
c || (c = !0, l = (n) => {
|
|
108
|
+
n.origin === t && N(n.data) && d(n.data);
|
|
109
|
+
}, window.addEventListener("message", l));
|
|
110
|
+
},
|
|
111
|
+
async awaitHostInit(n, r) {
|
|
112
|
+
return window.parent !== window ? e || new Promise((h) => {
|
|
113
|
+
const p = (k) => {
|
|
114
|
+
if (k.origin !== t || !N(k.data)) return;
|
|
115
|
+
const T = k.data;
|
|
116
|
+
T.type === "host:init" && (window.removeEventListener("message", p), window.clearTimeout(b), e = L(T), h(e));
|
|
117
|
+
}, b = window.setTimeout(() => {
|
|
118
|
+
window.removeEventListener("message", p), e || (e = _(r)), h(e);
|
|
119
|
+
}, n);
|
|
120
|
+
window.addEventListener("message", p);
|
|
121
|
+
}) : (e = _(r), e);
|
|
122
|
+
},
|
|
123
|
+
attachPageHide(n) {
|
|
124
|
+
const r = () => {
|
|
125
|
+
i();
|
|
126
|
+
try {
|
|
127
|
+
n.stopSession();
|
|
128
|
+
} catch {
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
window.addEventListener("pagehide", r, { once: !0 });
|
|
132
|
+
},
|
|
133
|
+
buildHandle(n, r) {
|
|
134
|
+
e = r;
|
|
135
|
+
const m = j(n);
|
|
136
|
+
return {
|
|
137
|
+
reachy: n,
|
|
138
|
+
media: m,
|
|
139
|
+
get theme() {
|
|
140
|
+
return e.theme;
|
|
141
|
+
},
|
|
142
|
+
get config() {
|
|
143
|
+
return e.config;
|
|
144
|
+
},
|
|
145
|
+
get appName() {
|
|
146
|
+
return e.appName;
|
|
147
|
+
},
|
|
148
|
+
get hostName() {
|
|
149
|
+
return e.hostName;
|
|
150
|
+
},
|
|
151
|
+
get userName() {
|
|
152
|
+
return e.userName;
|
|
153
|
+
},
|
|
154
|
+
onLeave(h) {
|
|
155
|
+
return o.add(h), () => o.delete(h);
|
|
156
|
+
},
|
|
157
|
+
onThemeChange(h) {
|
|
158
|
+
return s.add(h), () => s.delete(h);
|
|
159
|
+
},
|
|
160
|
+
onConfigChange(h) {
|
|
161
|
+
const p = (b) => h(b);
|
|
162
|
+
return f.add(p), () => f.delete(p);
|
|
163
|
+
},
|
|
164
|
+
setAppState(h) {
|
|
165
|
+
g(
|
|
166
|
+
h.phase,
|
|
167
|
+
h.connectingStep ?? null,
|
|
168
|
+
h.message ?? null
|
|
169
|
+
);
|
|
170
|
+
},
|
|
171
|
+
requestLeave() {
|
|
172
|
+
S({
|
|
173
|
+
source: y,
|
|
174
|
+
type: "embed:request-leave",
|
|
175
|
+
version: w
|
|
176
|
+
});
|
|
177
|
+
},
|
|
178
|
+
reportError(h, p) {
|
|
179
|
+
S({
|
|
180
|
+
source: y,
|
|
181
|
+
type: "embed:error",
|
|
182
|
+
version: w,
|
|
183
|
+
message: h,
|
|
184
|
+
fatal: p?.fatal === !0,
|
|
185
|
+
detail: p?.detail
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function A() {
|
|
193
|
+
try {
|
|
194
|
+
const t = window.location.pathname + window.location.search;
|
|
195
|
+
history.replaceState(history.state, document.title, t);
|
|
196
|
+
} catch {
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
function D(t, o) {
|
|
200
|
+
try {
|
|
201
|
+
sessionStorage.setItem("hf_token", t), sessionStorage.setItem("hf_username", o), sessionStorage.setItem(
|
|
202
|
+
"hf_token_expires",
|
|
203
|
+
new Date(Date.now() + M).toISOString()
|
|
204
|
+
);
|
|
205
|
+
} catch {
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function U(t) {
|
|
209
|
+
return new Promise((o) => {
|
|
210
|
+
if (typeof window > "u") {
|
|
211
|
+
o(!1);
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
214
|
+
if (window.ReachyMini) {
|
|
215
|
+
o(!0);
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
let s = !1;
|
|
219
|
+
const f = () => {
|
|
220
|
+
s || (s = !0, window.removeEventListener("reachymini:ready", f), window.clearTimeout(e), o(!!window.ReachyMini));
|
|
221
|
+
}, e = window.setTimeout(() => {
|
|
222
|
+
s || (s = !0, window.removeEventListener("reachymini:ready", f), o(!1));
|
|
223
|
+
}, t);
|
|
224
|
+
window.addEventListener("reachymini:ready", f);
|
|
225
|
+
});
|
|
226
|
+
}
|
|
227
|
+
function S(t) {
|
|
228
|
+
if (!(typeof window > "u"))
|
|
229
|
+
try {
|
|
230
|
+
window.parent.postMessage(t, v);
|
|
231
|
+
} catch (o) {
|
|
232
|
+
console.warn("[reachy-mini-sdk/host/embed] postMessage to host failed", o);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
function g(t, o, s = null) {
|
|
236
|
+
S({
|
|
237
|
+
source: y,
|
|
238
|
+
type: "embed:app-state",
|
|
239
|
+
version: w,
|
|
240
|
+
phase: t,
|
|
241
|
+
connectingStep: o,
|
|
242
|
+
message: s
|
|
243
|
+
});
|
|
244
|
+
}
|
|
245
|
+
function u(t, o = {}) {
|
|
246
|
+
if (!(typeof window > "u")) {
|
|
247
|
+
try {
|
|
248
|
+
window.parent.postMessage(
|
|
249
|
+
{
|
|
250
|
+
source: y,
|
|
251
|
+
type: "embed:debug",
|
|
252
|
+
version: w,
|
|
253
|
+
tag: t,
|
|
254
|
+
payload: o
|
|
255
|
+
},
|
|
256
|
+
v
|
|
257
|
+
);
|
|
258
|
+
} catch {
|
|
259
|
+
}
|
|
260
|
+
try {
|
|
261
|
+
let s = "";
|
|
262
|
+
try {
|
|
263
|
+
s = JSON.stringify(o);
|
|
264
|
+
} catch {
|
|
265
|
+
s = "<unserializable>";
|
|
266
|
+
}
|
|
267
|
+
console.info(`[embed-debug] ${t} ${s}`);
|
|
268
|
+
} catch {
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
function j(t) {
|
|
273
|
+
let o = null;
|
|
274
|
+
const s = t, f = () => {
|
|
275
|
+
const c = s._pc;
|
|
276
|
+
if (!c) return null;
|
|
277
|
+
const l = c.getReceivers().map((d) => d.track).filter(
|
|
278
|
+
(d) => d !== null && d.kind !== "" && d.readyState === "live"
|
|
279
|
+
);
|
|
280
|
+
return l.length === 0 ? null : new MediaStream(l);
|
|
281
|
+
}, e = () => o || (o = f(), o && u("media:cache:init", {
|
|
282
|
+
videoTracks: o.getVideoTracks().length,
|
|
283
|
+
audioTracks: o.getAudioTracks().length
|
|
284
|
+
}), o), a = () => {
|
|
285
|
+
o && (o = null, u("media:cache:clear"));
|
|
286
|
+
};
|
|
287
|
+
return t.addEventListener("sessionStopped", a), {
|
|
288
|
+
attachVideo(c) {
|
|
289
|
+
const l = t.attachVideo(c), d = e();
|
|
290
|
+
if (d && c.srcObject !== d)
|
|
291
|
+
try {
|
|
292
|
+
c.srcObject = d, c.play().catch(() => {
|
|
293
|
+
}), u("media:attach:replay", {
|
|
294
|
+
videoTracks: d.getVideoTracks().length,
|
|
295
|
+
audioTracks: d.getAudioTracks().length
|
|
296
|
+
});
|
|
297
|
+
} catch (i) {
|
|
298
|
+
u("media:attach:replay:error", {
|
|
299
|
+
message: i?.message ?? String(i)
|
|
300
|
+
});
|
|
301
|
+
}
|
|
302
|
+
return l;
|
|
303
|
+
},
|
|
304
|
+
get robotStream() {
|
|
305
|
+
return e();
|
|
306
|
+
},
|
|
307
|
+
get micStream() {
|
|
308
|
+
return s._micStream;
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function F(t) {
|
|
313
|
+
const o = [
|
|
314
|
+
"connected",
|
|
315
|
+
"disconnected",
|
|
316
|
+
"streaming",
|
|
317
|
+
"sessionStopped",
|
|
318
|
+
"sessionRejected",
|
|
319
|
+
"robotsChanged",
|
|
320
|
+
"error",
|
|
321
|
+
"state",
|
|
322
|
+
"log",
|
|
323
|
+
"message"
|
|
324
|
+
];
|
|
325
|
+
for (const e of o)
|
|
326
|
+
try {
|
|
327
|
+
t.addEventListener(e, (a) => {
|
|
328
|
+
let c = {};
|
|
329
|
+
const l = a;
|
|
330
|
+
if (l && typeof l == "object" && "detail" in l)
|
|
331
|
+
try {
|
|
332
|
+
c = JSON.parse(JSON.stringify(l.detail ?? null));
|
|
333
|
+
} catch {
|
|
334
|
+
c = { _unserializable: !0 };
|
|
335
|
+
}
|
|
336
|
+
u(`sdk:${e}`, c);
|
|
337
|
+
});
|
|
338
|
+
} catch {
|
|
339
|
+
}
|
|
340
|
+
try {
|
|
341
|
+
const e = t, a = e._handleSignalingMessage;
|
|
342
|
+
if (typeof a == "function") {
|
|
343
|
+
const c = e._sendToServer;
|
|
344
|
+
typeof c == "function" && (e._sendToServer = async function(d) {
|
|
345
|
+
const i = d, n = { type: i?.type ?? "?" };
|
|
346
|
+
if (i && "peerId" in i && (n.peerId = String(i.peerId)), i && "sessionId" in i && (n.sessionId = String(i.sessionId)), i && "sdp" in i) {
|
|
347
|
+
const r = i.sdp;
|
|
348
|
+
n.sdpType = r?.type ?? "?", n.sdpLen = r?.sdp?.length ?? 0;
|
|
349
|
+
}
|
|
350
|
+
if (i && "ice" in i) {
|
|
351
|
+
const r = i.ice;
|
|
352
|
+
n.iceCand = (r?.candidate ?? "").slice(0, 60) || "<end-of-candidates>";
|
|
353
|
+
}
|
|
354
|
+
u("sdk:send", n);
|
|
355
|
+
try {
|
|
356
|
+
const r = await c.call(this, d), m = r;
|
|
357
|
+
return u("sdk:send:res", {
|
|
358
|
+
inFor: n.type,
|
|
359
|
+
resType: m?.type ?? null,
|
|
360
|
+
keys: m ? Object.keys(m) : []
|
|
361
|
+
}), r;
|
|
362
|
+
} catch (r) {
|
|
363
|
+
throw u("sdk:send:err", {
|
|
364
|
+
inFor: n.type,
|
|
365
|
+
msg: r?.message ?? String(r)
|
|
366
|
+
}), r;
|
|
367
|
+
}
|
|
368
|
+
}), e._handleSignalingMessage = function(d) {
|
|
369
|
+
const i = d, n = { type: i?.type ?? "?" };
|
|
370
|
+
if ("sessionId" in i && (n.sessionId = String(i.sessionId)), "peerId" in i && (n.peerId = String(i.peerId)), "sdp" in i) {
|
|
371
|
+
const r = i.sdp;
|
|
372
|
+
n.sdpType = r?.type ?? "?", n.sdpLen = r?.sdp?.length ?? 0;
|
|
373
|
+
}
|
|
374
|
+
if ("ice" in i) {
|
|
375
|
+
const r = i.ice;
|
|
376
|
+
n.iceCand = (r?.candidate ?? "").slice(0, 60) || "<end-of-candidates>";
|
|
377
|
+
}
|
|
378
|
+
return "reason" in i && (n.reason = String(i.reason)), u("sdk:sse", n), a.call(this, d);
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
} catch {
|
|
382
|
+
}
|
|
383
|
+
const s = Date.now(), f = window.setInterval(() => {
|
|
384
|
+
const e = t, a = e._pc, c = e._dc;
|
|
385
|
+
u("sdk:probe", {
|
|
386
|
+
elapsedMs: Date.now() - s,
|
|
387
|
+
myPeerId: e._peerId ?? null,
|
|
388
|
+
state: e._state ?? null,
|
|
389
|
+
sseAborted: e._sseAbortController?.signal?.aborted ?? null,
|
|
390
|
+
pcState: a?.connectionState ?? null,
|
|
391
|
+
iceState: a?.iceConnectionState ?? null,
|
|
392
|
+
iceGather: a?.iceGatheringState ?? null,
|
|
393
|
+
signalingState: a?.signalingState ?? null,
|
|
394
|
+
dcState: c?.readyState ?? null,
|
|
395
|
+
sessionId: e._sessionId ?? null
|
|
396
|
+
}), Date.now() - s > 3e4 && window.clearInterval(f);
|
|
397
|
+
}, 1500);
|
|
398
|
+
}
|
|
399
|
+
export {
|
|
400
|
+
J as c
|
|
401
|
+
};
|
|
402
|
+
//# sourceMappingURL=index-C3B3FuBC.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index-C3B3FuBC.js","sources":["../../src/embed/index.ts"],"sourcesContent":["/**\n * Embedded-app client.\n *\n * Vanilla TypeScript helper that lives in the iframe side of the\n * host / app split. Consumed by `src/embed.{ts,tsx}` in each app\n * (or via the CDN entry\n * `@pollen-robotics/reachy-mini-sdk/host/embed` script tag).\n *\n * import { connectToHost } from '@pollen-robotics/reachy-mini-sdk/host/embed';\n *\n * const handle = await connectToHost<MyAppConfig>();\n * handle.onLeave(() => { /* clean up before unmount *\\/ });\n * handle.reachy.setHeadRpyDeg(0, 10, 0);\n *\n * Boot sequence (canonical reference: SPEC.md §6.4):\n * 1. Read `#creds=<base64>` synchronously and wipe the hash\n * with `history.replaceState`.\n * 2. Wait for `window.ReachyMini` (8 s timeout).\n * 3. Instantiate the SDK, seed the HF token into\n * `sessionStorage`.\n * 4. Send `embed:ready` to the parent.\n * 5. Wait for `host:init` (8 s timeout; Mode B proceeds from\n * hash alone when this never arrives).\n * 6. `connect()` → `startSession()` → `ensureAwake()`, emitting\n * `embed:app-state` at each step.\n * 7. Resolve `connectToHost()` with the live SDK handle.\n *\n * Strict Mode safety (SPEC §8.4): the function is idempotent\n * across multiple awaits via a module-level promise. Calling\n * `connectToHost()` twice returns the same in-flight promise;\n * a single SDK instance is created, a single `embed:ready` is\n * posted.\n */\nimport type {\n ReachyMiniInstance,\n ReachyMiniOptions,\n} from '../lib/sdk-types';\nimport {\n PROTOCOL_SOURCE,\n PROTOCOL_VERSION,\n decodeCredsFromHash,\n isProtocolMessage,\n} from '../lib/protocol';\nimport type {\n AppConnectingStep,\n AppPhase,\n ConfigPayload,\n CredsBundle,\n EmbedToHostMsg,\n HostInitMsg,\n HostToEmbedMsg,\n ThemeMode,\n} from '../lib/protocol';\n\nconst SDK_READY_TIMEOUT_MS = 8000;\nconst HOST_INIT_TIMEOUT_MS = 8000;\nconst TOKEN_TTL_MS = 15 * 60 * 1000;\n\n/**\n * Stable surface for the robot's WebRTC media. All accessors are\n * synchronous and safe to read at any point after the handle is\n * returned by `connectToHost()`. References are cached: calling\n * `media.robotStream` repeatedly returns the same `MediaStream`\n * instance (good for React effect deps), and the stream auto-clears\n * on `sessionStopped`.\n */\nexport interface RobotMedia {\n /**\n * Bind the robot's video element. Internally:\n * 1. Calls the SDK's own `attachVideo()` (which keeps the\n * element's `muted` flag in sync with `audioMuted`, kicks\n * off the latency monitor, and resets `srcObject` on\n * `sessionStopped`).\n * 2. Replays the cached `robotStream` so a late-mounting\n * `<video>` element catches up immediately.\n *\n * Returns a cleanup function that detaches the SDK listeners.\n */\n attachVideo(el: HTMLVideoElement): () => void;\n /**\n * Robot's outbound MediaStream (video + audio in a single\n * stream, as the daemon emits it). `null` until the WebRTC\n * tracks have arrived; cleared on `sessionStopped`.\n *\n * Hand it to `<video>.srcObject`, an `AudioContext`'s\n * `createMediaStreamSource`, or any other consumer.\n */\n readonly robotStream: MediaStream | null;\n /**\n * Local microphone MediaStream. Mirrors `reachy._micStream` so\n * apps don't reach into underscore-prefixed SDK internals.\n * `null` when `enableMicrophone` was `false` at construction\n * time, when the daemon refused bidirectional audio, or when\n * the session has stopped.\n */\n readonly micStream: MediaStream | null;\n}\n\n/** Resolved state at the moment `connectToHost()` returns. */\nexport interface ConnectedHandle<TConfig = unknown> {\n /** Live SDK instance: connected, session started, robot awake. */\n reachy: ReachyMiniInstance;\n /** Current theme; updated via `onThemeChange`. */\n theme: ThemeMode;\n /** Initial config (from URL `?config=` or mobile handoff).\n * Updates pushed via `onConfigChange`. */\n config: TConfig | null;\n /** App display name as passed by the host. */\n appName: string;\n /** Host display name (e.g. \"Reachy Mini\"). */\n hostName: string;\n /** HF user name when known (from `host:init`). */\n userName: string | null;\n\n /**\n * Stable accessors for the WebRTC media streams negotiated\n * during `startSession()`.\n *\n * Why apps must use these (not `reachy.attachVideo` /\n * `reachy._pc` / `reachy._micStream`):\n * `connectToHost()` fully completes the WebRTC handshake\n * before the embedded app's React tree mounts. The SDK's\n * one-shot `videoTrack` event and the underlying `pc.ontrack`\n * event have therefore ALREADY fired by the time a\n * freshly-mounted component subscribes - any listener\n * registered after `connectToHost()` resolves will sit silent\n * until the next `startSession()`, which embeds never\n * trigger. This API replays the streams from a synchronous\n * snapshot of the peer-connection's receivers, so\n * late-mounting consumers see the camera + audio immediately.\n *\n * For the data channel, mute toggles, motor commands and state\n * updates there is no race: the bridge only resolves once ICE\n * AND the data channel are connected, and state events stream\n * continuously at 50 Hz from the daemon. Apps can keep using\n * `reachy.setHeadRpyDeg(…)`, `reachy.setMicMuted(…)`,\n * `reachy.addEventListener('state', …)` directly.\n */\n readonly media: RobotMedia;\n\n /** Register a teardown callback. Fires on `host:leaving`\n * (one-shot) or `pagehide`. Return a promise to keep the host\n * waiting (bounded by the host's `timeoutMs`). Returns an\n * unsubscribe function. */\n onLeave(cb: () => void | Promise<void>): () => void;\n /** Register a theme-change handler. */\n onThemeChange(cb: (theme: ThemeMode) => void): () => void;\n /** Register a config-change handler. */\n onConfigChange(cb: (config: TConfig | null) => void): () => void;\n\n /** Push an app-level state update upstream so the host can\n * drive its ConnectingView overlay. */\n setAppState(state: {\n phase: AppPhase;\n connectingStep?: AppConnectingStep | null;\n message?: string | null;\n }): void;\n /** Ask the host to start the leave sequence. */\n requestLeave(): void;\n /** Report an error. `fatal: true` switches the host to ErrorView. */\n reportError(\n message: string,\n opts?: { fatal?: boolean; detail?: unknown },\n ): void;\n}\n\nexport interface ConnectToHostOptions {\n /** Forwarded to the SDK constructor. `appName`, `signalingUrl`,\n * `clientId` are auto-set from the creds bundle. */\n sdkOptions?: Partial<ReachyMiniOptions>;\n /** Origin of the host's window. Defaults to\n * `window.location.origin` (same-origin iframe). */\n expectedOrigin?: string;\n}\n\n/* ─────────────────── Module-level idempotency ─────────────────── */\n\nlet bootPromise: Promise<ConnectedHandle<unknown>> | null = null;\n\n/**\n * Target origin used by every outgoing `postMessage`. In Mode A\n * (host shell same-origin) this equals `window.location.origin`,\n * which is the strict default. In Mode B (mobile WebView at a\n * different origin like `http://localhost:1422`) we infer the\n * parent's origin from `document.referrer` at boot and fall back\n * to `'*'` if even that is empty.\n *\n * Outgoing messages carry no secrets (the HF token lives in the\n * URL hash, never in postMessage payloads), so `'*'` is safe as a\n * last-resort target for diagnostics + lifecycle pings.\n */\nlet parentTargetOrigin: string = '*';\n\nfunction detectParentOrigin(): string {\n try {\n if (typeof document !== 'undefined' && document.referrer) {\n return new URL(document.referrer).origin;\n }\n } catch {\n /* ignore malformed referrer */\n }\n return '*';\n}\n\n/** Boot the embedded app. Idempotent: calling twice returns the\n * same in-flight promise. */\nexport async function connectToHost<TConfig = unknown>(\n options: ConnectToHostOptions = {},\n): Promise<ConnectedHandle<TConfig>> {\n if (!bootPromise) {\n bootPromise = bootOnce(options) as Promise<ConnectedHandle<unknown>>;\n }\n return (await bootPromise) as ConnectedHandle<TConfig>;\n}\n\n/* ─────────────────── Boot pipeline ─────────────────── */\n\nasync function bootOnce(\n options: ConnectToHostOptions,\n): Promise<ConnectedHandle<unknown>> {\n const expectedOrigin = options.expectedOrigin ?? window.location.origin;\n // Pin the parent origin once, before any outgoing postMessage so\n // diagnostic events reach a cross-origin mobile shell instead of\n // being dropped by the browser. See `parentTargetOrigin` above.\n parentTargetOrigin = detectParentOrigin();\n\n // 1. Parse creds from the URL hash and wipe it synchronously.\n const creds = decodeCredsFromHash(window.location.hash);\n wipeUrlHash();\n\n if (!creds) {\n throw new Error(\n '[reachy-mini-sdk/host/embed] no creds bundle found in URL hash. ' +\n 'Was the embed mounted directly without ?embedded=1#creds=...?',\n );\n }\n\n // 2. Wait for the SDK script to finish loading.\n const sdkReady = await waitForSdkReady(SDK_READY_TIMEOUT_MS);\n if (!sdkReady) {\n throw new Error(\n '[reachy-mini-sdk/host/embed] window.ReachyMini did not become ' +\n `available within ${SDK_READY_TIMEOUT_MS}ms - check the SDK CDN tag.`,\n );\n }\n\n // 3. Seed the HF token before SDK construction so authenticate()\n // resolves without a redirect. Lenient on the user-name key:\n // the canonical schema is `userName` (camelCase) but earlier\n // mobile builds wrote `username` (lowercase). Accept both so\n // a stale shell in the wild keeps working; the `CredsBundle`\n // interface stays strict on the writing side as the single\n // source of truth.\n const credsUserName =\n creds.userName ??\n ((creds as unknown as { username?: string | null }).username ?? null);\n if (creds.hfToken && credsUserName) {\n seedSessionToken(creds.hfToken, credsUserName);\n }\n\n // 4. Build the SDK with the bundled signaling URL + appName.\n const sdk: ReachyMiniInstance = new window.ReachyMini({\n appName: creds.appName,\n signalingUrl: creds.signalingUrl,\n ...options.sdkOptions,\n });\n\n // 5. Build the bridge (subscriber registry) + post ready.\n const bridge = createBridge(expectedOrigin);\n postToHost({\n source: PROTOCOL_SOURCE,\n type: 'embed:ready',\n version: PROTOCOL_VERSION,\n });\n bridge.start();\n\n // 6. Optional host:init wait. In Mode B (mobile) the parent\n // never sends this and we proceed with the hash bundle alone.\n const live = await bridge.awaitHostInit(HOST_INIT_TIMEOUT_MS, creds);\n\n // 7. Sequence: connect → startSession → ensureAwake.\n pushAppState('connecting', 'link');\n postDebug('boot:link:start', { robotPeerId: live.robotPeerId });\n await sdk.authenticate();\n postDebug('boot:authenticate:ok', { state: (sdk as { state?: string }).state });\n await sdk.connect();\n postDebug('boot:connect:ok', {\n state: (sdk as { state?: string }).state,\n robots: ((sdk as { robots?: unknown[] }).robots ?? []).length,\n });\n\n pushAppState('connecting', 'session');\n postDebug('boot:session:start', { robotPeerId: live.robotPeerId });\n installSdkProbe(sdk);\n try {\n await sdk.startSession(live.robotPeerId);\n postDebug('boot:session:ok');\n } catch (err) {\n postDebug('boot:session:error', {\n message: (err as Error)?.message ?? String(err),\n });\n throw err;\n }\n\n pushAppState('connecting', 'wake');\n postDebug('boot:wake:start');\n await sdk.ensureAwake();\n postDebug('boot:wake:ok');\n\n // 8. We're live. Wire pagehide cleanup so the SDK releases the\n // robot if the browser kills the tab.\n bridge.attachPageHide(sdk);\n pushAppState('live', null);\n\n return bridge.buildHandle<unknown>(sdk, live);\n}\n\n/* ─────────────────── Bridge state ─────────────────── */\n\ninterface LiveState {\n theme: ThemeMode;\n config: ConfigPayload;\n appName: string;\n hostName: string;\n userName: string | null;\n robotPeerId: string;\n}\n\nfunction liveStateFromCreds(creds: CredsBundle): LiveState {\n return {\n theme: creds.theme,\n config: creds.config,\n appName: creds.appName,\n hostName: creds.hostName,\n userName: creds.userName ?? null,\n robotPeerId: creds.robotPeerId,\n };\n}\n\nfunction liveStateFromInit(msg: HostInitMsg): LiveState {\n return {\n theme: msg.theme,\n config: msg.config,\n appName: msg.appName,\n hostName: msg.hostName,\n userName: msg.userName ?? null,\n robotPeerId: msg.robotPeerId,\n };\n}\n\nfunction createBridge(expectedOrigin: string) {\n type LeaveCb = () => void | Promise<void>;\n type ThemeCb = (t: ThemeMode) => void;\n type ConfigCb = (c: unknown) => void;\n\n const leaveListeners = new Set<LeaveCb>();\n const themeListeners = new Set<ThemeCb>();\n const configListeners = new Set<ConfigCb>();\n\n let current: LiveState | null = null;\n let leaveTriggered = false;\n\n // Listener installed lazily so `embed:ready` is the only\n // outgoing event before the host has time to respond.\n let started = false;\n let onMessage: ((event: MessageEvent) => void) | null = null;\n\n function dispatchMessage(msg: HostToEmbedMsg): void {\n switch (msg.type) {\n case 'host:init': {\n current = liveStateFromInit(msg);\n // Re-notify subscribers in case the init arrives after\n // they registered (shouldn't happen with the current\n // boot order but cheap defensive code).\n themeListeners.forEach((cb) => cb(current!.theme));\n configListeners.forEach((cb) => cb(current!.config));\n break;\n }\n case 'host:theme-changed': {\n if (current) current.theme = msg.theme;\n themeListeners.forEach((cb) => cb(msg.theme));\n break;\n }\n case 'host:config-changed': {\n if (current) current.config = msg.config;\n configListeners.forEach((cb) => cb(msg.config));\n break;\n }\n case 'host:leaving': {\n runLeaveOnce();\n break;\n }\n }\n }\n\n function runLeaveOnce(): void {\n if (leaveTriggered) return;\n leaveTriggered = true;\n // Fire and forget; the host doesn't wait for an ack, it just\n // unmounts the iframe after `timeoutMs`.\n leaveListeners.forEach((cb) => {\n try {\n void cb();\n } catch (err) {\n console.warn('[reachy-mini-sdk/host/embed] onLeave threw', err);\n }\n });\n }\n\n return {\n start(): void {\n if (started) return;\n started = true;\n onMessage = (event: MessageEvent) => {\n if (event.origin !== expectedOrigin) return;\n if (!isProtocolMessage(event.data)) return;\n dispatchMessage(event.data as HostToEmbedMsg);\n };\n window.addEventListener('message', onMessage);\n },\n\n async awaitHostInit(\n timeoutMs: number,\n fallbackCreds: CredsBundle,\n ): Promise<LiveState> {\n // Mode A path: we expect a host:init. Mode B path: parent\n // is `window` (no iframe), so no one will reply - fall\n // back to creds after the timeout.\n const isInIframe = window.parent !== window;\n if (!isInIframe) {\n current = liveStateFromCreds(fallbackCreds);\n return current;\n }\n\n // If host:init already arrived (race), use it.\n if (current) return current;\n\n return new Promise((resolve) => {\n const initListener = (event: MessageEvent): void => {\n if (event.origin !== expectedOrigin) return;\n if (!isProtocolMessage(event.data)) return;\n const data = event.data as HostToEmbedMsg;\n if (data.type !== 'host:init') return;\n window.removeEventListener('message', initListener);\n window.clearTimeout(timer);\n current = liveStateFromInit(data);\n resolve(current);\n };\n const timer = window.setTimeout(() => {\n window.removeEventListener('message', initListener);\n // Timeout: fall back to creds. Useful when the parent\n // never sends init (older host versions, manual\n // testing).\n if (!current) current = liveStateFromCreds(fallbackCreds);\n resolve(current);\n }, timeoutMs);\n window.addEventListener('message', initListener);\n });\n },\n\n attachPageHide(sdk: ReachyMiniInstance): void {\n const onPageHide = (): void => {\n runLeaveOnce();\n try {\n void sdk.stopSession();\n } catch {\n /* ignore - tab is going away anyway */\n }\n };\n window.addEventListener('pagehide', onPageHide, { once: true });\n },\n\n buildHandle<TConfig>(\n sdk: ReachyMiniInstance,\n live: LiveState,\n ): ConnectedHandle<TConfig> {\n current = live;\n const media = createRobotMedia(sdk);\n return {\n reachy: sdk,\n media,\n get theme(): ThemeMode {\n return current!.theme;\n },\n get config(): TConfig | null {\n return current!.config as TConfig | null;\n },\n get appName(): string {\n return current!.appName;\n },\n get hostName(): string {\n return current!.hostName;\n },\n get userName(): string | null {\n return current!.userName;\n },\n onLeave(cb) {\n leaveListeners.add(cb);\n return () => leaveListeners.delete(cb);\n },\n onThemeChange(cb) {\n themeListeners.add(cb);\n return () => themeListeners.delete(cb);\n },\n onConfigChange(cb) {\n const wrapped = (c: unknown) => cb(c as TConfig | null);\n configListeners.add(wrapped);\n return () => configListeners.delete(wrapped);\n },\n setAppState(state) {\n pushAppState(\n state.phase,\n state.connectingStep ?? null,\n state.message ?? null,\n );\n },\n requestLeave() {\n postToHost({\n source: PROTOCOL_SOURCE,\n type: 'embed:request-leave',\n version: PROTOCOL_VERSION,\n });\n },\n reportError(message, opts) {\n postToHost({\n source: PROTOCOL_SOURCE,\n type: 'embed:error',\n version: PROTOCOL_VERSION,\n message,\n fatal: opts?.fatal === true,\n detail: opts?.detail,\n });\n },\n };\n },\n };\n}\n\n/* ─────────────────── Helpers ─────────────────── */\n\nfunction wipeUrlHash(): void {\n // Best-effort: replaceState fails on `file://` and a few exotic\n // schemes. We don't want to throw in the embed for that.\n try {\n const cleanUrl =\n window.location.pathname + window.location.search;\n history.replaceState(history.state, document.title, cleanUrl);\n } catch {\n /* ignore */\n }\n}\n\nfunction seedSessionToken(token: string, userName: string): void {\n try {\n sessionStorage.setItem('hf_token', token);\n sessionStorage.setItem('hf_username', userName);\n sessionStorage.setItem(\n 'hf_token_expires',\n new Date(Date.now() + TOKEN_TTL_MS).toISOString(),\n );\n } catch {\n /* ignore - private browsing / quota */\n }\n}\n\nfunction waitForSdkReady(timeoutMs: number): Promise<boolean> {\n return new Promise((resolve) => {\n if (typeof window === 'undefined') {\n resolve(false);\n return;\n }\n if (window.ReachyMini) {\n resolve(true);\n return;\n }\n let settled = false;\n const onReady = (): void => {\n if (settled) return;\n settled = true;\n window.removeEventListener('reachymini:ready', onReady);\n window.clearTimeout(timer);\n resolve(Boolean(window.ReachyMini));\n };\n const timer = window.setTimeout(() => {\n if (settled) return;\n settled = true;\n window.removeEventListener('reachymini:ready', onReady);\n resolve(false);\n }, timeoutMs);\n window.addEventListener('reachymini:ready', onReady);\n });\n}\n\nfunction postToHost(msg: EmbedToHostMsg): void {\n if (typeof window === 'undefined') return;\n // Mode B (mobile WebView) embeds us in an iframe at a DIFFERENT\n // origin than the parent shell. Sending to\n // `window.location.origin` then makes the browser drop the\n // message and warn \"Recipient has origin <X>\". `parentTargetOrigin`\n // is set once at boot to the parent's referrer-derived origin,\n // falling back to `'*'` (safe - payloads carry no secrets).\n try {\n window.parent.postMessage(msg, parentTargetOrigin);\n } catch (err) {\n console.warn('[reachy-mini-sdk/host/embed] postMessage to host failed', err);\n }\n}\n\nfunction pushAppState(\n phase: AppPhase,\n connectingStep: AppConnectingStep | null,\n message: string | null = null,\n): void {\n postToHost({\n source: PROTOCOL_SOURCE,\n type: 'embed:app-state',\n version: PROTOCOL_VERSION,\n phase,\n connectingStep,\n message,\n });\n}\n\n/**\n * Dev-only diagnostic channel. Forwards a tag + payload to the host\n * so the parent's console (visible to devtools and the Cursor MCP\n * browser) shows the embed's boot progression. The host's\n * `ReachyHostShell` listens for `embed:debug` and `console.info`s\n * the payload.\n */\nfunction postDebug(tag: string, payload: Record<string, unknown> = {}): void {\n if (typeof window === 'undefined') return;\n try {\n window.parent.postMessage(\n {\n source: PROTOCOL_SOURCE,\n type: 'embed:debug',\n version: PROTOCOL_VERSION,\n tag,\n payload,\n },\n parentTargetOrigin,\n );\n } catch {\n /* ignore */\n }\n try {\n let asJson = '';\n try {\n asJson = JSON.stringify(payload);\n } catch {\n asJson = '<unserializable>';\n }\n console.info(`[embed-debug] ${tag} ${asJson}`);\n } catch {\n /* ignore */\n }\n}\n\n/**\n * Build the `RobotMedia` surface for a freshly-resolved SDK\n * handle.\n *\n * Background\n * ──────────\n * `connectToHost()` only resolves once the SDK's `startSession()`\n * has completed - which means ICE is connected, the data channel\n * is open, AND `pc.ontrack` has already fired for every remote\n * track in the SDP answer. By the time the embedded React tree\n * mounts and a `<video>` element calls `attachVideo()`, the SDK's\n * one-shot `videoTrack` event has therefore already happened and\n * a freshly-registered listener will sit silent.\n *\n * Implementation\n * ──────────────\n * We avoid mutating the SDK (no monkey-patching of\n * `reachy.attachVideo`) and instead build a thin parallel API\n * around it:\n * - The `robotStream` is captured by reading\n * `sdk._pc.getReceivers()` lazily on first access. By then\n * ICE+DC are connected so every receiver has a live track.\n * The result is cached as a single stable `MediaStream`\n * instance - good for React `useEffect` deps.\n * - `attachVideo()` calls the SDK's own `attachVideo()` first\n * (so we keep its mute-sync, latency monitor and\n * `sessionStopped` cleanup) and then immediately replays the\n * cached stream into the element. Late-mounting consumers\n * therefore see the camera within one paint instead of\n * waiting forever.\n * - On `sessionStopped` we drop the cached stream so subsequent\n * reads return `null`. (`connectToHost()` is one-shot per\n * page load, so we do not currently rebuild the cache after\n * a stop / restart - apps tear down on session end.)\n *\n * `micStream` is just a delegating getter to `reachy._micStream`:\n * the SDK exposes it synchronously and there is no race - it is\n * acquired during `startSession()` before `connectToHost()`\n * resolves. We surface it on the handle so apps don't poke into\n * underscore-prefixed SDK internals.\n */\nfunction createRobotMedia(sdk: ReachyMiniInstance): RobotMedia {\n let cached: MediaStream | null = null;\n\n const sdkInternals = sdk as unknown as {\n _pc: RTCPeerConnection | null;\n _micStream: MediaStream | null;\n };\n\n const buildFromReceivers = (): MediaStream | null => {\n const pc = sdkInternals._pc;\n if (!pc) return null;\n const tracks = pc\n .getReceivers()\n .map((rcv) => rcv.track)\n .filter(\n (t): t is MediaStreamTrack =>\n t !== null && t.kind !== '' && t.readyState === 'live',\n );\n if (tracks.length === 0) return null;\n return new MediaStream(tracks);\n };\n\n const ensureCached = (): MediaStream | null => {\n if (cached) return cached;\n cached = buildFromReceivers();\n if (cached) {\n postDebug('media:cache:init', {\n videoTracks: cached.getVideoTracks().length,\n audioTracks: cached.getAudioTracks().length,\n });\n }\n return cached;\n };\n\n // Drop the cache as soon as the daemon tears down - keeps\n // `media.robotStream` honest if anything reads it after\n // `sessionStopped`.\n const onSessionStopped = (): void => {\n if (cached) {\n cached = null;\n postDebug('media:cache:clear');\n }\n };\n sdk.addEventListener('sessionStopped', onSessionStopped);\n\n return {\n attachVideo(el: HTMLVideoElement): () => void {\n const detach = sdk.attachVideo(el);\n const stream = ensureCached();\n if (stream && el.srcObject !== stream) {\n try {\n el.srcObject = stream;\n // Best-effort autoplay. Browsers gate this on a user\n // gesture; the host-side picker tap typically satisfies\n // it. Swallow the rejection so a Safari pre-gesture\n // mount never crashes the boot path.\n void el.play().catch(() => {\n /* ignore */\n });\n postDebug('media:attach:replay', {\n videoTracks: stream.getVideoTracks().length,\n audioTracks: stream.getAudioTracks().length,\n });\n } catch (err) {\n postDebug('media:attach:replay:error', {\n message: (err as Error)?.message ?? String(err),\n });\n }\n }\n return detach;\n },\n get robotStream(): MediaStream | null {\n return ensureCached();\n },\n get micStream(): MediaStream | null {\n return sdkInternals._micStream;\n },\n };\n}\n\n/**\n * One-shot SDK probe used while we hunt the \"stuck at session\" bug.\n * Subscribes to every internal event the SDK is known to emit and\n * forwards them to the host via `embed:debug`. No-op in production\n * once the bug is fixed.\n */\nfunction installSdkProbe(sdk: ReachyMiniInstance): void {\n const events = [\n 'connected',\n 'disconnected',\n 'streaming',\n 'sessionStopped',\n 'sessionRejected',\n 'robotsChanged',\n 'error',\n 'state',\n 'log',\n 'message',\n ];\n for (const ev of events) {\n try {\n (sdk as unknown as {\n addEventListener: (n: string, cb: (e: unknown) => void) => void;\n }).addEventListener(ev, (e: unknown) => {\n let detail: Record<string, unknown> = {};\n const evObj = e as { detail?: unknown };\n if (evObj && typeof evObj === 'object' && 'detail' in evObj) {\n try {\n detail = JSON.parse(JSON.stringify(evObj.detail ?? null));\n } catch {\n detail = { _unserializable: true };\n }\n }\n postDebug(`sdk:${ev}`, detail);\n });\n } catch {\n /* ignore */\n }\n }\n // Wrap _handleSignalingMessage so we see every payload central\n // delivers via SSE (peer offers, ICE candidates, sessionRejected,\n // etc.). If we never see a `peer` message of kind `sdp/offer`\n // here, central is dropping the offer or routing it to a stale\n // peer.\n try {\n const sdkAny = sdk as unknown as {\n _handleSignalingMessage?: (msg: unknown) => unknown;\n };\n const orig = sdkAny._handleSignalingMessage;\n if (typeof orig === 'function') {\n const sendOrig = (sdkAny as Record<string, unknown>)._sendToServer as\n | ((this: unknown, payload: unknown) => Promise<unknown>)\n | undefined;\n if (typeof sendOrig === 'function') {\n (sdkAny as Record<string, unknown>)._sendToServer =\n async function patchedSend(this: unknown, payload: unknown) {\n const p = payload as Record<string, unknown>;\n const dbg: Record<string, unknown> = { type: p?.type ?? '?' };\n if (p && 'peerId' in p) dbg.peerId = String(p.peerId);\n if (p && 'sessionId' in p) dbg.sessionId = String(p.sessionId);\n if (p && 'sdp' in p) {\n const sdp = p.sdp as { type?: string; sdp?: string } | undefined;\n dbg.sdpType = sdp?.type ?? '?';\n dbg.sdpLen = sdp?.sdp?.length ?? 0;\n }\n if (p && 'ice' in p) {\n const ice = p.ice as { candidate?: string } | undefined;\n dbg.iceCand =\n (ice?.candidate ?? '').slice(0, 60) || '<end-of-candidates>';\n }\n postDebug('sdk:send', dbg);\n try {\n const res = await sendOrig.call(this, payload);\n const rj = res as Record<string, unknown> | undefined;\n postDebug('sdk:send:res', {\n inFor: dbg.type,\n resType: rj?.type ?? null,\n keys: rj ? Object.keys(rj) : [],\n });\n return res;\n } catch (err) {\n postDebug('sdk:send:err', {\n inFor: dbg.type,\n msg: (err as Error)?.message ?? String(err),\n });\n throw err;\n }\n };\n }\n sdkAny._handleSignalingMessage = function patched(msg: unknown) {\n const m = msg as Record<string, unknown>;\n const payload: Record<string, unknown> = { type: m?.type ?? '?' };\n if ('sessionId' in m) payload.sessionId = String(m.sessionId);\n if ('peerId' in m) payload.peerId = String(m.peerId);\n if ('sdp' in m) {\n const sdp = m.sdp as { type?: string; sdp?: string } | undefined;\n payload.sdpType = sdp?.type ?? '?';\n payload.sdpLen = sdp?.sdp?.length ?? 0;\n }\n if ('ice' in m) {\n const ice = m.ice as { candidate?: string } | undefined;\n payload.iceCand =\n (ice?.candidate ?? '').slice(0, 60) || '<end-of-candidates>';\n }\n if ('reason' in m) payload.reason = String(m.reason);\n postDebug('sdk:sse', payload);\n return orig.call(this, msg);\n };\n }\n } catch {\n /* ignore */\n }\n const probeStart = Date.now();\n const interval = window.setInterval(() => {\n const sdkAny = sdk as unknown as {\n _pc?: RTCPeerConnection;\n _dc?: RTCDataChannel;\n _sessionId?: string;\n _peerId?: string;\n _state?: string;\n _sseAbortController?: { signal?: { aborted?: boolean } };\n };\n const pc = sdkAny._pc;\n const dc = sdkAny._dc;\n postDebug('sdk:probe', {\n elapsedMs: Date.now() - probeStart,\n myPeerId: sdkAny._peerId ?? null,\n state: sdkAny._state ?? null,\n sseAborted: sdkAny._sseAbortController?.signal?.aborted ?? null,\n pcState: pc?.connectionState ?? null,\n iceState: pc?.iceConnectionState ?? null,\n iceGather: pc?.iceGatheringState ?? null,\n signalingState: pc?.signalingState ?? null,\n dcState: dc?.readyState ?? null,\n sessionId: sdkAny._sessionId ?? null,\n });\n if (Date.now() - probeStart > 30_000) window.clearInterval(interval);\n }, 1500);\n}\n"],"names":["SDK_READY_TIMEOUT_MS","HOST_INIT_TIMEOUT_MS","TOKEN_TTL_MS","bootPromise","parentTargetOrigin","detectParentOrigin","connectToHost","options","bootOnce","expectedOrigin","creds","decodeCredsFromHash","wipeUrlHash","waitForSdkReady","credsUserName","seedSessionToken","sdk","bridge","createBridge","postToHost","PROTOCOL_SOURCE","PROTOCOL_VERSION","live","pushAppState","postDebug","installSdkProbe","err","liveStateFromCreds","liveStateFromInit","msg","leaveListeners","themeListeners","configListeners","current","leaveTriggered","started","onMessage","dispatchMessage","cb","runLeaveOnce","event","isProtocolMessage","timeoutMs","fallbackCreds","resolve","initListener","data","timer","onPageHide","media","createRobotMedia","wrapped","c","state","message","opts","cleanUrl","token","userName","settled","onReady","phase","connectingStep","tag","payload","asJson","cached","sdkInternals","buildFromReceivers","pc","tracks","rcv","t","ensureCached","onSessionStopped","el","detach","stream","events","ev","e","detail","evObj","sdkAny","orig","sendOrig","p","dbg","sdp","ice","res","rj","m","probeStart","interval","dc"],"mappings":"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"}
|