@pollen-robotics/reachy-mini-sdk 1.10.0-dev.0.main.828a720 → 1.10.0-dev.0.main.9f78da7

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.
@@ -23,6 +23,9 @@ export declare class ReachyMini extends EventTarget implements ReachyMiniInstanc
23
23
  private _state;
24
24
  private _robots;
25
25
  private _robotState;
26
+ private _lastPoseSeq;
27
+ private _lastPoseFrameAt;
28
+ private _poseSubRefs;
26
29
  private readonly _preselectedRobotId;
27
30
  private _token;
28
31
  private _username;
@@ -43,7 +46,10 @@ export declare class ReachyMini extends EventTarget implements ReachyMiniInstanc
43
46
  private _hardwareIdResolve;
44
47
  private _volumeResolve;
45
48
  private _micVolumeResolve;
49
+ private _firstWakeUpResolve;
46
50
  private _trackedFaceResolve;
51
+ private _robotNameResolve;
52
+ private _deleteHfTokenResolve;
47
53
  private _applyAudioConfigResolve;
48
54
  private _readAudioParameterResolve;
49
55
  private readonly _logSubscribers;
@@ -238,6 +244,50 @@ export declare class ReachyMini extends EventTarget implements ReachyMiniInstanc
238
244
  setVolume(volume: number): Promise<number | null>;
239
245
  getMicrophoneVolume(): Promise<number | null>;
240
246
  setMicrophoneVolume(volume: number): Promise<number | null>;
247
+ /**
248
+ * Query whether the first wake-up setup wizard has been completed.
249
+ * Robot-wide, persisted on the robot. Resolves `false` when pending,
250
+ * `true` when done, or `null` when the channel isn't open / the daemon
251
+ * predates the `get_first_wake_up` command (callers should fail-open
252
+ * and skip the wizard on `null`).
253
+ */
254
+ getFirstWakeUp(): Promise<boolean | null>;
255
+ /**
256
+ * Persist the first wake-up wizard completion flag on the robot.
257
+ * Resolves with the stored value (or `null` on channel-closed).
258
+ */
259
+ setFirstWakeUp(isCompleted: boolean): Promise<boolean | null>;
260
+ /**
261
+ * Query the persisted robot display name. Resolves the stored name,
262
+ * `null` when none is set / the channel isn't open / the daemon predates
263
+ * the `get_robot_name` command.
264
+ */
265
+ getRobotName(): Promise<string | null>;
266
+ /**
267
+ * Set and persist the robot display name on the robot. Resolves with the
268
+ * stored (trimmed) name, or `null` on error / channel-closed. Applied live
269
+ * by the daemon (status + central relay + mDNS), so it takes effect right
270
+ * away without a restart; the persisted name also overrides --robot-name
271
+ * on the next start.
272
+ */
273
+ setRobotName(name: string): Promise<string | null>;
274
+ /**
275
+ * Sign this robot out of Hugging Face: asks the daemon to delete its
276
+ * stored HF token, which de-registers the robot from the central
277
+ * signaling relay (it disappears from its owner's robot list until it
278
+ * is set up again). Works over the WebRTC data channel, so it reaches
279
+ * the robot remotely (no LAN HTTP path required).
280
+ *
281
+ * Resolves `true` when the daemon acked success, `false` on a daemon
282
+ * error, or `null` when no ack arrives before the timeout (e.g. a
283
+ * daemon that predates the `delete_hf_token` command silently drops
284
+ * it). Rejects if the data channel isn't open. Note the sign-out
285
+ * drops the central relay, so the session may tear down right after
286
+ * the ack - callers should treat a post-call session drop as expected,
287
+ * and a successful sign-out may surface as `null` if teardown races
288
+ * ahead of the ack.
289
+ */
290
+ signOut(): Promise<boolean | null>;
241
291
  applyAudioConfig(config: AudioConfigEntry[], { verify }?: ApplyAudioConfigOptions): Promise<boolean>;
242
292
  readAudioParameter(name: string): Promise<number[] | null>;
243
293
  /**
@@ -258,6 +308,21 @@ export declare class ReachyMini extends EventTarget implements ReachyMiniInstanc
258
308
  sendRaw(data: unknown): boolean;
259
309
  subscribeLogs({ onLine, onError }: SubscribeLogsOptions): () => void;
260
310
  requestState(): boolean;
311
+ /**
312
+ * Ask the daemon to *push* the robot state (~30 Hz) over the dedicated
313
+ * unreliable/unordered `pose` data channel instead of polling get_state.
314
+ * Fires `state` events as frames arrive. No-op against an older daemon (no
315
+ * pose channel) - fall back to `requestState()` polling there.
316
+ *
317
+ * Refcounted: pair every `subscribePose()` with exactly one
318
+ * `unsubscribePose()`. Multiple consumers share a single daemon-side
319
+ * subscription; the daemon only stops pushing once the last one releases.
320
+ * If the channel isn't open yet (or the session later reconnects), the
321
+ * subscription is (re-)asserted from `_checkSessionReady`.
322
+ */
323
+ subscribePose(): boolean;
324
+ /** Release one pose-stream consumer; sends `unsubscribe_pose` on the last. */
325
+ unsubscribePose(): boolean;
261
326
  setAudioMuted(muted: boolean): void;
262
327
  setMicMuted(muted: boolean): void;
263
328
  attachVideo(videoElement: HTMLVideoElement): () => void;
@@ -294,6 +359,11 @@ export declare class ReachyMini extends EventTarget implements ReachyMiniInstanc
294
359
  private _handleSignalingMessage;
295
360
  private _handleEndSession;
296
361
  private _handlePeerMessage;
362
+ /**
363
+ * @param fromPoseStream Frame came from the pushed `pose` channel rather
364
+ * than the reliable control channel. Only those may refresh the state
365
+ * mirror while the stream is live (see the `data.state` branch).
366
+ */
297
367
  private _handleRobotMessage;
