@pollen-robotics/reachy-mini-sdk 1.10.0-dev.0.main.b50d97e → 1.10.0-dev.0.main.c8367cd

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.
Files changed (128) hide show
  1. package/APP_CREATION_GUIDE.md +98 -0
  2. package/dist/lib/central-listener.d.ts +71 -0
  3. package/dist/lib/central-listener.d.ts.map +1 -0
  4. package/dist/lib/central-listener.js +277 -0
  5. package/dist/lib/central-listener.js.map +1 -0
  6. package/dist/lib/central-listener.test.d.ts +2 -0
  7. package/dist/lib/central-listener.test.d.ts.map +1 -0
  8. package/dist/lib/central-listener.test.js +378 -0
  9. package/dist/lib/central-listener.test.js.map +1 -0
  10. package/dist/lib/dc-watchdog.test.d.ts +2 -0
  11. package/dist/lib/dc-watchdog.test.d.ts.map +1 -0
  12. package/dist/lib/dc-watchdog.test.js +154 -0
  13. package/dist/lib/dc-watchdog.test.js.map +1 -0
  14. package/dist/lib/ensure-awake.test.d.ts +2 -0
  15. package/dist/lib/ensure-awake.test.d.ts.map +1 -0
  16. package/dist/lib/ensure-awake.test.js +117 -0
  17. package/dist/lib/ensure-awake.test.js.map +1 -0
  18. package/dist/lib/ice-grace.test.d.ts +2 -0
  19. package/dist/lib/ice-grace.test.d.ts.map +1 -0
  20. package/dist/lib/ice-grace.test.js +211 -0
  21. package/dist/lib/ice-grace.test.js.map +1 -0
  22. package/dist/lib/imu.test.d.ts +2 -0
  23. package/dist/lib/imu.test.d.ts.map +1 -0
  24. package/dist/lib/imu.test.js +78 -0
  25. package/dist/lib/imu.test.js.map +1 -0
  26. package/dist/lib/logger.d.ts +38 -0
  27. package/dist/lib/logger.d.ts.map +1 -0
  28. package/dist/lib/logger.js +81 -0
  29. package/dist/lib/logger.js.map +1 -0
  30. package/dist/lib/pending-replies.d.ts +127 -0
  31. package/dist/lib/pending-replies.d.ts.map +1 -0
  32. package/dist/lib/pending-replies.js +255 -0
  33. package/dist/lib/pending-replies.js.map +1 -0
  34. package/dist/lib/pending-replies.test.d.ts +2 -0
  35. package/dist/lib/pending-replies.test.d.ts.map +1 -0
  36. package/dist/lib/pending-replies.test.js +217 -0
  37. package/dist/lib/pending-replies.test.js.map +1 -0
  38. package/dist/lib/reachy-mini.d.ts +202 -88
  39. package/dist/lib/reachy-mini.d.ts.map +1 -1
  40. package/dist/lib/reachy-mini.js +651 -566
  41. package/dist/lib/reachy-mini.js.map +1 -1
  42. package/dist/lib/redial.test.d.ts +2 -0
  43. package/dist/lib/redial.test.d.ts.map +1 -0
  44. package/dist/lib/redial.test.js +206 -0
  45. package/dist/lib/redial.test.js.map +1 -0
  46. package/dist/lib/request.test.d.ts +2 -0
  47. package/dist/lib/request.test.d.ts.map +1 -0
  48. package/dist/lib/request.test.js +90 -0
  49. package/dist/lib/request.test.js.map +1 -0
  50. package/dist/lib/session-supervisor.d.ts +201 -0
  51. package/dist/lib/session-supervisor.d.ts.map +1 -0
  52. package/dist/lib/session-supervisor.js +520 -0
  53. package/dist/lib/session-supervisor.js.map +1 -0
  54. package/dist/lib/token-store.d.ts +50 -0
  55. package/dist/lib/token-store.d.ts.map +1 -0
  56. package/dist/lib/token-store.js +120 -0
  57. package/dist/lib/token-store.js.map +1 -0
  58. package/dist/lib/token-store.test.d.ts +2 -0
  59. package/dist/lib/token-store.test.d.ts.map +1 -0
  60. package/dist/lib/token-store.test.js +82 -0
  61. package/dist/lib/token-store.test.js.map +1 -0
  62. package/dist/lib/types.d.ts +280 -6
  63. package/dist/lib/types.d.ts.map +1 -1
  64. package/dist/lib/url-helpers.d.ts.map +1 -1
  65. package/dist/lib/url-helpers.js +3 -1
  66. package/dist/lib/url-helpers.js.map +1 -1
  67. package/dist/lib/version.d.ts +2 -0
  68. package/dist/lib/version.d.ts.map +1 -0
  69. package/dist/lib/version.js +5 -0
  70. package/dist/lib/version.js.map +1 -0
  71. package/dist/reachy-mini-sdk.d.ts +15 -1
  72. package/dist/reachy-mini-sdk.d.ts.map +1 -1
  73. package/dist/reachy-mini-sdk.js +12 -0
  74. package/dist/reachy-mini-sdk.js.map +1 -1
  75. package/host/dist/ReachyHost.d.ts.map +1 -1
  76. package/host/dist/chunks/centralRest-uXNMidlL.js +2315 -0
  77. package/host/dist/chunks/index-Beu4GNzi.js +821 -0
  78. package/host/dist/chunks/{mountHost-DjE5zE9R.js → mountHost-BTTF4Uu9.js} +24933 -27488
  79. package/host/dist/components/ConnectingView.d.ts.map +1 -1
  80. package/host/dist/components/DaemonUpdateGate.d.ts +65 -0
  81. package/host/dist/components/DaemonUpdateGate.d.ts.map +1 -0
  82. package/host/dist/components/ErrorView.d.ts.map +1 -1
  83. package/host/dist/components/HostErrorBoundary.d.ts +30 -0
  84. package/host/dist/components/HostErrorBoundary.d.ts.map +1 -0
  85. package/host/dist/components/LeavingView.d.ts.map +1 -1
  86. package/host/dist/components/PickerView.d.ts +16 -14
  87. package/host/dist/components/PickerView.d.ts.map +1 -1
  88. package/host/dist/components/PostOAuthSplash.d.ts +14 -9
  89. package/host/dist/components/PostOAuthSplash.d.ts.map +1 -1
  90. package/host/dist/components/ReachyHostShell.d.ts.map +1 -1
  91. package/host/dist/components/SignInView.d.ts.map +1 -1
  92. package/host/dist/components/TopBar.d.ts +5 -6
  93. package/host/dist/components/TopBar.d.ts.map +1 -1
  94. package/host/dist/components/WelcomeBackOverlay.d.ts.map +1 -1
  95. package/host/dist/embed/index.d.ts +68 -5
  96. package/host/dist/embed/index.d.ts.map +1 -1
  97. package/host/dist/entry/auto.js +2 -2
  98. package/host/dist/entry/embed.d.ts +5 -4
  99. package/host/dist/entry/embed.d.ts.map +1 -1
  100. package/host/dist/entry/embed.js +5 -4
  101. package/host/dist/hooks/useHostBridge.d.ts +35 -1
  102. package/host/dist/hooks/useHostBridge.d.ts.map +1 -1
  103. package/host/dist/hooks/useLatestDaemonVersion.d.ts +7 -0
  104. package/host/dist/hooks/useLatestDaemonVersion.d.ts.map +1 -0
  105. package/host/dist/hooks/useOAuth.d.ts +18 -0
  106. package/host/dist/hooks/useOAuth.d.ts.map +1 -1
  107. package/host/dist/hooks/useRobots.d.ts +0 -2
  108. package/host/dist/hooks/useRobots.d.ts.map +1 -1
  109. package/host/dist/index.d.ts +2 -2
  110. package/host/dist/index.d.ts.map +1 -1
  111. package/host/dist/index.js +11 -10
  112. package/host/dist/lib/daemonRelease.d.ts +84 -0
  113. package/host/dist/lib/daemonRelease.d.ts.map +1 -0
  114. package/host/dist/lib/protocol.d.ts +141 -7
  115. package/host/dist/lib/protocol.d.ts.map +1 -1
  116. package/host/dist/lib/rebootWatch.d.ts +51 -0
  117. package/host/dist/lib/rebootWatch.d.ts.map +1 -0
  118. package/host/dist/lib/sdk-types.d.ts +1 -1
  119. package/host/dist/lib/sdk-types.d.ts.map +1 -1
  120. package/host/dist/lib/sdkStaleness.d.ts +78 -0
  121. package/host/dist/lib/sdkStaleness.d.ts.map +1 -0
  122. package/host/dist/lib/settings.d.ts +16 -5
  123. package/host/dist/lib/settings.d.ts.map +1 -1
  124. package/package.json +11 -4
  125. package/host/dist/chunks/index-lMs2sCXG.js +0 -439
  126. package/host/dist/chunks/reachy-mini-CdsdQLIU.js +0 -1523
  127. package/host/dist/lib/centralListener.d.ts +0 -73
  128. package/host/dist/lib/centralListener.d.ts.map +0 -1
