@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.
- package/APP_CREATION_GUIDE.md +98 -0
- package/dist/lib/central-listener.d.ts +71 -0
- package/dist/lib/central-listener.d.ts.map +1 -0
- package/dist/lib/central-listener.js +277 -0
- package/dist/lib/central-listener.js.map +1 -0
- package/dist/lib/central-listener.test.d.ts +2 -0
- package/dist/lib/central-listener.test.d.ts.map +1 -0
- package/dist/lib/central-listener.test.js +378 -0
- package/dist/lib/central-listener.test.js.map +1 -0
- package/dist/lib/dc-watchdog.test.d.ts +2 -0
- package/dist/lib/dc-watchdog.test.d.ts.map +1 -0
- package/dist/lib/dc-watchdog.test.js +154 -0
- package/dist/lib/dc-watchdog.test.js.map +1 -0
- package/dist/lib/ensure-awake.test.d.ts +2 -0
- package/dist/lib/ensure-awake.test.d.ts.map +1 -0
- package/dist/lib/ensure-awake.test.js +117 -0
- package/dist/lib/ensure-awake.test.js.map +1 -0
- package/dist/lib/ice-grace.test.d.ts +2 -0
- package/dist/lib/ice-grace.test.d.ts.map +1 -0
- package/dist/lib/ice-grace.test.js +211 -0
- package/dist/lib/ice-grace.test.js.map +1 -0
- package/dist/lib/imu.test.d.ts +2 -0
- package/dist/lib/imu.test.d.ts.map +1 -0
- package/dist/lib/imu.test.js +78 -0
- package/dist/lib/imu.test.js.map +1 -0
- package/dist/lib/logger.d.ts +38 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +81 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/pending-replies.d.ts +127 -0
- package/dist/lib/pending-replies.d.ts.map +1 -0
- package/dist/lib/pending-replies.js +255 -0
- package/dist/lib/pending-replies.js.map +1 -0
- package/dist/lib/pending-replies.test.d.ts +2 -0
- package/dist/lib/pending-replies.test.d.ts.map +1 -0
- package/dist/lib/pending-replies.test.js +217 -0
- package/dist/lib/pending-replies.test.js.map +1 -0
- package/dist/lib/reachy-mini.d.ts +202 -88
- package/dist/lib/reachy-mini.d.ts.map +1 -1
- package/dist/lib/reachy-mini.js +651 -566
- package/dist/lib/reachy-mini.js.map +1 -1
- package/dist/lib/redial.test.d.ts +2 -0
- package/dist/lib/redial.test.d.ts.map +1 -0
- package/dist/lib/redial.test.js +206 -0
- package/dist/lib/redial.test.js.map +1 -0
- package/dist/lib/request.test.d.ts +2 -0
- package/dist/lib/request.test.d.ts.map +1 -0
- package/dist/lib/request.test.js +90 -0
- package/dist/lib/request.test.js.map +1 -0
- package/dist/lib/session-supervisor.d.ts +201 -0
- package/dist/lib/session-supervisor.d.ts.map +1 -0
- package/dist/lib/session-supervisor.js +520 -0
- package/dist/lib/session-supervisor.js.map +1 -0
- package/dist/lib/token-store.d.ts +50 -0
- package/dist/lib/token-store.d.ts.map +1 -0
- package/dist/lib/token-store.js +120 -0
- package/dist/lib/token-store.js.map +1 -0
- package/dist/lib/token-store.test.d.ts +2 -0
- package/dist/lib/token-store.test.d.ts.map +1 -0
- package/dist/lib/token-store.test.js +82 -0
- package/dist/lib/token-store.test.js.map +1 -0
- package/dist/lib/types.d.ts +280 -6
- package/dist/lib/types.d.ts.map +1 -1
- package/dist/lib/url-helpers.d.ts.map +1 -1
- package/dist/lib/url-helpers.js +3 -1
- package/dist/lib/url-helpers.js.map +1 -1
- package/dist/lib/version.d.ts +2 -0
- package/dist/lib/version.d.ts.map +1 -0
- package/dist/lib/version.js +5 -0
- package/dist/lib/version.js.map +1 -0
- package/dist/reachy-mini-sdk.d.ts +15 -1
- package/dist/reachy-mini-sdk.d.ts.map +1 -1
- package/dist/reachy-mini-sdk.js +12 -0
- package/dist/reachy-mini-sdk.js.map +1 -1
- package/host/dist/ReachyHost.d.ts.map +1 -1
- package/host/dist/chunks/centralRest-uXNMidlL.js +2315 -0
- package/host/dist/chunks/index-Beu4GNzi.js +821 -0
- package/host/dist/chunks/{mountHost-DjE5zE9R.js → mountHost-BTTF4Uu9.js} +24933 -27488
- package/host/dist/components/ConnectingView.d.ts.map +1 -1
- package/host/dist/components/DaemonUpdateGate.d.ts +65 -0
- package/host/dist/components/DaemonUpdateGate.d.ts.map +1 -0
- package/host/dist/components/ErrorView.d.ts.map +1 -1
- package/host/dist/components/HostErrorBoundary.d.ts +30 -0
- package/host/dist/components/HostErrorBoundary.d.ts.map +1 -0
- package/host/dist/components/LeavingView.d.ts.map +1 -1
- package/host/dist/components/PickerView.d.ts +16 -14
- package/host/dist/components/PickerView.d.ts.map +1 -1
- package/host/dist/components/PostOAuthSplash.d.ts +14 -9
- package/host/dist/components/PostOAuthSplash.d.ts.map +1 -1
- package/host/dist/components/ReachyHostShell.d.ts.map +1 -1
- package/host/dist/components/SignInView.d.ts.map +1 -1
- package/host/dist/components/TopBar.d.ts +5 -6
- package/host/dist/components/TopBar.d.ts.map +1 -1
- package/host/dist/components/WelcomeBackOverlay.d.ts.map +1 -1
- package/host/dist/embed/index.d.ts +68 -5
- package/host/dist/embed/index.d.ts.map +1 -1
- package/host/dist/entry/auto.js +2 -2
- package/host/dist/entry/embed.d.ts +5 -4
- package/host/dist/entry/embed.d.ts.map +1 -1
- package/host/dist/entry/embed.js +5 -4
- package/host/dist/hooks/useHostBridge.d.ts +35 -1
- package/host/dist/hooks/useHostBridge.d.ts.map +1 -1
- package/host/dist/hooks/useLatestDaemonVersion.d.ts +7 -0
- package/host/dist/hooks/useLatestDaemonVersion.d.ts.map +1 -0
- package/host/dist/hooks/useOAuth.d.ts +18 -0
- package/host/dist/hooks/useOAuth.d.ts.map +1 -1
- package/host/dist/hooks/useRobots.d.ts +0 -2
- package/host/dist/hooks/useRobots.d.ts.map +1 -1
- package/host/dist/index.d.ts +2 -2
- package/host/dist/index.d.ts.map +1 -1
- package/host/dist/index.js +11 -10
- package/host/dist/lib/daemonRelease.d.ts +84 -0
- package/host/dist/lib/daemonRelease.d.ts.map +1 -0
- package/host/dist/lib/protocol.d.ts +141 -7
- package/host/dist/lib/protocol.d.ts.map +1 -1
- package/host/dist/lib/rebootWatch.d.ts +51 -0
- package/host/dist/lib/rebootWatch.d.ts.map +1 -0
- package/host/dist/lib/sdk-types.d.ts +1 -1
- package/host/dist/lib/sdk-types.d.ts.map +1 -1
- package/host/dist/lib/sdkStaleness.d.ts +78 -0
- package/host/dist/lib/sdkStaleness.d.ts.map +1 -0
- package/host/dist/lib/settings.d.ts +16 -5
- package/host/dist/lib/settings.d.ts.map +1 -1
- package/package.json +11 -4
- package/host/dist/chunks/index-lMs2sCXG.js +0 -439
- package/host/dist/chunks/reachy-mini-CdsdQLIU.js +0 -1523
- package/host/dist/lib/centralListener.d.ts +0 -73
- package/host/dist/lib/centralListener.d.ts.map +0 -1
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session-scoped persistence of the HF OAuth token.
|
|
3
|
+
*
|
|
4
|
+
* The token lives in `sessionStorage` (tab-scoped, dies with the tab),
|
|
5
|
+
* under keys shared with the host shell and the embed bootstrap. On top
|
|
6
|
+
* of the OAuth expiry, reads enforce a SLIDING IDLE WINDOW: a token
|
|
7
|
+
* whose last successful read is older than `TOKEN_MAX_IDLE_MS` is
|
|
8
|
+
* treated as absent and wiped. That property replaced the host's old
|
|
9
|
+
* "wipe on pagehide" hygiene, which fired on every reload / navigation
|
|
10
|
+
* (pagehide can't tell a refresh from a tab close) and forced a
|
|
11
|
+
* re-login on every F5. The idle window keeps the one attack the wipe
|
|
12
|
+
* actually covered - a tab resurrected days later via session restore
|
|
13
|
+
* gets its sessionStorage back - without punishing reloads.
|
|
14
|
+
*
|
|
15
|
+
* `consumeOAuthErrorParams` supports the silent sign-in flow
|
|
16
|
+
* (`login({ prompt: 'none' })`): a failed silent authorization comes
|
|
17
|
+
* back as `?error=login_required...` query params, which must be
|
|
18
|
+
* stripped from the URL before the app router or a copy-pasted link
|
|
19
|
+
* leaks them around.
|
|
20
|
+
*/
|
|
21
|
+
const KEY_TOKEN = 'hf_token';
|
|
22
|
+
const KEY_USER = 'hf_username';
|
|
23
|
+
const KEY_EXPIRES = 'hf_token_expires';
|
|
24
|
+
const KEY_LAST_SEEN = 'hf_token_last_seen';
|
|
25
|
+
/** Idle cutoff: a cached token unused for longer than this is dropped. */
|
|
26
|
+
export const TOKEN_MAX_IDLE_MS = 24 * 60 * 60 * 1000;
|
|
27
|
+
/** Persist a token bundle and stamp its last-seen time. */
|
|
28
|
+
export function writeStoredToken(bundle) {
|
|
29
|
+
try {
|
|
30
|
+
sessionStorage.setItem(KEY_TOKEN, bundle.token);
|
|
31
|
+
sessionStorage.setItem(KEY_USER, bundle.username);
|
|
32
|
+
sessionStorage.setItem(KEY_EXPIRES, bundle.expires);
|
|
33
|
+
sessionStorage.setItem(KEY_LAST_SEEN, new Date().toISOString());
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
/* ignore - private browsing / quota */
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Read the cached token if it is still usable, refreshing its
|
|
41
|
+
* last-seen stamp (sliding window). Returns `null` - and wipes the
|
|
42
|
+
* store - when the token is expired or idle for too long.
|
|
43
|
+
*
|
|
44
|
+
* A missing last-seen stamp (bundle written by an older SDK, or seeded
|
|
45
|
+
* by the embed's fragment-credentials path) is treated as fresh and
|
|
46
|
+
* stamped now, so a rollout never logs anyone out.
|
|
47
|
+
*/
|
|
48
|
+
export function readUsableToken(now = Date.now()) {
|
|
49
|
+
try {
|
|
50
|
+
const token = sessionStorage.getItem(KEY_TOKEN);
|
|
51
|
+
const username = sessionStorage.getItem(KEY_USER);
|
|
52
|
+
const expires = sessionStorage.getItem(KEY_EXPIRES);
|
|
53
|
+
if (!token || !expires)
|
|
54
|
+
return null;
|
|
55
|
+
if (new Date(expires).getTime() <= now) {
|
|
56
|
+
clearStoredToken();
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
const lastSeen = sessionStorage.getItem(KEY_LAST_SEEN);
|
|
60
|
+
if (lastSeen !== null) {
|
|
61
|
+
const lastSeenMs = new Date(lastSeen).getTime();
|
|
62
|
+
if (!Number.isFinite(lastSeenMs) || now - lastSeenMs > TOKEN_MAX_IDLE_MS) {
|
|
63
|
+
clearStoredToken();
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
sessionStorage.setItem(KEY_LAST_SEEN, new Date(now).toISOString());
|
|
68
|
+
return { token, username: username ?? '', expires };
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
/** Remove every token key from the store. */
|
|
75
|
+
export function clearStoredToken() {
|
|
76
|
+
try {
|
|
77
|
+
sessionStorage.removeItem(KEY_TOKEN);
|
|
78
|
+
sessionStorage.removeItem(KEY_USER);
|
|
79
|
+
sessionStorage.removeItem(KEY_EXPIRES);
|
|
80
|
+
sessionStorage.removeItem(KEY_LAST_SEEN);
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
/* ignore */
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* OAuth error codes a `prompt=none` silent authorization can bounce
|
|
88
|
+
* back with. Gating on this set keeps `consumeOAuthErrorParams` from
|
|
89
|
+
* swallowing an app's own unrelated `?error=` param (and rewriting
|
|
90
|
+
* history for it) on a page load that has nothing to do with OAuth.
|
|
91
|
+
*/
|
|
92
|
+
const OAUTH_ERROR_CODES = new Set([
|
|
93
|
+
'login_required',
|
|
94
|
+
'consent_required',
|
|
95
|
+
'interaction_required',
|
|
96
|
+
'access_denied',
|
|
97
|
+
]);
|
|
98
|
+
/**
|
|
99
|
+
* Detect an OAuth error return (`?error=...` from a `prompt=none`
|
|
100
|
+
* silent authorization), strip the OAuth params from the URL, and
|
|
101
|
+
* return the error code. Returns `null` when the URL carries no OAuth
|
|
102
|
+
* error. Other query params are preserved.
|
|
103
|
+
*/
|
|
104
|
+
export function consumeOAuthErrorParams() {
|
|
105
|
+
try {
|
|
106
|
+
const url = new URL(window.location.href);
|
|
107
|
+
const error = url.searchParams.get('error');
|
|
108
|
+
if (error === null || !OAUTH_ERROR_CODES.has(error))
|
|
109
|
+
return null;
|
|
110
|
+
url.searchParams.delete('error');
|
|
111
|
+
url.searchParams.delete('error_description');
|
|
112
|
+
url.searchParams.delete('state');
|
|
113
|
+
window.history.replaceState(null, '', url.toString());
|
|
114
|
+
return error;
|
|
115
|
+
}
|
|
116
|
+
catch {
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=token-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.js","sourceRoot":"","sources":["../../lib/token-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.test.d.ts","sourceRoot":"","sources":["../../lib/token-store.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Token-store tests: the sliding idle window that replaced the host's
|
|
3
|
+
* pagehide wipe, and the OAuth-error URL scrubbing used by the silent
|
|
4
|
+
* sign-in flow.
|
|
5
|
+
*
|
|
6
|
+
* The property that must hold: a token can NEVER be served past its
|
|
7
|
+
* OAuth expiry, nor after sitting unused longer than the idle window -
|
|
8
|
+
* but a plain reload (seconds of idle) always keeps the session.
|
|
9
|
+
*/
|
|
10
|
+
import { afterEach, describe, expect, it } from 'vitest';
|
|
11
|
+
import { TOKEN_MAX_IDLE_MS, clearStoredToken, consumeOAuthErrorParams, readUsableToken, writeStoredToken, } from './token-store.js';
|
|
12
|
+
const NOW = 1_700_000_000_000;
|
|
13
|
+
function seed(opts) {
|
|
14
|
+
const expiresInMs = opts?.expiresInMs ?? 60 * 60 * 1000;
|
|
15
|
+
writeStoredToken({
|
|
16
|
+
token: 'hf_test_token',
|
|
17
|
+
username: 'thibaud',
|
|
18
|
+
expires: new Date(NOW + expiresInMs).toISOString(),
|
|
19
|
+
});
|
|
20
|
+
if (opts?.lastSeenAgoMs === null) {
|
|
21
|
+
sessionStorage.removeItem('hf_token_last_seen');
|
|
22
|
+
}
|
|
23
|
+
else if (opts?.lastSeenAgoMs !== undefined) {
|
|
24
|
+
sessionStorage.setItem('hf_token_last_seen', new Date(NOW - opts.lastSeenAgoMs).toISOString());
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
clearStoredToken();
|
|
29
|
+
window.history.replaceState(null, '', '/');
|
|
30
|
+
});
|
|
31
|
+
describe('readUsableToken', () => {
|
|
32
|
+
it('round-trips a fresh token and refreshes its last-seen stamp', () => {
|
|
33
|
+
seed({ lastSeenAgoMs: 60_000 });
|
|
34
|
+
const read = readUsableToken(NOW);
|
|
35
|
+
expect(read).toEqual({
|
|
36
|
+
token: 'hf_test_token',
|
|
37
|
+
username: 'thibaud',
|
|
38
|
+
expires: new Date(NOW + 60 * 60 * 1000).toISOString(),
|
|
39
|
+
});
|
|
40
|
+
expect(sessionStorage.getItem('hf_token_last_seen')).toBe(new Date(NOW).toISOString());
|
|
41
|
+
});
|
|
42
|
+
it('returns null when nothing is stored', () => {
|
|
43
|
+
expect(readUsableToken(NOW)).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
it('drops an expired token and wipes the store', () => {
|
|
46
|
+
seed({ expiresInMs: -1 });
|
|
47
|
+
expect(readUsableToken(NOW)).toBeNull();
|
|
48
|
+
expect(sessionStorage.getItem('hf_token')).toBeNull();
|
|
49
|
+
});
|
|
50
|
+
it('drops a token idle for longer than the window (session restore)', () => {
|
|
51
|
+
seed({ lastSeenAgoMs: TOKEN_MAX_IDLE_MS + 1 });
|
|
52
|
+
expect(readUsableToken(NOW)).toBeNull();
|
|
53
|
+
expect(sessionStorage.getItem('hf_token')).toBeNull();
|
|
54
|
+
});
|
|
55
|
+
it('keeps a token idle for less than the window (plain reload)', () => {
|
|
56
|
+
seed({ lastSeenAgoMs: TOKEN_MAX_IDLE_MS - 1 });
|
|
57
|
+
expect(readUsableToken(NOW)).not.toBeNull();
|
|
58
|
+
});
|
|
59
|
+
it('accepts and stamps a legacy bundle without a last-seen key', () => {
|
|
60
|
+
seed({ lastSeenAgoMs: null });
|
|
61
|
+
expect(readUsableToken(NOW)).not.toBeNull();
|
|
62
|
+
expect(sessionStorage.getItem('hf_token_last_seen')).toBe(new Date(NOW).toISOString());
|
|
63
|
+
});
|
|
64
|
+
it('drops a token whose last-seen stamp is unparsable', () => {
|
|
65
|
+
seed();
|
|
66
|
+
sessionStorage.setItem('hf_token_last_seen', 'not-a-date');
|
|
67
|
+
expect(readUsableToken(NOW)).toBeNull();
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
describe('consumeOAuthErrorParams', () => {
|
|
71
|
+
it('returns null and leaves the URL alone without an error param', () => {
|
|
72
|
+
window.history.replaceState(null, '', '/?config=abc');
|
|
73
|
+
expect(consumeOAuthErrorParams()).toBeNull();
|
|
74
|
+
expect(window.location.search).toBe('?config=abc');
|
|
75
|
+
});
|
|
76
|
+
it('consumes a silent-auth decline and scrubs only the OAuth params', () => {
|
|
77
|
+
window.history.replaceState(null, '', '/?config=abc&error=login_required&error_description=User+must+be+logged+in&state=xyz');
|
|
78
|
+
expect(consumeOAuthErrorParams()).toBe('login_required');
|
|
79
|
+
expect(window.location.search).toBe('?config=abc');
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
//# sourceMappingURL=token-store.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"token-store.test.js","sourceRoot":"","sources":["../../lib/token-store.test.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/types.d.ts
CHANGED
|
@@ -44,16 +44,57 @@ export interface FaceTarget {
|
|
|
44
44
|
roll?: number | null;
|
|
45
45
|
ts?: number | null;
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Sound Direction of Arrival from the robot's ReSpeaker microphone array.
|
|
49
|
+
* Streamed inside `RobotState` (wire shape), so it rides the same ~30 Hz
|
|
50
|
+
* pose push / `get_state` replies as the pose - no extra round-trip.
|
|
51
|
+
*/
|
|
52
|
+
export interface DoA {
|
|
53
|
+
/**
|
|
54
|
+
* Angle in radians (ReSpeaker convention): `0` = left, `π/2` =
|
|
55
|
+
* front/back, `π` = right.
|
|
56
|
+
*/
|
|
57
|
+
angle: number;
|
|
58
|
+
/** Whether the array currently detects speech coming from that angle. */
|
|
59
|
+
speech_detected: boolean;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* One IMU reading from the robot's BMI088 (wireless version only).
|
|
63
|
+
* Wire shape of the daemon's `ImuDataMsg`, minus the `type` envelope.
|
|
64
|
+
*/
|
|
65
|
+
export interface ImuData {
|
|
66
|
+
/** [x, y, z] linear acceleration, m/s². */
|
|
67
|
+
accelerometer: number[];
|
|
68
|
+
/** [x, y, z] angular velocity, rad/s. */
|
|
69
|
+
gyroscope: number[];
|
|
70
|
+
/** [w, x, y, z] orientation estimate. */
|
|
71
|
+
quaternion: number[];
|
|
72
|
+
/** Sensor die temperature, °C. */
|
|
73
|
+
temperature: number;
|
|
74
|
+
}
|
|
47
75
|
export interface RobotState {
|
|
48
76
|
/** Flat row-major 4×4 head pose (16 numbers). */
|
|
49
77
|
head?: number[];
|
|
50
78
|
/** [rightRad, leftRad]. */
|
|
51
79
|
antennas?: number[];
|
|
80
|
+
/**
|
|
81
|
+
* Per-motor head joint values (7 numbers, body yaw at [0]), streamed by
|
|
82
|
+
* the daemon alongside `head` so clients can check the physical pose
|
|
83
|
+
* motor by motor (e.g. the first wake-up wizard's sleep-position check).
|
|
84
|
+
* The antennas need no twin field: `antennas` already is the two motor
|
|
85
|
+
* values. Absent on daemons that predate the field.
|
|
86
|
+
*/
|
|
87
|
+
head_joint_positions?: number[];
|
|
52
88
|
/** Radians. */
|
|
53
89
|
body_yaw?: number;
|
|
54
90
|
motor_mode?: 'enabled' | 'disabled' | 'gravity_compensation';
|
|
55
91
|
is_move_running?: boolean;
|
|
56
92
|
face_target?: FaceTarget;
|
|
93
|
+
/**
|
|
94
|
+
* Sound Direction of Arrival, or absent when the robot has no mic array
|
|
95
|
+
* (or no reading yet). Refreshed by the daemon at ~10 Hz.
|
|
96
|
+
*/
|
|
97
|
+
doa?: DoA;
|
|
57
98
|
}
|
|
58
99
|
/** SDK constructor options. */
|
|
59
100
|
export interface ReachyMiniOptions {
|
|
@@ -82,6 +123,40 @@ export interface ReachyMiniOptions {
|
|
|
82
123
|
* per page load; the host iframe relies on this to skip the picker.
|
|
83
124
|
*/
|
|
84
125
|
autoStartFromUrl?: boolean;
|
|
126
|
+
/**
|
|
127
|
+
* Automatic session re-dial (default `true`). When an ESTABLISHED
|
|
128
|
+
* session's transport dies for good (ICE `failed` past its grace
|
|
129
|
+
* window, stuck `disconnected`, or expired while the tab was
|
|
130
|
+
* backgrounded), the SDK tears the dead RTCPeerConnection down and
|
|
131
|
+
* re-dials the same robot through the signaling server — up to 5
|
|
132
|
+
* attempts with backoff (~22 s window). The app sees
|
|
133
|
+
* `sessionReconnecting` per attempt, then either `sessionReconnected`
|
|
134
|
+
* followed by a fresh `streaming` event, or a terminal
|
|
135
|
+
* `sessionStopped` with reason `reconnect_failed`. Failures during
|
|
136
|
+
* the INITIAL `startSession()` handshake still reject the promise —
|
|
137
|
+
* the caller owns that retry. Set to `false` to restore the previous
|
|
138
|
+
* fatal-`error` behaviour.
|
|
139
|
+
*/
|
|
140
|
+
autoReconnect?: boolean;
|
|
141
|
+
}
|
|
142
|
+
/** Options accepted by `login()`. */
|
|
143
|
+
export interface LoginOptions {
|
|
144
|
+
/**
|
|
145
|
+
* OIDC prompt behaviour for the OAuth redirect.
|
|
146
|
+
*
|
|
147
|
+
* `'none'` performs a SILENT authorization: a user who is logged in
|
|
148
|
+
* to Hugging Face and has already authorized the app comes straight
|
|
149
|
+
* back with a code (no screen, no interaction); everyone else comes
|
|
150
|
+
* back immediately with `?error=login_required` /
|
|
151
|
+
* `consent_required` instead of landing on the HF login page. The
|
|
152
|
+
* error return is consumed and stripped by `authenticate()`, which
|
|
153
|
+
* then simply reports "not signed in". Supported by the HF
|
|
154
|
+
* authorize endpoint although absent from its discovery metadata
|
|
155
|
+
* (verified empirically).
|
|
156
|
+
*
|
|
157
|
+
* Omit for the regular interactive flow.
|
|
158
|
+
*/
|
|
159
|
+
prompt?: 'none';
|
|
85
160
|
}
|
|
86
161
|
/** Options accepted by `autoConnect()`. */
|
|
87
162
|
export interface AutoConnectOptions {
|
|
@@ -136,6 +211,35 @@ export interface SubscribeLogsOptions {
|
|
|
136
211
|
}) => void;
|
|
137
212
|
onError?: (error: string) => void;
|
|
138
213
|
}
|
|
214
|
+
/** `request()` options. */
|
|
215
|
+
export interface RequestOptions {
|
|
216
|
+
/**
|
|
217
|
+
* Milliseconds to wait for a matching reply before fail-opening to
|
|
218
|
+
* `null`. Defaults to the SDK's shared command round-trip timeout (4 s).
|
|
219
|
+
*/
|
|
220
|
+
timeoutMs?: number;
|
|
221
|
+
/**
|
|
222
|
+
* Override the reply matcher. Default: the first robot message whose
|
|
223
|
+
* `command` field equals the sent command's `type` (the daemon's
|
|
224
|
+
* reply convention).
|
|
225
|
+
*/
|
|
226
|
+
match?: (msg: Record<string, unknown>) => boolean;
|
|
227
|
+
}
|
|
228
|
+
/** One progress event from an in-flight `startDaemonUpdate()`. */
|
|
229
|
+
export interface UpdateProgressEvent {
|
|
230
|
+
status: 'in_progress' | 'done' | 'failed';
|
|
231
|
+
/** A log line of the underlying update job (when `in_progress`). */
|
|
232
|
+
line?: string;
|
|
233
|
+
/** Set when `status === 'failed'`. */
|
|
234
|
+
error?: string;
|
|
235
|
+
}
|
|
236
|
+
/** `startDaemonUpdate()` argument. */
|
|
237
|
+
export interface StartDaemonUpdateOptions {
|
|
238
|
+
/** Install the latest pre-release instead of the latest stable. */
|
|
239
|
+
preRelease?: boolean;
|
|
240
|
+
/** Receives one event per log line of the update job. */
|
|
241
|
+
onProgress?: (event: UpdateProgressEvent) => void;
|
|
242
|
+
}
|
|
139
243
|
/** Single XVF3800 parameter write. */
|
|
140
244
|
export interface AudioConfigEntry {
|
|
141
245
|
name: string;
|
|
@@ -247,6 +351,19 @@ export interface NetworkChangeEventDetail {
|
|
|
247
351
|
rtt?: number;
|
|
248
352
|
saveData?: boolean;
|
|
249
353
|
}
|
|
354
|
+
/**
|
|
355
|
+
* One auto-reconnect attempt is starting (fires once per attempt,
|
|
356
|
+
* BEFORE its backoff wait). Show a "Reconnecting…" UI on the first one
|
|
357
|
+
* and keep it until `sessionReconnected` or a `sessionStopped` with
|
|
358
|
+
* reason `reconnect_failed`. Motion/RPC calls made while reconnecting
|
|
359
|
+
* fail exactly as they would on a stopped session.
|
|
360
|
+
*/
|
|
361
|
+
export interface SessionReconnectingEventDetail {
|
|
362
|
+
attempt: number;
|
|
363
|
+
maxAttempts: number;
|
|
364
|
+
/** Human-readable description of what killed the transport. */
|
|
365
|
+
cause: string;
|
|
366
|
+
}
|
|
250
367
|
/** Map of event names to their detail shapes. */
|
|
251
368
|
export interface ReachyMiniEventMap {
|
|
252
369
|
connected: CustomEvent<ConnectedEventDetail>;
|
|
@@ -263,6 +380,18 @@ export interface ReachyMiniEventMap {
|
|
|
263
380
|
networkOnline: CustomEvent<Record<string, never>>;
|
|
264
381
|
networkOffline: CustomEvent<Record<string, never>>;
|
|
265
382
|
networkChange: CustomEvent<NetworkChangeEventDetail>;
|
|
383
|
+
sessionReconnecting: CustomEvent<SessionReconnectingEventDetail>;
|
|
384
|
+
/**
|
|
385
|
+
* Auto-reconnect succeeded; the detail is the 1-based attempt that
|
|
386
|
+
* got through. A fresh `streaming` event fires alongside (same
|
|
387
|
+
* robot, new sessionId); video re-attaches automatically through
|
|
388
|
+
* the `videoTrack` listener installed by `attachVideo()`. App-level
|
|
389
|
+
* state that lives daemon-side per session (motor mode override, a
|
|
390
|
+
* replaced audio sender track, …) must be re-asserted by the app.
|
|
391
|
+
*/
|
|
392
|
+
sessionReconnected: CustomEvent<{
|
|
393
|
+
attempt: number;
|
|
394
|
+
}>;
|
|
266
395
|
}
|
|
267
396
|
/** Public surface of a ReachyMini SDK instance. */
|
|
268
397
|
export interface ReachyMiniInstance extends EventTarget {
|
|
@@ -282,8 +411,24 @@ export interface ReachyMiniInstance extends EventTarget {
|
|
|
282
411
|
readonly preselectedRobotId: string | null;
|
|
283
412
|
/** `true` iff `preselectedRobotId !== null`. UX branching helper. */
|
|
284
413
|
readonly isEmbedded: boolean;
|
|
414
|
+
/**
|
|
415
|
+
* Live RTCPeerConnection, or `null` between sessions. Read-only escape
|
|
416
|
+
* hatch for stats sampling (`getStats()`); mutating it is unsupported.
|
|
417
|
+
* Auto-reconnect re-dials REPLACE this object, so re-read it on every
|
|
418
|
+
* use — never capture it across ticks.
|
|
419
|
+
*/
|
|
420
|
+
readonly peerConnection: RTCPeerConnection | null;
|
|
421
|
+
/**
|
|
422
|
+
* Build version of this SDK (npm package `version`), injected from
|
|
423
|
+
* package.json at build time. The JS SDK's OWN version — distinct from
|
|
424
|
+
* `getVersion()`, which asks the DAEMON its version over the data
|
|
425
|
+
* channel. `0.0.0-managed-by-ci` means an unreleased/branch build.
|
|
426
|
+
*/
|
|
427
|
+
readonly sdkVersion: string;
|
|
285
428
|
/** Underlying RTCPeerConnection. Apps can read it to inspect
|
|
286
|
-
* audio / video transceivers.
|
|
429
|
+
* audio / video transceivers.
|
|
430
|
+
* @deprecated Use `peerConnection`. Kept for compatibility with
|
|
431
|
+
* app bundles that predate the getter. */
|
|
287
432
|
_pc: RTCPeerConnection | null;
|
|
288
433
|
/** Silent placeholder MediaStream the SDK feeds the WebRTC audio
|
|
289
434
|
* sender so robot-speaker output can negotiate sendrecv. Apps inject
|
|
@@ -291,7 +436,7 @@ export interface ReachyMiniInstance extends EventTarget {
|
|
|
291
436
|
* `replaceTrack()` on the audio sender from `_pc.getSenders()`. */
|
|
292
437
|
_micStream: MediaStream | null;
|
|
293
438
|
authenticate(): Promise<boolean>;
|
|
294
|
-
login(): Promise<void>;
|
|
439
|
+
login(options?: LoginOptions): Promise<void>;
|
|
295
440
|
logout(): void;
|
|
296
441
|
connect(token?: string): Promise<void>;
|
|
297
442
|
/**
|
|
@@ -303,6 +448,20 @@ export interface ReachyMiniInstance extends EventTarget {
|
|
|
303
448
|
disconnect(): void;
|
|
304
449
|
startSession(robotId: string): Promise<void>;
|
|
305
450
|
stopSession(): Promise<void>;
|
|
451
|
+
/**
|
|
452
|
+
* Enable/disable the automatic session re-dial at runtime (see the
|
|
453
|
+
* `autoReconnect` constructor option). Disabling aborts any re-dial
|
|
454
|
+
* already in flight. Meant for flows that EXPECT the transport to
|
|
455
|
+
* die, e.g. a daemon self-update reboot.
|
|
456
|
+
*
|
|
457
|
+
* Sharp edge: aborting an in-flight re-dial leaves the session torn
|
|
458
|
+
* down (the loop closed the transport on entry) WITHOUT emitting
|
|
459
|
+
* `sessionStopped` or `error` — the caller owns the terminal event.
|
|
460
|
+
* The daemon-update flow relies on exactly that (its own
|
|
461
|
+
* `sessionStopped` arrives from the reboot); any other caller must
|
|
462
|
+
* surface the end of the session itself.
|
|
463
|
+
*/
|
|
464
|
+
setAutoReconnect(enabled: boolean): void;
|
|
306
465
|
attachVideo(el: HTMLVideoElement): () => void;
|
|
307
466
|
setHeadRpyDeg(roll: number, pitch: number, yaw: number): boolean;
|
|
308
467
|
setAntennasDeg(right: number, left: number): boolean;
|
|
@@ -331,8 +490,60 @@ export interface ReachyMiniInstance extends EventTarget {
|
|
|
331
490
|
}): boolean;
|
|
332
491
|
/** Send an arbitrary JSON message on the data channel. */
|
|
333
492
|
sendRaw(data: unknown): boolean;
|
|
493
|
+
/**
|
|
494
|
+
* Generic command round-trip for daemon commands the SDK has no typed
|
|
495
|
+
* wrapper for: send `command` and await the matching reply, `null` on
|
|
496
|
+
* the fail-open timeout (daemon predates the command). Lets an app use
|
|
497
|
+
* a newer daemon feature without waiting for an SDK release.
|
|
498
|
+
*/
|
|
499
|
+
request(command: {
|
|
500
|
+
type: string;
|
|
501
|
+
} & Record<string, unknown>, options?: RequestOptions): Promise<Record<string, unknown> | null>;
|
|
334
502
|
/** Play a sound file on the robot's speakers (basename). */
|
|
335
503
|
playSound(file: string): boolean;
|
|
504
|
+
/**
|
|
505
|
+
* Play a named recorded move (motion + its bundled sound) from a HF
|
|
506
|
+
* dataset, daemon-side. Fire-and-forget: returns `true` once the command
|
|
507
|
+
* is sent on the data channel (not when playback finishes), `false` if the
|
|
508
|
+
* channel isn't open. `dataset` defaults to the daemon's emotions library
|
|
509
|
+
* (`pollen-robotics/reachy-mini-emotions-library`), already pre-downloaded
|
|
510
|
+
* on the robot; pass it to source a move from another repo.
|
|
511
|
+
* `initialGotoDuration` (seconds) eases into the move's first frame
|
|
512
|
+
* instead of snapping. The daemon self-guards against a concurrent move,
|
|
513
|
+
* so a rapid double call is a no-op.
|
|
514
|
+
*/
|
|
515
|
+
playRecordedMove(moveName: string, opts?: {
|
|
516
|
+
dataset?: string;
|
|
517
|
+
initialGotoDuration?: number;
|
|
518
|
+
}): boolean;
|
|
519
|
+
/**
|
|
520
|
+
* Stop whatever move is currently playing on the daemon (recorded move,
|
|
521
|
+
* uploaded move, goto), silencing its bundled sound too. Fire-and-forget
|
|
522
|
+
* and idempotent: a stop with no move running is acked as a no-op, not
|
|
523
|
+
* an error. Returns `false` if the data channel is not open.
|
|
524
|
+
*/
|
|
525
|
+
stopMove(): boolean;
|
|
526
|
+
/**
|
|
527
|
+
* Ask the daemon to pre-download a recorded-move dataset into its local
|
|
528
|
+
* HF cache, so a later `playRecordedMove` from that dataset starts
|
|
529
|
+
* instantly instead of blocking on the download. Send it right after
|
|
530
|
+
* connecting for any non-official dataset you plan to play (the daemon
|
|
531
|
+
* only preloads the official pollen-robotics libraries by itself).
|
|
532
|
+
* Fire-and-forget and idempotent: cache-first daemon-side, so repeats
|
|
533
|
+
* are cheap; on failure `playRecordedMove` still downloads on demand.
|
|
534
|
+
*/
|
|
535
|
+
preloadDataset(dataset: string): boolean;
|
|
536
|
+
/**
|
|
537
|
+
* Like `preloadDataset()`, but resolves once the daemon acks the preload,
|
|
538
|
+
* i.e. when the dataset is actually in the local HF cache. `true` on
|
|
539
|
+
* success, `false` on a daemon-reported download failure, `null` on the
|
|
540
|
+
* fail-open timeout (slow download, or a daemon that predates the
|
|
541
|
+
* command). Rejects when the channel isn't open or the session tears
|
|
542
|
+
* down mid-flight.
|
|
543
|
+
*/
|
|
544
|
+
preloadDatasetAndWait(dataset: string, options?: {
|
|
545
|
+
timeoutMs?: number;
|
|
546
|
+
}): Promise<boolean | null>;
|
|
336
547
|
/** Drop incoming audio queued for the robot speaker (barge-in). */
|
|
337
548
|
clearIncomingAudio(): boolean;
|
|
338
549
|
/** Enable daemon-side visual head tracking. */
|
|
@@ -347,6 +558,25 @@ export interface ReachyMiniInstance extends EventTarget {
|
|
|
347
558
|
setVolume(volume: number): Promise<number | null>;
|
|
348
559
|
getMicrophoneVolume(): Promise<number | null>;
|
|
349
560
|
setMicrophoneVolume(volume: number): Promise<number | null>;
|
|
561
|
+
/**
|
|
562
|
+
* Query the persisted robot display name. `null` when none is set, the
|
|
563
|
+
* channel isn't open, or the daemon predates the `get_robot_name` command.
|
|
564
|
+
*/
|
|
565
|
+
getRobotName(): Promise<string | null>;
|
|
566
|
+
/**
|
|
567
|
+
* Query whether the first wake-up setup wizard has been completed
|
|
568
|
+
* (robot-wide, persisted). `null` when the channel isn't open or the
|
|
569
|
+
* daemon predates the command (callers should fail-open).
|
|
570
|
+
*/
|
|
571
|
+
getFirstWakeUp(): Promise<boolean | null>;
|
|
572
|
+
/** Persist the first wake-up wizard completion flag on the robot. */
|
|
573
|
+
setFirstWakeUp(isCompleted: boolean): Promise<boolean | null>;
|
|
574
|
+
/**
|
|
575
|
+
* Set and persist the robot display name. Applied live by the daemon
|
|
576
|
+
* (status + central relay + mDNS), so it takes effect right away without a
|
|
577
|
+
* restart. Resolves with the stored name, or `null` on error.
|
|
578
|
+
*/
|
|
579
|
+
setRobotName(name: string): Promise<string | null>;
|
|
350
580
|
/**
|
|
351
581
|
* Apply a batch of XVF3800 audio-board parameters on the robot.
|
|
352
582
|
* Mirrors the on-robot `AudioBase.apply_audio_config()` SDK call.
|
|
@@ -366,10 +596,38 @@ export interface ReachyMiniInstance extends EventTarget {
|
|
|
366
596
|
getVersion(): Promise<string | null>;
|
|
367
597
|
/** Hardware ID (USB serial), or `null` on developer machines. */
|
|
368
598
|
getHardwareId(): Promise<string | null>;
|
|
599
|
+
/**
|
|
600
|
+
* One-shot IMU reading (accelerometer, gyroscope, quaternion,
|
|
601
|
+
* temperature). Resolves `null` when the robot has no IMU (Lite
|
|
602
|
+
* version, simulation) or the daemon predates the `get_imu` command.
|
|
603
|
+
*/
|
|
604
|
+
getImu(): Promise<ImuData | null>;
|
|
369
605
|
/** Force a `state` event right now (background poll runs at 500 ms). */
|
|
370
606
|
requestState(): boolean;
|
|
607
|
+
/**
|
|
608
|
+
* Ask the daemon to *push* the robot state (~30 Hz, delivered as `state`
|
|
609
|
+
* events) over the dedicated unreliable/unordered `pose` channel instead
|
|
610
|
+
* of polling `requestState` - immune to the Wi-Fi round-trip lag of the
|
|
611
|
+
* reliable control channel. Refcounted: pair every `subscribePose()` with
|
|
612
|
+
* exactly one `unsubscribePose()`; multiple consumers share one daemon-side
|
|
613
|
+
* subscription. No-op against a daemon that predates the pose channel (the
|
|
614
|
+
* SDK's ~2 Hz self-poll keeps `state` updated there).
|
|
615
|
+
*/
|
|
616
|
+
subscribePose(): boolean;
|
|
617
|
+
/** Release one pose-stream consumer; sends `unsubscribe_pose` on the last. */
|
|
618
|
+
unsubscribePose(): boolean;
|
|
371
619
|
/** Subscribe to daemon `journalctl` logs over the data channel. */
|
|
372
620
|
subscribeLogs(options: SubscribeLogsOptions): () => void;
|
|
621
|
+
/**
|
|
622
|
+
* Ask the daemon to update itself from PyPI (Wireless only) and
|
|
623
|
+
* restart. Fire-and-ack: returns `false` only when the data channel
|
|
624
|
+
* isn't open. A *successful* update restarts the daemon before a
|
|
625
|
+
* `done` event can arrive, so treat the session teardown followed by
|
|
626
|
+
* a successful reconnect as the success signal; `onProgress` fires
|
|
627
|
+
* with `status: 'failed'` when the daemon declines the job or the
|
|
628
|
+
* install errors before the restart.
|
|
629
|
+
*/
|
|
630
|
+
startDaemonUpdate(options?: StartDaemonUpdateOptions): boolean;
|
|
373
631
|
/**
|
|
374
632
|
* Motor torque mode: `enabled` (position control), `disabled`
|
|
375
633
|
* (limp), `gravity_compensation` (float-by-hand). Returns
|
|
@@ -392,11 +650,22 @@ export interface ReachyMiniInstance extends EventTarget {
|
|
|
392
650
|
* completion. Does NOT touch motor mode; caller manages it.
|
|
393
651
|
*/
|
|
394
652
|
gotoSleep(options?: MotionAwaitOptions): Promise<void>;
|
|
395
|
-
/**
|
|
653
|
+
/**
|
|
654
|
+
* Read the awake state from the cached `motor_mode`. Gravity
|
|
655
|
+
* compensation counts as awake: the robot is standing, even though
|
|
656
|
+
* it won't follow position targets in that mode.
|
|
657
|
+
*/
|
|
396
658
|
isAwake(): boolean;
|
|
397
659
|
/**
|
|
398
|
-
* Idempotent
|
|
399
|
-
* the
|
|
660
|
+
* Idempotent bring-up to a state where the robot follows position
|
|
661
|
+
* targets. Plays the wake trajectory when asleep and awaits it, so
|
|
662
|
+
* callers can start commanding poses on resolution without fighting
|
|
663
|
+
* the emote; switches back to position control (no emote) when the
|
|
664
|
+
* robot is up but in gravity compensation; noop when already there.
|
|
665
|
+
*
|
|
666
|
+
* `timeoutMs` bounds only the initial `motor_mode` read; the wake
|
|
667
|
+
* trajectory gets its own internal budget. Never rejects - a robot
|
|
668
|
+
* that won't confirm the wake still lets the app boot.
|
|
400
669
|
*/
|
|
401
670
|
ensureAwake(timeoutMs?: number): Promise<boolean>;
|
|
402
671
|
/** Upload a recorded move (and optional audio) and play it on the daemon's local clock. */
|
|
@@ -412,7 +681,12 @@ export interface ReachyMiniInstance extends EventTarget {
|
|
|
412
681
|
/** Cancel an in-flight `playUploadedAudio`. */
|
|
413
682
|
cancelAudio(uploadId?: string | null): boolean;
|
|
414
683
|
}
|
|
415
|
-
export type ReachyMiniConstructor = new (options?: ReachyMiniOptions) => ReachyMiniInstance
|
|
684
|
+
export type ReachyMiniConstructor = (new (options?: ReachyMiniOptions) => ReachyMiniInstance) & {
|
|
685
|
+
/** Build version of the SDK (npm package `version`), reachable without
|
|
686
|
+
* an instance via `ReachyMini.version`. Mirror of the instance
|
|
687
|
+
* `sdkVersion`. */
|
|
688
|
+
readonly version: string;
|
|
689
|
+
};
|
|
416
690
|
/**
|
|
417
691
|
* Internal "session reject" error shape. Surfaced by `startSession()`
|
|
418
692
|
* rejections; consumers can downcast a caught `Error` to read the
|