298
368
  /** Snap video playback to live edge if buffered lag exceeds 0.5 s. */
299
369
  private _startLatencyMonitor;
@@ -1 +1 @@
1
- {"version":3,"file":"reachy-mini.d.ts","sourceRoot":"","sources":["../../lib/reachy-mini.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAwBH,OAAO,KAAK,EACR,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EAEjB,UAAU,EACV,kBAAkB,EAClB,QAAQ,EACR,eAAe,EACf,cAAc,EACd,wBAAwB,EAExB,kBAAkB,EAClB,iBAAiB,EACjB,SAAS,EACT,UAAU,EAEV,oBAAoB,EACpB,kBAAkB,EACrB,MAAM,YAAY,CAAC;AAmBpB,kEAAkE;AAClE,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC1C,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,sBAAsB,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;AA8DnE,qBAAa,UAAW,SAAQ,WAAY,YAAW,kBAAkB;IAGrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAS;IACpD,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,mBAAmB,CAAU;IAGrC,OAAO,CAAC,MAAM,CAA8D;IAC5E,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,WAAW,CAAkB;IACrC,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;IAGpD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,aAAa,CAA8B;IAGnD,OAAO,CAAC,mBAAmB,CAAgC;IAG3D,GAAG,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IACrC,OAAO,CAAC,GAAG,CAA+B;IAC1C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,iBAAiB,CAA8C;IAGvE,UAAU,EAAE,WAAW,GAAG,IAAI,CAAQ;IACtC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,iBAAiB,CAA+C;IACxE,OAAO,CAAC,qBAAqB,CAA+C;IAG5E,OAAO,CAAC,eAAe,CAA6C;IACpE,OAAO,CAAC,kBAAkB,CAA6C;IACvE,OAAO,CAAC,cAAc,CAA6C;IACnE,OAAO,CAAC,iBAAiB,CAA6C;IACtE,OAAO,CAAC,mBAAmB,CAAiD;IAI5E,OAAO,CAAC,wBAAwB,CAA8C;IAC9E,OAAO,CAAC,0BAA0B,CAA+C;IAGjF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiC;IACjE,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAA0C;IAMrF,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAGxB;IACJ,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqE;IAGnG,OAAO,CAAC,iBAAiB,CAAyB;IAGlD,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,oBAAoB,CAAuB;IAGnD,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,cAAc,CAAuC;IAC7D,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAS;IASxB,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,eAAe,CAA0C;IACjE,OAAO,CAAC,yBAAyB,CAA6B;IAC9D,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,wBAAwB,CAA6B;IAG7D,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAGxC;IAGF,OAAO,CAAC,aAAa,CAAiC;gBAE1C,OAAO,GAAE,iBAAsB;IAiB3C,IAAI,KAAK,IAAI,cAAc,GAAG,WAAW,GAAG,WAAW,CAAwB;IAC/E,IAAI,MAAM,IAAI,SAAS,EAAE,CAAyB;IAClD,IAAI,UAAU,IAAI,UAAU,CAA6B;IACzD,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAA2B;IACxD,IAAI,eAAe,IAAI,OAAO,CAA0B;IACxD,IAAI,YAAY,IAAI,OAAO,CAA+B;IAC1D,IAAI,QAAQ,IAAI,OAAO,CAA2B;IAClD,IAAI,UAAU,IAAI,OAAO,CAA6B;IACtD,IAAI,kBAAkB,IAAI,MAAM,GAAG,IAAI,CAAqC;IAC5E,IAAI,UAAU,IAAI,OAAO,CAA8C;IAEvE;;;;;OAKG;IACH,eAAe,IAAI,IAAI;IAmBjB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;IAoChC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,MAAM,IAAI,IAAI;IAWR,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0EtC,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;YA8EjE,iBAAiB;IA+C/B,OAAO,CAAC,mBAAmB;IAkBrB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA8IlD,OAAO,CAAC,oBAAoB;IA8BtB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IA+ClC,UAAU,IAAI,IAAI;IAkDlB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAYtB;;;;;;;;;OASG;IACH,OAAO,CAAC,iBAAiB;IAqCzB;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;IA2ChC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,wBAAwB;IA+BhC,iDAAiD;IACjD,OAAO,CAAC,0BAA0B;IAwBlC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACH,SAAS,CACL,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAC/F,OAAO;IAiCV,UAAU,CACN,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAClC;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAClF,OAAO;IAuCV,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IAIzE,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAI1D,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAItC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIhC,kBAAkB,IAAI,OAAO;IAI7B,iBAAiB,CAAC,MAAM,SAAM,GAAG,OAAO;IAYxC,gBAAgB,IAAI,OAAO;IAI3B,cAAc,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAQ5C;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CACb,EAAE,UAAkB,EAAE,UAAU,EAAE,GAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,sBAAsB,CAAA;KAAO,GACvG,OAAO;IAKV,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,sBAAsB,GAAG,OAAO;IAI5E,cAAc,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,GAAE,MAAM,EAAE,GAAG,IAAW,GAAG,OAAO;IAIjE,MAAM,CAAC,EAAE,SAAgB,EAAE,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpE,SAAS,CAAC,EAAE,SAAgB,EAAE,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE,OAAO,CAAC,2BAA2B;IAsBnC,OAAO,CAAC,+BAA+B;IAWvC,OAAO,IAAI,OAAO;IAKZ,WAAW,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBrD,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAcpC,aAAa,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAcvC,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQnC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQjD,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQ7C,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQ3D,gBAAgB,CACZ,MAAM,EAAE,gBAAgB,EAAE,EAC1B,EAAE,MAAa,EAAE,GAAE,uBAA4B,GAChD,OAAO,CAAC,OAAO,CAAC;IAQnB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;IAQ1D;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,cAAc;IAiBtB,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAI/B,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,oBAAoB,GAAG,MAAM,IAAI;IAoBpE,YAAY,IAAI,OAAO;IAMvB,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAKnC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IASjC,WAAW,CAAC,YAAY,EAAE,gBAAgB,GAAG,MAAM,IAAI;IA6BjD,QAAQ,CACV,MAAM,EAAE,QAAQ,EAChB,EACI,SAAgB,EAChB,WAAkB,EAClB,WAAoB,EACpB,QAAwB,EACxB,aAAmB,EACnB,mBAAuB,EACvB,cAAqB,EACrB,UAAkC,EAClC,SAAiC,GACpC,GAAE,eAAoB,GACxB,OAAO,CAAC,cAAc,CAAC;IAkJ1B,UAAU,CAAC,QAAQ,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO;IAM7C,WAAW,CACb,SAAS,EAAE,IAAI,EACf,EAAE,WAAqB,EAAE,UAAkC,EAAE,GAAE,kBAAuB,GACvF,OAAO,CAAC,MAAM,CAAC;IAqCZ,iBAAiB,CACnB,QAAQ,EAAE,MAAM,EAChB,EAAE,SAAgB,EAAE,GAAE,wBAA6B,GACpD,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,CAAA;KAAE,CAAC;IAkB7B,WAAW,CAAC,QAAQ,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO;IAWpD,OAAO,CAAC,KAAK;IAOb,OAAO,CAAC,iBAAiB;YAkBX,sBAAsB;YAStB,aAAa;IA4B3B,OAAO,CAAC,YAAY;IAMpB;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,GAAG,OAAO,EACf,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAClC,OAAO,CAAC,CAAC,CAAC;IAmBb;;;OAGG;IACH,cAAc,CACV,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAC9C,MAAM,IAAI;IAYb,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,kBAAkB;YAYZ,uBAAuB;IAiCrC,OAAO,CAAC,iBAAiB;YAyCX,kBAAkB;IA2DhC,OAAO,CAAC,mBAAmB;IAyJ3B,sEAAsE;IACtE,OAAO,CAAC,oBAAoB;CAe/B"}
1
+ {"version":3,"file":"reachy-mini.d.ts","sourceRoot":"","sources":["../../lib/reachy-mini.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAwBH,OAAO,KAAK,EACR,uBAAuB,EACvB,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EAEjB,UAAU,EACV,kBAAkB,EAClB,QAAQ,EACR,eAAe,EACf,cAAc,EACd,wBAAwB,EAExB,kBAAkB,EAClB,iBAAiB,EACjB,SAAS,EACT,UAAU,EAEV,oBAAoB,EACpB,kBAAkB,EACrB,MAAM,YAAY,CAAC;AAmBpB,kEAAkE;AAClE,MAAM,WAAW,mBAAmB;IAChC,MAAM,EAAE,aAAa,GAAG,MAAM,GAAG,QAAQ,CAAC;IAC1C,oEAAoE;IACpE,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,sBAAsB,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,IAAI,CAAC;AA6FnE,qBAAa,UAAW,SAAQ,WAAY,YAAW,kBAAkB;IAGrE,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgB;IAC1C,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAAS;IACpD,OAAO,CAAC,iBAAiB,CAAU;IACnC,OAAO,CAAC,mBAAmB,CAAU;IAGrC,OAAO,CAAC,MAAM,CAA8D;IAC5E,OAAO,CAAC,OAAO,CAAmB;IAClC,OAAO,CAAC,WAAW,CAAkB;IAIrC,OAAO,CAAC,YAAY,CAAuB;IAG3C,OAAO,CAAC,gBAAgB,CAAK;IAM7B,OAAO,CAAC,YAAY,CAAK;IACzB,OAAO,CAAC,QAAQ,CAAC,mBAAmB,CAAgB;IAGpD,OAAO,CAAC,MAAM,CAAuB;IACrC,OAAO,CAAC,SAAS,CAAuB;IACxC,OAAO,CAAC,aAAa,CAA8B;IAGnD,OAAO,CAAC,mBAAmB,CAAgC;IAG3D,GAAG,EAAE,iBAAiB,GAAG,IAAI,CAAQ;IACrC,OAAO,CAAC,GAAG,CAA+B;IAC1C,OAAO,CAAC,UAAU,CAAuB;IACzC,OAAO,CAAC,gBAAgB,CAAuB;IAC/C,OAAO,CAAC,iBAAiB,CAA8C;IAGvE,UAAU,EAAE,WAAW,GAAG,IAAI,CAAQ;IACtC,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,WAAW,CAAQ;IAC3B,OAAO,CAAC,aAAa,CAAS;IAG9B,OAAO,CAAC,iBAAiB,CAA+C;IACxE,OAAO,CAAC,qBAAqB,CAA+C;IAG5E,OAAO,CAAC,eAAe,CAA6C;IACpE,OAAO,CAAC,kBAAkB,CAA6C;IACvE,OAAO,CAAC,cAAc,CAA6C;IACnE,OAAO,CAAC,iBAAiB,CAA6C;IACtE,OAAO,CAAC,mBAAmB,CAA8C;IACzE,OAAO,CAAC,mBAAmB,CAAiD;IAC5E,OAAO,CAAC,iBAAiB,CAA6C;IACtE,OAAO,CAAC,qBAAqB,CAA8C;IAI3E,OAAO,CAAC,wBAAwB,CAA8C;IAC9E,OAAO,CAAC,0BAA0B,CAA+C;IAGjF,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAiC;IACjE,OAAO,CAAC,QAAQ,CAAC,0BAA0B,CAA0C;IAMrF,OAAO,CAAC,WAAW,CAAK;IACxB,OAAO,CAAC,QAAQ,CAAC,WAAW,CAGxB;IACJ,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAqE;IAGnG,OAAO,CAAC,iBAAiB,CAAyB;IAGlD,OAAO,CAAC,mBAAmB,CAAuB;IAClD,OAAO,CAAC,oBAAoB,CAAuB;IAGnD,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,cAAc,CAAuC;IAC7D,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAS;IASxB,OAAO,CAAC,cAAc,CAA8C;IACpE,OAAO,CAAC,eAAe,CAA0C;IACjE,OAAO,CAAC,yBAAyB,CAA6B;IAC9D,OAAO,CAAC,cAAc,CAA6B;IACnD,OAAO,CAAC,eAAe,CAA6B;IACpD,OAAO,CAAC,wBAAwB,CAA6B;IAG7D,OAAO,CAAC,QAAQ,CAAC,yBAAyB,CAGxC;IAGF,OAAO,CAAC,aAAa,CAAiC;gBAE1C,OAAO,GAAE,iBAAsB;IAiB3C,IAAI,KAAK,IAAI,cAAc,GAAG,WAAW,GAAG,WAAW,CAAwB;IAC/E,IAAI,MAAM,IAAI,SAAS,EAAE,CAAyB;IAClD,IAAI,UAAU,IAAI,UAAU,CAA6B;IACzD,IAAI,QAAQ,IAAI,MAAM,GAAG,IAAI,CAA2B;IACxD,IAAI,eAAe,IAAI,OAAO,CAA0B;IACxD,IAAI,YAAY,IAAI,OAAO,CAA+B;IAC1D,IAAI,QAAQ,IAAI,OAAO,CAA2B;IAClD,IAAI,UAAU,IAAI,OAAO,CAA6B;IACtD,IAAI,kBAAkB,IAAI,MAAM,GAAG,IAAI,CAAqC;IAC5E,IAAI,UAAU,IAAI,OAAO,CAA8C;IAEvE;;;;;OAKG;IACH,eAAe,IAAI,IAAI;IAmBjB,YAAY,IAAI,OAAO,CAAC,OAAO,CAAC;IAoChC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAM5B,MAAM,IAAI,IAAI;IAWR,OAAO,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IA0EtC,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC;YA8EjE,iBAAiB;IA+C/B,OAAO,CAAC,mBAAmB;IAkBrB,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyKlD,OAAO,CAAC,oBAAoB;IA8BtB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;IAkDlC,UAAU,IAAI,IAAI;IAqDlB;;;;;OAKG;IACH,OAAO,CAAC,cAAc;IAYtB;;;;;;;;;OASG;IACH,OAAO,CAAC,iBAAiB;IAqCzB;;;;;;OAMG;IACH,OAAO,CAAC,wBAAwB;IA2ChC;;;;;;;;;;;;;;;;;;;OAmBG;IACH,OAAO,CAAC,wBAAwB;IA+BhC,iDAAiD;IACjD,OAAO,CAAC,0BAA0B;IAwBlC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA0CG;IACH,SAAS,CACL,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,GAAE;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;KAAO,GAC/F,OAAO;IAiCV,UAAU,CACN,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAClC;QAAE,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,GAClF,OAAO;IAuCV,aAAa,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO;IAIzE,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO;IAI1D,aAAa,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO;IAItC,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO;IAIhC,kBAAkB,IAAI,OAAO;IAI7B,iBAAiB,CAAC,MAAM,SAAM,GAAG,OAAO;IAYxC,gBAAgB,IAAI,OAAO;IAI3B,cAAc,IAAI,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAQ5C;;;;;;;;;;;;;OAaG;IACH,iBAAiB,CACb,EAAE,UAAkB,EAAE,UAAU,EAAE,GAAE;QAAE,UAAU,CAAC,EAAE,OAAO,CAAC;QAAC,UAAU,CAAC,EAAE,sBAAsB,CAAA;KAAO,GACvG,OAAO;IAKV,YAAY,CAAC,IAAI,EAAE,SAAS,GAAG,UAAU,GAAG,sBAAsB,GAAG,OAAO;IAI5E,cAAc,CAAC,EAAE,EAAE,OAAO,EAAE,GAAG,GAAE,MAAM,EAAE,GAAG,IAAW,GAAG,OAAO;IAIjE,MAAM,CAAC,EAAE,SAAgB,EAAE,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAKpE,SAAS,CAAC,EAAE,SAAgB,EAAE,GAAE,kBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC;IAIvE,OAAO,CAAC,2BAA2B;IAsBnC,OAAO,CAAC,+BAA+B;IAWvC,OAAO,IAAI,OAAO;IAKZ,WAAW,CAAC,SAAS,SAAO,GAAG,OAAO,CAAC,OAAO,CAAC;IAkBrD,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAcpC,aAAa,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAcvC,SAAS,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQnC,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQjD,mBAAmB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQ7C,mBAAmB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQ3D;;;;;;OAMG;IACH,cAAc,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAWzC;;;OAGG;IACH,cAAc,CAAC,WAAW,EAAE,OAAO,GAAG,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAS7D;;;;OAIG;IACH,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQtC;;;;;;OAMG;IACH,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAQlD;;;;;;;;;;;;;;;OAeG;IACH,OAAO,IAAI,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC;IAQlC,gBAAgB,CACZ,MAAM,EAAE,gBAAgB,EAAE,EAC1B,EAAE,MAAa,EAAE,GAAE,uBAA4B,GAChD,OAAO,CAAC,OAAO,CAAC;IAQnB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,GAAG,IAAI,CAAC;IAQ1D;;;;;;;;;;;;;OAaG;IACH,OAAO,CAAC,cAAc;IAsCtB,OAAO,CAAC,IAAI,EAAE,OAAO,GAAG,OAAO;IAI/B,aAAa,CAAC,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,oBAAoB,GAAG,MAAM,IAAI;IAoBpE,YAAY,IAAI,OAAO;IAIvB;;;;;;;;;;;OAWG;IACH,aAAa,IAAI,OAAO;IAKxB,8EAA8E;IAC9E,eAAe,IAAI,OAAO;IAQ1B,aAAa,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IAKnC,WAAW,CAAC,KAAK,EAAE,OAAO,GAAG,IAAI;IASjC,WAAW,CAAC,YAAY,EAAE,gBAAgB,GAAG,MAAM,IAAI;IA6BjD,QAAQ,CACV,MAAM,EAAE,QAAQ,EAChB,EACI,SAAgB,EAChB,WAAkB,EAClB,WAAoB,EACpB,QAAwB,EACxB,aAAmB,EACnB,mBAAuB,EACvB,cAAqB,EACrB,UAAkC,EAClC,SAAiC,GACpC,GAAE,eAAoB,GACxB,OAAO,CAAC,cAAc,CAAC;IAkJ1B,UAAU,CAAC,QAAQ,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO;IAM7C,WAAW,CACb,SAAS,EAAE,IAAI,EACf,EAAE,WAAqB,EAAE,UAAkC,EAAE,GAAE,kBAAuB,GACvF,OAAO,CAAC,MAAM,CAAC;IAqCZ,iBAAiB,CACnB,QAAQ,EAAE,MAAM,EAChB,EAAE,SAAgB,EAAE,GAAE,wBAA6B,GACpD,OAAO,CAAC;QAAE,OAAO,EAAE,IAAI,CAAA;KAAE,CAAC;IAkB7B,WAAW,CAAC,QAAQ,GAAE,MAAM,GAAG,IAAW,GAAG,OAAO;IAWpD,OAAO,CAAC,KAAK;IAOb,OAAO,CAAC,iBAAiB;YAkBX,sBAAsB;YAStB,aAAa;IA4B3B,OAAO,CAAC,YAAY;IAMpB;;;;;;OAMG;IACH,OAAO,CAAC,CAAC,GAAG,OAAO,EACf,MAAM,EAAE,MAAM,EACd,MAAM,GAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAM,EACpC,IAAI,GAAE;QAAE,SAAS,CAAC,EAAE,MAAM,CAAA;KAAO,GAClC,OAAO,CAAC,CAAC,CAAC;IAmBb;;;OAGG;IACH,cAAc,CACV,MAAM,EAAE,MAAM,EACd,EAAE,EAAE,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,IAAI,GAC9C,MAAM,IAAI;IAYb,OAAO,CAAC,iBAAiB;IAQzB,OAAO,CAAC,iBAAiB;IA8BzB,OAAO,CAAC,kBAAkB;YAyBZ,uBAAuB;IAiCrC,OAAO,CAAC,iBAAiB;YAyCX,kBAAkB;IA2DhC;;;;OAIG;IACH,OAAO,CAAC,mBAAmB;IA6L3B,sEAAsE;IACtE,OAAO,CAAC,oBAAoB;CAe/B"}
@@ -40,6 +40,35 @@ const ICE_FAILED_GRACE_MS = 1000;
40
40
  * to be honest with the user.
41
41
  */
42
42
  const MAX_VISIBILITY_DEFER_MS = 60_000;
43
+ /**
44
+ * How long a pushed pose frame keeps the periodic `get_state` poll on hold -
45
+ * both on the way out (no request is sent) and on the way back (a reply that
46
+ * arrives anyway doesn't touch the state mirror).
47
+ *
48
+ * Poll replies carry no `seq`, so they slip past the stale-frame guard: a
49
+ * reply that crosses a fresher pushed frame would rewind the very mirror the
50
+ * stream exists to smooth. While frames flow at ~30 Hz there is nothing left
51
+ * for the poll to add, so it stands down. Keying that on frame arrival rather
52
+ * than on `_poseSubRefs` keeps the poll running against a daemon that doesn't
53
+ * know `subscribe_pose`, and brings it back on its own if the stream stalls.
54
+ * A little over one 500 ms poll period, so a couple of dropped frames don't
55
+ * flip it back and forth.
56
+ */
57
+ const POSE_STREAM_FRESH_MS = 750;
58
+ /**
59
+ * Fail-open ceiling for a single `_slotRoundtrip` request/response.
60
+ * Every slot command has a strict "one reply per request" contract, so
61
+ * a missing reply means the daemon either never got it or - crucially -
62
+ * predates that command entirely: an older daemon silently drops an
63
+ * unknown `type` and sends nothing back, which would otherwise leave the
64
+ * caller's promise pending forever (e.g. a newer SDK calling a command a
65
+ * 1.8.x daemon doesn't implement). Resolving `null` on timeout maps
66
+ * cleanly onto the "unsupported / failed" value every slot caller already
67
+ * handles. 4 s is comfortably above a WebRTC data-channel round trip on a
68
+ * congested phone link while still failing fast enough that a gated UI
69
+ * doesn't feel hung.
70
+ */
71
+ const SLOT_ROUNDTRIP_TIMEOUT_MS = 4000;
43
72
  export class ReachyMini extends EventTarget {
44
73
  // ─── Config ──────────────────────────────────────────────────────────
45
74
  _signalingUrl;
@@ -52,6 +81,19 @@ export class ReachyMini extends EventTarget {
52
81
  _state = 'disconnected';
53
82
  _robots = [];
54
83
  _robotState = {};
84
+ // Highest `seq` seen on the unordered `pose` channel. Frames that arrive
85
+ // out of order (older seq) are dropped so a late packet can't rewind the
86
+ // live mirror. `null` until the first pose frame.
87
+ _lastPoseSeq = null;
88
+ // When the last pushed pose frame was applied, used to park the periodic
89
+ // `get_state` poll while the stream is live (see POSE_STREAM_FRESH_MS).
90
+ _lastPoseFrameAt = 0;
91
+ // Local refcount of pose-stream consumers (the 3D mirror, the wizard's
92
+ // move-end watcher, ...). The daemon's subscription is a per-peer boolean
93
+ // (not refcounted), so we only send `unsubscribe_pose` once the LAST local
94
+ // consumer releases - otherwise one consumer's cleanup would kill the
95
+ // stream for the others.
96
+ _poseSubRefs = 0;
55
97
  _preselectedRobotId;
56
98
  // ─── Auth ────────────────────────────────────────────────────────────
57
99
  _token = null;
@@ -78,7 +120,10 @@ export class ReachyMini extends EventTarget {
78
120
  _hardwareIdResolve = null;
79
121
  _volumeResolve = null;
80
122
  _micVolumeResolve = null;
123
+ _firstWakeUpResolve = null;
81
124
  _trackedFaceResolve = null;
125
+ _robotNameResolve = null;
126
+ _deleteHfTokenResolve = null;
82
127
  // applyAudioConfig() / readAudioParameter() share the same single-slot
83
128
  // pattern as the volume helpers. Separate slots so the two can be
84
129
  // in-flight concurrently without collision.
@@ -547,7 +592,35 @@ export class ReachyMini extends EventTarget {
547
592
  }
548
593
  };
549
594
  this._pc.ondatachannel = (e) => {
550
- this._dc = e.channel;
595
+ const ch = e.channel;
596
+ // On `subscribe_pose` the daemon opens a second,
597
+ // unreliable/unordered channel labelled "pose" that *pushes*
598
+ // the robot state at ~30 Hz (see media_server
599
+ // `_setup_pose_channel`), so this can fire mid-session. It
600
+ // carries the same `{state:{...}}` envelope as a get_state
601
+ // reply, so we route it through the same handler - but it
602
+ // must NOT gate session readiness (that's the reliable
603
+ // control channel's job) nor become `_dc` (commands must
604
+ // never ride the lossy channel).
605
+ if (ch.label === 'pose') {
606
+ // Fresh channel (new session or daemon restart): the
607
+ // daemon's seq counter may have reset, so forget the old
608
+ // high-water mark or we'd drop every new frame.
609
+ this._lastPoseSeq = null;
610
+ ch.onmessage = (ev) => {
611
+ const msg = JSON.parse(ev.data);
612
+ // Drop stale/reordered frames (unordered channel).
613
+ if (typeof msg.seq === 'number') {
614
+ if (this._lastPoseSeq !== null && msg.seq <= this._lastPoseSeq)
615
+ return;
616
+ this._lastPoseSeq = msg.seq;
617
+ }
618
+ this._lastPoseFrameAt = Date.now();
619
+ this._handleRobotMessage(msg, true);
620
+ };
621
+ return;
622
+ }
623
+ this._dc = ch;
551
624
  this._dc.onopen = () => {
552
625
  this._dcOpen = true;
553
626
  this._checkSessionReady();
@@ -611,6 +684,10 @@ export class ReachyMini extends EventTarget {
611
684
  this._micVolumeResolve(null);
612
685
  this._micVolumeResolve = null;
613
686
  }
687
+ if (this._firstWakeUpResolve) {
688
+ this._firstWakeUpResolve(null);
689
+ this._firstWakeUpResolve = null;
690
+ }
614
691
  if (this._trackedFaceResolve) {
615
692
  this._trackedFaceResolve(null);
616
693
  this._trackedFaceResolve = null;
@@ -623,6 +700,14 @@ export class ReachyMini extends EventTarget {
623
700
  this._readAudioParameterResolve(null);
624
701
  this._readAudioParameterResolve = null;
625
702
  }
703
+ if (this._robotNameResolve) {
704
+ this._robotNameResolve(null);
705
+ this._robotNameResolve = null;
706
+ }
707
+ if (this._deleteHfTokenResolve) {
708
+ this._deleteHfTokenResolve(null);
709
+ this._deleteHfTokenResolve = null;
710
+ }
626
711
  this._logSubscribers.clear();
627
712
  this._updateProgressSubscribers.clear();
628
713
  this._rejectPendingMotionCompletions(new Error('Session stopped'));
@@ -691,6 +776,10 @@ export class ReachyMini extends EventTarget {
691
776
  this._micVolumeResolve(null);
692
777
  this._micVolumeResolve = null;
693
778
  }
779
+ if (this._firstWakeUpResolve) {
780
+ this._firstWakeUpResolve(null);
781
+ this._firstWakeUpResolve = null;
782
+ }
694
783
  if (this._trackedFaceResolve) {
695
784
  this._trackedFaceResolve(null);
696
785
  this._trackedFaceResolve = null;
@@ -703,6 +792,14 @@ export class ReachyMini extends EventTarget {
703
792
  this._readAudioParameterResolve(null);
704
793
  this._readAudioParameterResolve = null;
705
794
  }
795
+ if (this._robotNameResolve) {
796
+ this._robotNameResolve(null);
797
+ this._robotNameResolve = null;
798
+ }
799
+ if (this._deleteHfTokenResolve) {
800
+ this._deleteHfTokenResolve(null);
801
+ this._deleteHfTokenResolve = null;
802
+ }
706
803
  this._logSubscribers.clear();
707
804
  this._updateProgressSubscribers.clear();
708
805
  this._rejectPendingMotionCompletions(new Error('Disconnected'));
@@ -1178,6 +1275,66 @@ export class ReachyMini extends EventTarget {
1178
1275
  setMicrophoneVolume(volume) {
1179
1276
  return this._slotRoundtrip(() => this._micVolumeResolve, (next) => { this._micVolumeResolve = next; }, { type: 'set_microphone_volume', volume: clampVolume(volume) });
1180
1277
  }
1278
+ /**
1279
+ * Query whether the first wake-up setup wizard has been completed.
1280
+ * Robot-wide, persisted on the robot. Resolves `false` when pending,
1281
+ * `true` when done, or `null` when the channel isn't open / the daemon
1282
+ * predates the `get_first_wake_up` command (callers should fail-open
1283
+ * and skip the wizard on `null`).
1284
+ */
1285
+ getFirstWakeUp() {
1286
+ // Fail-open, so this never rejects: the wizard gate runs right after
1287
+ // connect, which is exactly when the channel may not be open yet.
1288
+ if (!this._dc || this._dc.readyState !== 'open')
1289
+ return Promise.resolve(null);
1290
+ return this._slotRoundtrip(() => this._firstWakeUpResolve, (next) => { this._firstWakeUpResolve = next; }, { type: 'get_first_wake_up' });
1291
+ }
1292
+ /**
1293
+ * Persist the first wake-up wizard completion flag on the robot.
1294
+ * Resolves with the stored value (or `null` on channel-closed).
1295
+ */
1296
+ setFirstWakeUp(isCompleted) {
1297
+ if (!this._dc || this._dc.readyState !== 'open')
1298
+ return Promise.resolve(null);
1299
+ return this._slotRoundtrip(() => this._firstWakeUpResolve, (next) => { this._firstWakeUpResolve = next; }, { type: 'set_first_wake_up', is_completed: isCompleted });
1300
+ }
1301
+ /**
1302
+ * Query the persisted robot display name. Resolves the stored name,
1303
+ * `null` when none is set / the channel isn't open / the daemon predates
1304
+ * the `get_robot_name` command.
1305
+ */
1306
+ getRobotName() {
1307
+ return this._slotRoundtrip(() => this._robotNameResolve, (next) => { this._robotNameResolve = next; }, { type: 'get_robot_name' });
1308
+ }
1309
+ /**
1310
+ * Set and persist the robot display name on the robot. Resolves with the
1311
+ * stored (trimmed) name, or `null` on error / channel-closed. Applied live
1312
+ * by the daemon (status + central relay + mDNS), so it takes effect right
1313
+ * away without a restart; the persisted name also overrides --robot-name
1314
+ * on the next start.
1315
+ */
1316
+ setRobotName(name) {
1317
+ return this._slotRoundtrip(() => this._robotNameResolve, (next) => { this._robotNameResolve = next; }, { type: 'set_robot_name', name });
1318
+ }
1319
+ /**
1320
+ * Sign this robot out of Hugging Face: asks the daemon to delete its
1321
+ * stored HF token, which de-registers the robot from the central
1322
+ * signaling relay (it disappears from its owner's robot list until it
1323
+ * is set up again). Works over the WebRTC data channel, so it reaches
1324
+ * the robot remotely (no LAN HTTP path required).
1325
+ *
1326
+ * Resolves `true` when the daemon acked success, `false` on a daemon
1327
+ * error, or `null` when no ack arrives before the timeout (e.g. a
1328
+ * daemon that predates the `delete_hf_token` command silently drops
1329
+ * it). Rejects if the data channel isn't open. Note the sign-out
1330
+ * drops the central relay, so the session may tear down right after
1331
+ * the ack - callers should treat a post-call session drop as expected,
1332
+ * and a successful sign-out may surface as `null` if teardown races
1333
+ * ahead of the ack.
1334
+ */
1335
+ signOut() {
1336
+ return this._slotRoundtrip(() => this._deleteHfTokenResolve, (next) => { this._deleteHfTokenResolve = next; }, { type: 'delete_hf_token' });
1337
+ }
1181
1338
  applyAudioConfig(config, { verify = true } = {}) {
1182
1339
  return this._slotRoundtrip(() => this._applyAudioConfigResolve, (next) => { this._applyAudioConfigResolve = next; }, { type: 'apply_audio_config', config, verify }).then((v) => v === true);
1183
1340
  }
@@ -1207,7 +1364,29 @@ export class ReachyMini extends EventTarget {
1207
1364
  const prev = getSlot();
1208
1365
  if (prev)
1209
1366
  prev(null);
1210
- setSlot(resolve);
1367
+ let timer;
1368
+ // Single settle path shared by the daemon response, supersession
1369
+ // by a newer call, and the fail-open timeout below. It clears the
1370
+ // timer once and detaches itself from the slot only if it's still
1371
+ // the current occupant, so a stale settle (timed-out or superseded)
1372
+ // can't clear a newer call's slot registration. The message handler
1373
+ // stores `settle` (not `resolve`), so every response route funnels
1374
+ // through here.
1375
+ // Note: slots are keyed by command type, not request id, so this
1376
+ // does not prevent a genuinely late daemon reply from being routed
1377
+ // to a newer same-command caller — that cross-talk is inherent to
1378
+ // the single-flight slot design and unchanged here.
1379
+ const settle = (v) => {
1380
+ if (timer !== undefined) {
1381
+ clearTimeout(timer);
1382
+ timer = undefined;
1383
+ }
1384
+ if (getSlot() === settle)
1385
+ setSlot(null);
1386
+ resolve(v);
1387
+ };
1388
+ setSlot(settle);
1389
+ timer = setTimeout(() => settle(null), SLOT_ROUNDTRIP_TIMEOUT_MS);
1211
1390
  this._sendCommand(command);
1212
1391
  });
1213
1392
  }
@@ -1237,6 +1416,30 @@ export class ReachyMini extends EventTarget {
1237
1416
  requestState() {
1238
1417
  return this._sendCommand({ type: 'get_state' });
1239
1418
  }
1419
+ /**
1420
+ * Ask the daemon to *push* the robot state (~30 Hz) over the dedicated
1421
+ * unreliable/unordered `pose` data channel instead of polling get_state.
1422
+ * Fires `state` events as frames arrive. No-op against an older daemon (no
1423
+ * pose channel) - fall back to `requestState()` polling there.
1424
+ *
1425
+ * Refcounted: pair every `subscribePose()` with exactly one
1426
+ * `unsubscribePose()`. Multiple consumers share a single daemon-side
1427
+ * subscription; the daemon only stops pushing once the last one releases.
1428
+ * If the channel isn't open yet (or the session later reconnects), the
1429
+ * subscription is (re-)asserted from `_checkSessionReady`.
1430
+ */
1431
+ subscribePose() {
1432
+ this._poseSubRefs++;
1433
+ return this._sendCommand({ type: 'subscribe_pose' });
1434
+ }
1435
+ /** Release one pose-stream consumer; sends `unsubscribe_pose` on the last. */
1436
+ unsubscribePose() {
1437
+ if (this._poseSubRefs > 0)
1438
+ this._poseSubRefs--;
1439
+ if (this._poseSubRefs > 0)
1440
+ return true; // still wanted by another consumer
1441
+ return this._sendCommand({ type: 'unsubscribe_pose' });
1442
+ }
1240
1443
  // ─── Audio ───────────────────────────────────────────────────────────
1241
1444
  setAudioMuted(muted) {
1242
1445
  this._audioMuted = muted;
@@ -1617,7 +1820,22 @@ export class ReachyMini extends EventTarget {
1617
1820
  if (this._iceConnected && this._dcOpen && this._sessionResolve) {
1618
1821
  this._state = 'streaming';
1619
1822
  this.requestState();
1620
- this._stateRefreshInterval = setInterval(() => this.requestState(), 500);
1823
+ // Re-assert a pose subscription that was requested before the data
1824
+ // channel was open, or lost on reconnect (a fresh peer starts
1825
+ // unsubscribed on the daemon). Sent raw so it doesn't touch the
1826
+ // local refcount, which already reflects the live consumer count.
1827
+ if (this._poseSubRefs > 0)
1828
+ this._sendCommand({ type: 'subscribe_pose' });
1829
+ // A fresh session has received no pose frame yet, so a timestamp
1830
+ // left over from the previous one must not hold off the poll.
1831
+ this._lastPoseFrameAt = 0;
1832
+ this._stateRefreshInterval = setInterval(() => {
1833
+ // Skip while the pose stream is already feeding state; see
1834
+ // POSE_STREAM_FRESH_MS.
1835
+ if (Date.now() - this._lastPoseFrameAt < POSE_STREAM_FRESH_MS)
1836
+ return;
1837
+ this.requestState();
1838
+ }, 500);
1621
1839
  this._emit('streaming', { sessionId: this._sessionId, robotId: this._selectedRobotId });
1622
1840
  this._sessionResolve();
1623
1841
  this._sessionResolve = null;
@@ -1761,7 +1979,12 @@ export class ReachyMini extends EventTarget {
1761
1979
  this._emit('error', { source: 'webrtc', error: e });
1762
1980
  }
1763
1981
  }
1764
- _handleRobotMessage(data) {
1982
+ /**
1983
+ * @param fromPoseStream Frame came from the pushed `pose` channel rather
1984
+ * than the reliable control channel. Only those may refresh the state
1985
+ * mirror while the stream is live (see the `data.state` branch).
1986
+ */
1987
+ _handleRobotMessage(data, fromPoseStream = false) {
1765
1988
  // JSON-RPC frames (app control surface) are handled separately from
1766
1989
  // the legacy {command|type} robot messages that share this channel.
1767
1990
  if (data.jsonrpc === '2.0') {
@@ -1792,6 +2015,27 @@ export class ReachyMini extends EventTarget {
1792
2015
  }
1793
2016
  return;
1794
2017
  }
2018
+ if (data.command === 'get_first_wake_up' || data.command === 'set_first_wake_up') {
2019
+ if (this._firstWakeUpResolve) {
2020
+ this._firstWakeUpResolve(data.status === 'error' ? null : !!data.is_completed);
2021
+ this._firstWakeUpResolve = null;
2022
+ }
2023
+ return;
2024
+ }
2025
+ if (data.command === 'get_robot_name' || data.command === 'set_robot_name') {
2026
+ if (this._robotNameResolve) {
2027
+ this._robotNameResolve(data.status === 'error' ? null : (data.name ?? null));
2028
+ this._robotNameResolve = null;
2029
+ }
2030
+ return;
2031
+ }
2032
+ if (data.command === 'delete_hf_token') {
2033
+ if (this._deleteHfTokenResolve) {
2034
+ this._deleteHfTokenResolve(data.status === 'error' ? false : true);
2035
+ this._deleteHfTokenResolve = null;
2036
+ }
2037
+ return;
2038
+ }
1795
2039
  if (data.command === 'apply_audio_config') {
1796
2040
  if (this._applyAudioConfigResolve) {
1797
2041
  this._applyAudioConfigResolve(data.error ? false : !!data.applied);
@@ -1888,7 +2132,18 @@ export class ReachyMini extends EventTarget {
1888
2132
  }
1889
2133
  return;
1890
2134
  }
1891
- if (data.state) {
2135
+ // Only the stream may write the mirror while the stream is live. The
2136
+ // poll stands down in that case (see POSE_STREAM_FRESH_MS), but it
2137
+ // can't unsend a request already in flight: `get_state` rides the
2138
+ // reliable channel, so its reply queues behind whatever else is on it
2139
+ // - a `upload_move_*` burst, typically - and can land hundreds of ms
2140
+ // after the snapshot it carries. Having no `seq`, it slips past the
2141
+ // stale-frame guard and rewinds every consumer to a pose from before
2142
+ // the upload, until the next pushed frame puts them back. That reads
2143
+ // as a one-frame flick to the pre-move pose right as an animation
2144
+ // starts. Nothing is lost by dropping it: pushed frames carry the same
2145
+ // fields (daemon-side `build_state_dict` feeds both).
2146
+ if (data.state && (fromPoseStream || Date.now() - this._lastPoseFrameAt >= POSE_STREAM_FRESH_MS)) {
1892
2147
  const s = data.state;
1893
2148
  if (s.head_pose)
1894
2149
  this._robotState.head = s.head_pose.flat();