@@ -1,14 +1,15 @@
1
- import { R as o } from "./chunks/reachy-mini-CdsdQLIU.js";
2
- import { R as s, m as a } from "./chunks/mountHost-DjE5zE9R.js";
3
- import { c as t } from "./chunks/index-lMs2sCXG.js";
4
- import { PROTOCOL_VERSION as c, decodeCredsFromHash as m, encodeCredsToHash as w, isProtocolMessage as h } from "./lib/protocol.js";
1
+ import { R as o } from "./chunks/centralRest-uXNMidlL.js";
2
+ import { R as i, m as a } from "./chunks/mountHost-BTTF4Uu9.js";
3
+ import { c as r, a as c } from "./chunks/index-Beu4GNzi.js";
4
+ import { PROTOCOL_VERSION as m, decodeCredsFromHash as w, encodeCredsToHash as h, isProtocolMessage as y } from "./lib/protocol.js";
5
5
  typeof window < "u" && !window.ReachyMini && (window.ReachyMini = o, window.dispatchEvent(new Event("reachymini:ready")));
6
6
  export {
7
- c as PROTOCOL_VERSION,
8
- s as ReachyHost,
9
- t as connectToHost,
10
- m as decodeCredsFromHash,
11
- w as encodeCredsToHash,
12
- h as isProtocolMessage,
7
+ m as PROTOCOL_VERSION,
8
+ i as ReachyHost,
9
+ r as connectToHost,
10
+ c as connectToHostCredsOnly,
11
+ w as decodeCredsFromHash,
12
+ h as encodeCredsToHash,
13
+ y as isProtocolMessage,
13
14
  a as mountHost
14
15
  };
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Daemon version policy for the standalone web shell.
3
+ *
4
+ * Why the shell cares
5
+ * ───────────────────
6
+ * A Space runs against whatever daemon the user's robot happens to have.
7
+ * Neither central nor the host holds a data channel, so the only version
8
+ * signal is the one the embed forwards on `embed:app-state` once its
9
+ * session is up (`daemonVersion`).
10
+ *
11
+ * Why this is softer than the mobile app
12
+ * ──────────────────────────────────────
13
+ * The mobile app blocks the moment the daemon is behind the latest
14
+ * GitHub release. That is defensible for a controlled first-party
15
+ * funnel; it is not for the web, where every published Space would turn
16
+ * a daemon release into a global kill switch for robots that were
17
+ * working fine a minute earlier. So the web policy has two tiers:
18
+ *
19
+ * block - the daemon is below `MIN_SUPPORTED_DAEMON_VERSION`, i.e.
20
+ * too old for the app contract to hold. Blocking is the only
21
+ * honest answer.
22
+ * notice - the daemon merely trails the latest release. Dismissable
23
+ * banner, the app stays usable.
24
+ *
25
+ * Fail-open discipline
26
+ * ────────────────────
27
+ * Every unknown resolves to "no action". An unparseable version, a
28
+ * daemon too old to answer `get_version`, an offline or rate-limited
29
+ * GitHub: none of these may ever cost the user their app. We only act
30
+ * on versions we positively understand.
31
+ */
32
+ /**
33
+ * Floor below which the shell refuses to run an app.
34
+ *
35
+ * Set to the first release that understands the WebRTC `start_update`
36
+ * command (reachy_mini#1208, v1.8.2), which makes it the lowest version
37
+ * we can still repair from here: under it, the gate's own "Update now"
38
+ * button would do nothing and the user has to go through the desktop
39
+ * app anyway.
40
+ */
41
+ export declare const MIN_SUPPORTED_DAEMON_VERSION = "1.8.2";
42
+ export interface SemVer {
43
+ major: number;
44
+ minor: number;
45
+ patch: number;
46
+ }
47
+ /**
48
+ * Parse a `MAJOR.MINOR.PATCH` string into a `SemVer`. Tolerates a
49
+ * leading `v` (GitHub tags) and trailing pre-release / build metadata
50
+ * (`1.8.2-rc1` → `1.8.2`). Returns `null` when the first three numeric
51
+ * components can't be read, so callers can fail-open.
52
+ */
53
+ export declare function parseSemver(value: string | null | undefined): SemVer | null;
54
+ /** Negative if `a < b`, positive if `a > b`, 0 if equal. */
55
+ export declare function compareSemver(a: SemVer, b: SemVer): number;
56
+ /**
57
+ * True only when BOTH versions parse AND `current` is strictly behind
58
+ * `latest`. Any unparseable input returns `false` (fail-open).
59
+ */
60
+ export declare function isDaemonOutdated(current: string | null | undefined, latest: string | null | undefined): boolean;
61
+ /**
62
+ * What the shell should do about this daemon.
63
+ *
64
+ * `'block'` - below the supported floor: hard gate.
65
+ * `'notice'` - behind the latest release: dismissable banner.
66
+ * `null` - up to date, or not enough information to judge.
67
+ */
68
+ export type DaemonUpdateSeverity = 'block' | 'notice';
69
+ export declare function daemonUpdateSeverity(current: string | null | undefined, latest: string | null | undefined): DaemonUpdateSeverity | null;
70
+ /**
71
+ * True when the daemon is new enough to install its own update over the
72
+ * data channel. Unknown / unparseable → false, so the caller points the
73
+ * user at the desktop app rather than offering a dead button.
74
+ */
75
+ export declare function supportsSelfUpdate(current: string | null | undefined): boolean;
76
+ /**
77
+ * Resolve the latest published daemon version (e.g. `"1.8.2"`), or
78
+ * `null` when it can't be determined. Cached in localStorage for
79
+ * `TTL_MS`: the unauthenticated GitHub API allows 60 requests per
80
+ * hour per IP, and a Space reloads far more often than daemons ship.
81
+ * Never throws.
82
+ */
83
+ export declare function fetchLatestDaemonVersion(): Promise<string | null>;
84
+ //# sourceMappingURL=daemonRelease.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"daemonRelease.d.ts","sourceRoot":"","sources":["../../src/lib/daemonRelease.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAWH;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,UAAU,CAAC;AAEpD,MAAM,WAAW,MAAM;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,GAAG,IAAI,CAS3E;AAED,4DAA4D;AAC5D,wBAAgB,aAAa,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAE1D;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAClC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC,OAAO,CAKT;AAED;;;;;;GAMG;AACH,MAAM,MAAM,oBAAoB,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEtD,wBAAgB,oBAAoB,CAClC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAClC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC,oBAAoB,GAAG,IAAI,CAW7B;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAI9E;AAsBD;;;;;;GAMG;AACH,wBAAsB,wBAAwB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAyBvE"}
@@ -14,10 +14,12 @@
14
14
  * - Every message carries `source: 'reachy-mini'`. Lets receivers
15
15
  * distinguish our envelopes from unrelated `postMessage` traffic
16
16
  * (DevTools, MUI portals, browser extensions, ...).
17
- * - Both sides validate `event.origin` against the expected origin
18
- * before trusting the payload. In our deployment (same-origin
19
- * iframe within an HF Space) the expected origin is
20
- * `event.origin === window.location.origin`.
17
+ * - Both sides validate `event.origin` before trusting the payload,
18
+ * but against different references: the web shell requires strict
19
+ * same-origin (`window.location.origin` - Mode A, the Space embeds
20
+ * itself), while the embed accepts its PARENT's origin resolved via
21
+ * `document.referrer` (Mode B, the mobile shell at e.g.
22
+ * `tauri.localhost` is a different origin by construction).
21
23
  *
22
24
  * Message families
23
25
  * ────────────────
@@ -87,8 +89,19 @@ export interface HostInitMsg {
87
89
  hfToken?: string;
88
90
  /** HF account user name, when known. */
89
91
  userName?: string | null;
90
- /** Robot ID selected by the host's picker. */
92
+ /** Robot ID selected by the host's picker. Unstable: the central
93
+ * peer id rotates on every relay reconnect, so the embed treats
94
+ * this as a starting point and re-resolves the live id from
95
+ * `robotHardwareId` right before `startSession()`. */
91
96
  robotPeerId: string;
97
+ /** Stable hardware id of the selected robot (central
98
+ * `meta.hardware_id`), when the daemon exposes one. Lets the embed
99
+ * re-resolve the CURRENT `robotPeerId` from central just before
100
+ * dialing, so a rotated peer id (long iframe cold-start, relay
101
+ * reconnect) doesn't strand the app on a dead producer. Omitted for
102
+ * daemons too old to advertise it - the embed then uses
103
+ * `robotPeerId` as-is. */
104
+ robotHardwareId?: string | null;
92
105
  /** Optional opaque payload from `?config=<base64>` or from the
93
106
  * mobile-app handoff. App is responsible for parsing /
94
107
  * validating. */
@@ -133,7 +146,45 @@ export interface HostLeavingMsg {
133
146
  * regardless. */
134
147
  timeoutMs: number;
135
148
  }
136
- export type HostToEmbedMsg = HostInitMsg | HostThemeChangedMsg | HostConfigChangedMsg | HostLeavingMsg;
149
+ /**
150
+ * Ask the embed to trigger the daemon's PyPI self-update.
151
+ *
152
+ * Only the embed holds a data channel, so the host cannot send
153
+ * `start_update` itself - it delegates here and watches the
154
+ * `embed:update-progress` replies. A successful update ends with a
155
+ * `systemctl restart` on the robot, which kills the session: the embed
156
+ * will go silent and the host is expected to remount the iframe once
157
+ * the robot is back on central.
158
+ *
159
+ * Additive message (no version bump): an embed too old to know this
160
+ * type ignores it, so the host must not block on a reply.
161
+ */
162
+ export interface HostStartUpdateMsg {
163
+ source: typeof PROTOCOL_SOURCE;
164
+ type: 'host:start-update';
165
+ version: 1;
166
+ /** Install the latest pre-release instead of the latest stable. */
167
+ preRelease?: boolean;
168
+ }
169
+ /**
170
+ * The host gave up on the update it started (its own stall timer
171
+ * fired while the session was still alive). This does NOT abort the
172
+ * daemon-side job - nothing can, the install owns the robot - it
173
+ * disarms the embed's update-mode plumbing: the sessionStopped →
174
+ * `rebooting` translator and the auto-reconnect stand-down. Without
175
+ * it, the user's next NORMAL end-session would replay a stale
176
+ * `rebooting` frame into a host that already declared the update
177
+ * failed.
178
+ *
179
+ * Additive message (no version bump): an embed too old to know this
180
+ * type ignores it, and its own translator dies with the iframe.
181
+ */
182
+ export interface HostCancelUpdateMsg {
183
+ source: typeof PROTOCOL_SOURCE;
184
+ type: 'host:cancel-update';
185
+ version: 1;
186
+ }
187
+ export type HostToEmbedMsg = HostInitMsg | HostThemeChangedMsg | HostConfigChangedMsg | HostLeavingMsg | HostStartUpdateMsg | HostCancelUpdateMsg;
137
188
  /**
138
189
  * Emitted by the app as early as possible (synchronous tick of
139
190
  * the embed entry, before the SDK is touched). Tells the host
@@ -175,6 +226,65 @@ export interface EmbedAppStateMsg {
175
226
  * ignore it.
176
227
  */
177
228
  rttMs?: number | null;
229
+ /**
230
+ * Daemon version reported by the robot (`get_version` over the data
231
+ * channel), resolved once the session is up. Additive field (no
232
+ * version bump).
233
+ *
234
+ * The host has no data channel of its own and central exposes no
235
+ * version, so this is the ONLY way a shell can tell whether the robot
236
+ * it just handed the app to is running current software. The web
237
+ * shell uses it to gate on a minimum supported version; hosts that
238
+ * don't care ignore it.
239
+ *
240
+ * `null` (or omitted) until resolved, or when the daemon predates
241
+ * `get_version`. Receivers MUST treat "unknown" as "fine" - never
242
+ * block a robot just because it stayed silent.
243
+ */
244
+ daemonVersion?: string | null;
245
+ /**
246
+ * Build version of the SDK bundle the app loaded
247
+ * (`ReachyMini.version`, generated at publish time). Additive field
248
+ * (no version bump).
249
+ *
250
+ * The embed rides whatever `window.ReachyMini` the app shipped, so
251
+ * this tells the shell how stale the app's robot stack is - e.g. to
252
+ * warn that behaviour may be off against a newer daemon. `null` (or
253
+ * omitted) when the SDK predates the field - which, unlike
254
+ * `daemonVersion`, is a meaningful signal: every SDK from the release
255
+ * that introduced this field reports it, so silence through a live
256
+ * session means "built before that". Receivers may surface a notice
257
+ * on it, but MUST never block on it.
258
+ */
259
+ sdkVersion?: string | null;
260
+ }
261
+ /**
262
+ * Progress of a `host:start-update` job, relayed from the SDK's
263
+ * `update_progress` stream one message per log line.
264
+ *
265
+ * in_progress : a log line of the install.
266
+ * rebooting : the WebRTC session died while the update was in
267
+ * flight. Synthesised by the embed, NOT by the daemon -
268
+ * a successful install restarts the daemon before it
269
+ * can report anything, so the transport dropping is the
270
+ * success signal. Nothing more will arrive on this
271
+ * channel: the host should tear the iframe down and
272
+ * wait for the robot to reappear on central.
273
+ * done : the daemon explicitly reported completion. Rare (see
274
+ * above), but forwarded when it happens.
275
+ * failed : the daemon declined the job (not wireless, no update
276
+ * available, one already running) or the install raised
277
+ * before the restart.
278
+ */
279
+ export interface EmbedUpdateProgressMsg {
280
+ source: typeof PROTOCOL_SOURCE;
281
+ type: 'embed:update-progress';
282
+ version: 1;
283
+ status: 'in_progress' | 'rebooting' | 'done' | 'failed';
284
+ /** Log line for `in_progress`. */
285
+ line?: string | null;
286
+ /** Reason for `failed`. */
287
+ error?: string | null;
178
288
  }
179
289
  /** App requests to leave (user clicked an in-app exit, error,
180
290
  * ...). The host runs the same tear-down as a top-bar
@@ -184,6 +294,22 @@ export interface EmbedRequestLeaveMsg {
184
294
  type: 'embed:request-leave';
185
295
  version: 1;
186
296
  }
297
+ /**
298
+ * The embed finished its `host:leaving` tear-down: app `onLeave`
299
+ * callbacks ran AND the SDK's host-owned sleep sequence completed
300
+ * (goto_sleep trajectory + motors disabled). Lets the host unmount as
301
+ * soon as the robot is actually asleep instead of after a fixed
302
+ * worst-case timeout - and, because the WebRTC session is still up when
303
+ * this fires, the motors are already `Disabled` before the app-slot lock
304
+ * frees, so the daemon's idle-reset sees "already asleep" and does NOT
305
+ * replay a second goto_sleep (no double trajectory / go_sleep sound).
306
+ * The host still force-unmounts after its own cap if this never arrives.
307
+ */
308
+ export interface EmbedLeftMsg {
309
+ source: typeof PROTOCOL_SOURCE;
310
+ type: 'embed:left';
311
+ version: 1;
312
+ }
187
313
  /** App-level error report. `fatal: true` switches the host to
188
314
  * ErrorView; `fatal: false` is logged and may surface a toast. */
189
315
  export interface EmbedErrorMsg {
@@ -194,7 +320,7 @@ export interface EmbedErrorMsg {
194
320
  fatal: boolean;
195
321
  detail?: unknown;
196
322
  }
197
- export type EmbedToHostMsg = EmbedReadyMsg | EmbedAppStateMsg | EmbedRequestLeaveMsg | EmbedErrorMsg;
323
+ export type EmbedToHostMsg = EmbedReadyMsg | EmbedAppStateMsg | EmbedRequestLeaveMsg | EmbedLeftMsg | EmbedErrorMsg | EmbedUpdateProgressMsg;
198
324
  /**
199
325
  * Serialised credentials passed from the host (Mode A) or the
200
326
  * mobile app (Mode B) to the embed via the URL hash fragment.
@@ -211,6 +337,14 @@ export interface CredsBundle {
211
337
  hfToken?: string | null;
212
338
  userName?: string | null;
213
339
  robotPeerId: string;
340
+ /** Stable hardware id of the selected robot (central
341
+ * `meta.hardware_id`), when the daemon exposes one. Lets the embed
342
+ * re-resolve the CURRENT `robotPeerId` from central just before
343
+ * dialing, so a rotated peer id (long iframe cold-start, relay
344
+ * reconnect) doesn't strand the app on a dead producer. Omitted for
345
+ * daemons too old to advertise it - the embed then uses
346
+ * `robotPeerId` as-is. */
347
+ robotHardwareId?: string | null;
214
348
  signalingUrl: string;
215
349
  theme: ThemeMode;
216
350
  config: ConfigPayload;
@@ -1 +1 @@
1
- {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/lib/protocol.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"protocol.d.ts","sourceRoot":"","sources":["../../src/lib/protocol.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Offline-first latch for the daemon-update reboot watch.
3
+ *
4
+ * Problem: when the daemon restarts mid-update, the shell tears the
5
+ * iframe down and waits for the robot to reappear on central before
6
+ * telling `DaemonUpdateGate` the update is done. But central can keep
7
+ * the robot's PRE-reboot registration listed for a while (the SSE
8
+ * offline event or the listing TTL hasn't fired yet), so "the robot is
9
+ * listed" alone reads the STALE entry as "back online" seconds after
10
+ * the reboot started - the gate then declares success while the robot
11
+ * is still down, and the user re-picks a dead robot.
12
+ *
13
+ * Peer-id exclusion doesn't fix this: the embed re-resolves the live
14
+ * peer id right before dialing, so the shell doesn't reliably know
15
+ * which id the stale listing carries. What IS reliable is the order of
16
+ * observations: the dying relay connection makes central drop the old
17
+ * registration (realtime SSE `roles: []` event), and only then does the
18
+ * rebooted daemon re-register under a fresh id. So the watch requires
19
+ * seeing the robot ABSENT from the list at least once (the sticky
20
+ * `sawTargetOffline` boolean) before accepting its presence as "back
21
+ * online". Callers must skip observations while the list is still in
22
+ * its initial-loading window, where an empty array means "no data yet".
23
+ *
24
+ * If central never drops the stale entry (not observed in practice -
25
+ * the relay socket dies with the daemon), the watch never completes and
26
+ * the gate's reboot timeout produces an honest "check your robot"
27
+ * message instead of a false success.
28
+ */
29
+ import type { RobotInfo } from './sdk-types';
30
+ /** What the shell remembers about the robot it dropped mid-update. */
31
+ export interface RebootTarget {
32
+ /** Stable hardware id (serial), the strongest match. */
33
+ hardwareId: string | null;
34
+ /** Advertised name, fallback for daemons without a hardware id. */
35
+ name: string | null;
36
+ }
37
+ /**
38
+ * Is the rebooting robot present in the central listing? Matched on
39
+ * hardware id first (survives the peer-id rotation), then name. With
40
+ * neither identity (older daemon), fall back to "any robot listed":
41
+ * combined with the offline-first latch this means "the list emptied,
42
+ * then repopulated", which is correct for single-robot accounts and
43
+ * degrades to the gate's reboot timeout on multi-robot ones.
44
+ */
45
+ export declare function isTargetListed(robots: readonly RobotInfo[], target: RebootTarget): boolean;
46
+ /** Sticky offline latch, advanced on every settled observation: once
47
+ * the target has been seen absent, it stays seen. The watch completes
48
+ * when `sawTargetOffline(...) && isTargetListed(...)` - never on a run
49
+ * of presence-only observations (the stale-listing regression). */
50
+ export declare function sawTargetOffline(prev: boolean, targetListed: boolean): boolean;
51
+ //# sourceMappingURL=rebootWatch.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rebootWatch.d.ts","sourceRoot":"","sources":["../../src/lib/rebootWatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,sEAAsE;AACtE,MAAM,WAAW,YAAY;IAC3B,wDAAwD;IACxD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,mEAAmE;IACnE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;CACrB;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,SAAS,SAAS,EAAE,EAC5B,MAAM,EAAE,YAAY,GACnB,OAAO,CAMT;AAED;;;oEAGoE;AACpE,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,OAAO,EAAE,YAAY,EAAE,OAAO,GAAG,OAAO,CAE9E"}
@@ -10,7 +10,7 @@
10
10
  * `ts/lib/types.ts` (re-exported from `ts/reachy-mini-sdk.ts`) — the
11
11
  * shipped `.d.ts` is auto-generated by `tsc` from that source.
12
12
  */
13
- export type { RobotInfo, RobotState, ReachyMiniOptions, ReachyMiniInstance, ReachyMiniConstructor, AutoConnectOptions, AutoConnectRobotChoice, AutoConnectResult, MotionAwaitOptions, SubscribeLogsOptions, } from '@pollen-robotics/reachy-mini-sdk';
13
+ export type { RobotInfo, RobotState, ReachyMiniOptions, ReachyMiniInstance, ReachyMiniConstructor, AutoConnectOptions, AutoConnectRobotChoice, AutoConnectResult, MotionAwaitOptions, SubscribeLogsOptions, StartDaemonUpdateOptions, UpdateProgressEvent, } from '@pollen-robotics/reachy-mini-sdk';
14
14
  import type { ReachyMiniConstructor } from '@pollen-robotics/reachy-mini-sdk';
15
15
  /**
16
16
  * Host-internal global augmentation: the host shell instantiates
@@ -1 +1 @@
1
- {"version":3,"file":"sdk-types.d.ts","sourceRoot":"","sources":["../../src/lib/sdk-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EACV,SAAS,EACT,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,GACrB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAE9E;;;;;;;;;;;;;;GAcG;AACH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,EAAE,qBAAqB,CAAC;QAClC;gEACwD;QACxD,WAAW,CAAC,EAAE;YACZ,SAAS,CAAC,EAAE;gBACV,eAAe,CAAC,EAAE,MAAM,CAAC;gBACzB,YAAY,CAAC,EAAE,MAAM,CAAC;gBACtB,UAAU,CAAC,EAAE,MAAM,CAAC;gBACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;aACnB,CAAC;SACH,CAAC;KACH;CACF;AAED,OAAO,EAAE,CAAC"}
1
+ {"version":3,"file":"sdk-types.d.ts","sourceRoot":"","sources":["../../src/lib/sdk-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EACV,SAAS,EACT,UAAU,EACV,iBAAiB,EACjB,kBAAkB,EAClB,qBAAqB,EACrB,kBAAkB,EAClB,sBAAsB,EACtB,iBAAiB,EACjB,kBAAkB,EAClB,oBAAoB,EACpB,wBAAwB,EACxB,mBAAmB,GACpB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,kCAAkC,CAAC;AAE9E;;;;;;;;;;;;;;GAcG;AACH,OAAO,CAAC,MAAM,CAAC;IACb,UAAU,MAAM;QACd,UAAU,EAAE,qBAAqB,CAAC;QAClC;gEACwD;QACxD,WAAW,CAAC,EAAE;YACZ,SAAS,CAAC,EAAE;gBACV,eAAe,CAAC,EAAE,MAAM,CAAC;gBACzB,YAAY,CAAC,EAAE,MAAM,CAAC;gBACtB,UAAU,CAAC,EAAE,MAAM,CAAC;gBACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;aACnB,CAAC;SACH,CAAC;KACH;CACF;AAED,OAAO,EAAE,CAAC"}
@@ -0,0 +1,78 @@
1
+ /**
2
+ * SDK staleness self-check for the embed runtime.
3
+ *
4
+ * Why this exists
5
+ * ───────────────
6
+ * A published Space bundles its SDK at build time and is never
7
+ * rebuilt: if a bundled SDK generation ever becomes incompatible with
8
+ * the deployed daemons, there is NO channel to tell the users of those
9
+ * frozen apps - except code that ships inside the app itself. This
10
+ * module is that channel. It runs inside the iframe, so every host
11
+ * (web shell, mobile WebView, anything future) is covered with zero
12
+ * parent integration. The one case it structurally cannot cover is a
13
+ * bundle that predates this very check; an independently-updated
14
+ * parent catches that instead (the mobile app, via the absence of
15
+ * `sdkVersion` on app-state).
16
+ *
17
+ * Why it is this conservative (learned in #1316 review)
18
+ * ─────────────────────────────────────────────────────
19
+ * The first cut of this feature was the "global kill switch" the
20
+ * daemon update policy explicitly argues against: it warned on ANY
21
+ * version gap, full-screen, on every reload, comparing the npm bundle
22
+ * version against the DAEMON's GitHub release tag - two version lines
23
+ * that only agree while they happen to be tagged together. This
24
+ * version inverts every one of those choices:
25
+ *
26
+ * - source of truth: the npm registry entry of the package itself.
27
+ * The registry is what apps actually install from, so it cannot
28
+ * drift from the bundle's own version line.
29
+ * - trigger: MAJOR gap only. A major bump is the only signal that
30
+ * the app/robot contract may genuinely no longer hold; minor and
31
+ * patch releases must never nag a working app.
32
+ * - dismissal: persisted (localStorage). One "I understand" covers
33
+ * everything up to the dismissed major; the overlay returns only
34
+ * when a NEWER major ships.
35
+ * - theming: follows `prefers-color-scheme` instead of hardcoding a
36
+ * light card into apps that may be dark.
37
+ *
38
+ * Fail-open discipline: unparseable versions, dev placeholders
39
+ * (`0.x`), an unreachable registry - all resolve to "stay silent".
40
+ * Advisory code must never cost the user their app.
41
+ */
42
+ /**
43
+ * Resolve the latest published SDK version from the npm registry's
44
+ * `latest` dist-tag (a ~200-byte JSON, CORS-enabled), or `null` when
45
+ * it can't be determined. Cached in localStorage for `TTL_MS`; a
46
+ * failed refresh serves the stale value rather than flapping to null.
47
+ * Never throws.
48
+ */
49
+ export declare function fetchLatestSdkVersion(): Promise<string | null>;
50
+ /**
51
+ * Should the overlay show for this current/latest pair? True only
52
+ * when BOTH versions parse, the current one is a real release (not a
53
+ * `0.x` dev placeholder), and the latest is a strictly NEWER MAJOR.
54
+ * Anything unknown resolves to false.
55
+ */
56
+ export declare function isSdkMajorBehind(current: string | null | undefined, latest: string | null | undefined): boolean;
57
+ /**
58
+ * A dismissal covers every release up to the dismissed latest's
59
+ * major: the overlay returns only when a newer major ships than the
60
+ * one the user already acknowledged.
61
+ */
62
+ export declare function isSdkWarningDismissed(latest: string | null | undefined): boolean;
63
+ export declare function dismissSdkWarning(latest: string): void;
64
+ /**
65
+ * Fire-and-forget entry point for the embed: check, and show the
66
+ * overlay when (and only when) the bundled SDK is a full major behind
67
+ * the npm registry and the user hasn't already acknowledged that
68
+ * major. Never throws, never blocks the session.
69
+ */
70
+ export declare function maybeWarnSdkStale(currentVersion: string | null): Promise<void>;
71
+ /**
72
+ * Plain-DOM overlay (the embed is framework-agnostic). Colors follow
73
+ * `prefers-color-scheme` - an advisory card must not hardcode a light
74
+ * theme into an app that may be dark. Exported for tests; production
75
+ * code goes through `maybeWarnSdkStale`.
76
+ */
77
+ export declare function showSdkStalenessOverlay(current: string, latest: string): void;
78
+ //# sourceMappingURL=sdkStaleness.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sdkStaleness.d.ts","sourceRoot":"","sources":["../../src/lib/sdkStaleness.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwCG;AA6CH;;;;;;GAMG;AACH,wBAAsB,qBAAqB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAgBpE;AAID;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAC9B,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,EAClC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC,OAAO,CAMT;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAChC,OAAO,CAUT;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAMtD;AAID;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,cAAc,EAAE,MAAM,GAAG,IAAI,GAC5B,OAAO,CAAC,IAAI,CAAC,CASf;AAWD;;;;;GAKG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,MAAM,GACb,IAAI,CA2DN"}
@@ -12,12 +12,14 @@
12
12
  *
13
13
  * Storage rules (APP_CREATION_GUIDE §13.5.2 hash-only creds):
14
14
  * - HF token + user name live in `sessionStorage` (tab-scoped,
15
- * wiped on close). The Reachy Mini SDK reads them from
16
- * `hf_token` / `hf_username` / `hf_token_expires`.
15
+ * dies with the tab). The Reachy Mini SDK owns those keys via its
16
+ * token-store (`hf_token` / `hf_username` / `hf_token_expires` /
17
+ * `hf_token_last_seen`), which bounds their lifetime with the OAuth
18
+ * expiry plus a sliding idle window.
17
19
  * - Persistent flags (`hf_oauth_client_id`, `oauth-pending`,
18
- * `signed-out`) live in `localStorage`.
19
- * - Tokens have a 15 min TTL by default; the SDK refreshes
20
- * on demand if a real OAuth refresh flow is wired.
20
+ * `signed-out`) live in `localStorage`; the one-shot silent-auth
21
+ * attempt flag lives in `sessionStorage` (must survive the OAuth
22
+ * redirect round trip but reset with a fresh tab).
21
23
  */
22
24
  export type ClientIdSource = 'none' | 'caller' | 'space' | 'localStorage';
23
25
  export interface DevTokenConfig {
@@ -59,6 +61,15 @@ export declare function markUserSignedOut(): void;
59
61
  export declare function clearSignedOutFlag(): void;
60
62
  /** Returns `true` if the user explicitly signed out earlier. */
61
63
  export declare function isUserSignedOut(): boolean;
64
+ /** Mark that this tab already attempted a silent sign-in
65
+ * (`login({ prompt: 'none' })`), successful or not. Read before
66
+ * auto-redirecting so a declined silent auth can never loop. */
67
+ export declare function markSilentAuthAttempted(): void;
68
+ /** `true` if this tab already attempted a silent sign-in. */
69
+ export declare function hasSilentAuthAttempted(): boolean;
70
+ /** Re-arm the silent attempt (explicit "Sign in" click), so a future
71
+ * boot in this tab may try silently again. */
72
+ export declare function clearSilentAuthAttempted(): void;
62
73
  /** Wipe HF token keys from sessionStorage. Called by the embed
63
74
  * on `host:leaving` before the iframe unmounts
64
75
  * (APP_CREATION_GUIDE §13.5.2). */
@@ -1 +1 @@
1
- {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/lib/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAYH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,QAAQ,GACR,OAAO,GACP,cAAc,CAAC;AAEnB,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW3E;AAED;wDACwD;AACxD,wBAAgB,qBAAqB,CACnC,cAAc,CAAC,EAAE,MAAM,GACtB;IAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAW1D;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMpD;AAWD;8CAC8C;AAC9C,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAYzD;AAED;;2BAE2B;AAC3B,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;iCAEiC;AACjC,wBAAgB,iBAAiB,IAAI,OAAO,CAI3C;AAID;mEACmE;AACnE,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC;AAED;+DAC+D;AAC/D,wBAAgB,mBAAmB,IAAI,OAAO,CAQ7C;AAID;+DAC+D;AAC/D,wBAAgB,iBAAiB,IAAI,IAAI,CAMxC;AAED;iCACiC;AACjC,wBAAgB,kBAAkB,IAAI,IAAI,CAMzC;AAED,gEAAgE;AAChE,wBAAgB,eAAe,IAAI,OAAO,CAMzC;AAID;;oCAEoC;AACpC,wBAAgB,oBAAoB,IAAI,IAAI,CAQ3C;AAID;;sDAEsD;AACtD,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,IAAI,CAUrD"}
1
+ {"version":3,"file":"settings.d.ts","sourceRoot":"","sources":["../../src/lib/settings.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAgBH,MAAM,MAAM,cAAc,GACtB,MAAM,GACN,QAAQ,GACR,OAAO,GACP,cAAc,CAAC;AAEnB,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;CAClB;AAID,0DAA0D;AAC1D,wBAAgB,eAAe,CAAC,cAAc,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAW3E;AAED;wDACwD;AACxD,wBAAgB,qBAAqB,CACnC,cAAc,CAAC,EAAE,MAAM,GACtB;IAAE,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAAC,MAAM,EAAE,cAAc,CAAA;CAAE,CAW1D;AAED,sDAAsD;AACtD,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAMpD;AAWD;8CAC8C;AAC9C,wBAAgB,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAYzD;AAED;;2BAE2B;AAC3B,wBAAgB,iBAAiB,IAAI,OAAO,CAE3C;AAED;;iCAEiC;AACjC,wBAAgB,iBAAiB,IAAI,OAAO,CAI3C;AAID;mEACmE;AACnE,wBAAgB,gBAAgB,IAAI,IAAI,CAMvC;AAED;+DAC+D;AAC/D,wBAAgB,mBAAmB,IAAI,OAAO,CAQ7C;AAID;+DAC+D;AAC/D,wBAAgB,iBAAiB,IAAI,IAAI,CAMxC;AAED;iCACiC;AACjC,wBAAgB,kBAAkB,IAAI,IAAI,CAMzC;AAED,gEAAgE;AAChE,wBAAgB,eAAe,IAAI,OAAO,CAMzC;AAID;;iEAEiE;AACjE,wBAAgB,uBAAuB,IAAI,IAAI,CAM9C;AAED,6DAA6D;AAC7D,wBAAgB,sBAAsB,IAAI,OAAO,CAMhD;AAED;+CAC+C;AAC/C,wBAAgB,wBAAwB,IAAI,IAAI,CAM/C;AAID;;oCAEoC;AACpC,wBAAgB,oBAAoB,IAAI,IAAI,CAS3C;AAID;;sDAEsD;AACtD,wBAAgB,aAAa,CAAC,CAAC,GAAG,OAAO,KAAK,CAAC,GAAG,IAAI,CAUrD"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pollen-robotics/reachy-mini-sdk",
3
- "version": "1.10.0-dev.0.main.b50d97e",
3
+ "version": "1.10.0-dev.0.main.c8367cd",
4
4
  "description": "Browser SDK for controlling a Reachy Mini robot over WebRTC, plus an optional Hugging Face Spaces host shell (OAuth + robot picker + iframe bridge) exposed via the ./host subpath.",
5
5
  "type": "module",
6
6
  "main": "./dist/reachy-mini-sdk.js",
@@ -45,6 +45,8 @@
45
45
  ],
46
46
  "sideEffects": false,
47
47
  "scripts": {
48
+ "gen:version": "node scripts/gen-version.mjs",
49
+ "prebuild:sdk": "node scripts/gen-version.mjs",
48
50
  "build:sdk": "tsc -p tsconfig.json",
49
51
  "build:host": "cd host && vite build && tsc -p tsconfig.build.json",
50
52
  "build": "npm run build:sdk && npm run build:host",
@@ -53,14 +55,16 @@
53
55
  "typecheck:host": "cd host && tsc --noEmit",
54
56
  "typecheck": "npm run typecheck:sdk && npm run typecheck:host",
55
57
  "dev": "cd host && vite",
58
+ "test": "vitest run",
59
+ "test:watch": "vitest",
56
60
  "clean": "rm -rf dist host/dist"
57
61
  },
58
62
  "dependencies": {
59
63
  "@emotion/react": "^11.14.0",
60
64
  "@emotion/styled": "^11.14.1",
61
65
  "@huggingface/hub": "^0.15.2",
62
- "@mui/icons-material": "^7.3.5",
63
- "@mui/material": "^7.3.5"
66
+ "@mui/icons-material": "^9.0.0",
67
+ "@mui/material": "^9.0.0"
64
68
  },
65
69
  "peerDependencies": {
66
70
  "react": "^18.0.0 || ^19.0.0",
@@ -75,14 +79,17 @@
75
79
  }
76
80
  },
77
81
  "devDependencies": {
82
+ "@testing-library/react": "^16.3.2",
78
83
  "@types/node": "^25.7.0",
79
84
  "@types/react": "^19.2.14",
80
85
  "@types/react-dom": "^19.2.3",
81
86
  "@vitejs/plugin-react-swc": "^4.0.0",
87
+ "jsdom": "^29.1.1",
82
88
  "react": "^19.2.5",
83
89
  "react-dom": "^19.2.5",
84
90
  "typescript": "^5.7.3",
85
- "vite": "^7.2.1"
91
+ "vite": "^7.2.1",
92
+ "vitest": "^4.1.10"
86
93
  },
87
94
  "keywords": [
88
95
  "reachy",