From e98cf4e949f53739f4ea04cfa050e19e5788a365 Mon Sep 17 00:00:00 2001 From: tfrere Date: Thu, 21 May 2026 17:03:58 +0200 Subject: [PATCH 1/5] feat(js): merge host package into reachy-mini-sdk Fold @pollen-robotics/reachy-mini-host into @pollen-robotics/reachy-mini-sdk so app authors install, version, and import from a single entry point: import { ReachyMini } from '@pollen-robotics/reachy-mini-sdk'; import { mountHost } from '@pollen-robotics/reachy-mini-sdk/host/auto'; import { connectToHost } from '@pollen-robotics/reachy-mini-sdk/host/embed'; import { PROTOCOL_VERSION } from '@pollen-robotics/reachy-mini-sdk/host/protocol'; Layout changes - js/host/ -> js/sdk/host/ (whole directory) - js/host/src/lib/sdk-types -> js/sdk/reachy-mini-sdk.d.ts (canonical types, shipped as .d.ts companion to the SDK runtime; host re-exports from the new location) - js/package.json (workspaces) deleted; js/sdk/ is now the only npm package - js/package-lock.json moved to js/sdk/package-lock.json Package surface - New subpath exports on @pollen-robotics/reachy-mini-sdk: ./host (mountHost + types) ./host/auto (CDN auto bundle) ./host/embed (CDN embed bundle) ./host/protocol (PROTOCOL_VERSION + helpers) - Host React/MUI deps and devDeps moved into js/sdk/package.json - Single build script (`npm run build`) drives vite + tsc for the host bundles - CI publishes one tarball instead of two (--workspace flags dropped) Runtime cleanup - entry/auto.ts, entry/embed.ts and the `./host` npm entry now import ReachyMini directly from the SDK module and self-assign window.ReachyMini (when unset) at module-load time, dispatching reachymini:ready before exposing window.ReachyMiniHost / ReachyMiniHostEmbed. Apps that consume the host no longer need a separate ` + + + +``` + +Both bundles auto-install the SDK on `window.ReachyMini` at load time, so an app that uses the host no longer needs a separate ` @@ -61,7 +63,7 @@ npm install --save-dev @pollen-robotics/reachy-mini-sdk The CDN URL pins to the **major** version (`@1`), so patch + minor releases land in every Space automatically; only a deliberate breaking change requires each app to update its tag. The host shares its version with the SDK and the -daemon (single repo, single release). +daemon (single repo, single release, single npm package). ## 90-second tour @@ -96,7 +98,7 @@ if (isEmbed) { **`src/host.ts`** — mounts the shell: ```ts -import { mountHost } from '@pollen-robotics/reachy-mini-host/auto'; +import { mountHost } from '@pollen-robotics/reachy-mini-sdk/host/auto'; mountHost({ appName: 'My App', @@ -108,7 +110,7 @@ mountHost({ **`src/embed.ts`** — connects and runs the app: ```ts -import { connectToHost } from '@pollen-robotics/reachy-mini-host/embed'; +import { connectToHost } from '@pollen-robotics/reachy-mini-sdk/host/embed'; const handle = await connectToHost(); handle.reachy.setHeadRpyDeg(0, 10, 0); diff --git a/js/host/SPEC.md b/js/sdk/host/SPEC.md similarity index 95% rename from js/host/SPEC.md rename to js/sdk/host/SPEC.md index e0555b740..1d36c71e4 100644 --- a/js/host/SPEC.md +++ b/js/sdk/host/SPEC.md @@ -5,7 +5,7 @@ | 1.0 | Active | 2026-05-16 | Source-of-truth for what a Reachy Mini app must do at boot, what -`@pollen-robotics/reachy-mini-host` does for it, and the contract between the +`@pollen-robotics/reachy-mini-sdk/host` does for it, and the contract between the shell, the embed and the app code. This file describes **observable behaviour and invariants**. The @@ -49,14 +49,14 @@ Three actors, one app repository: | Actor | Lives in | Owns | |------------|-----------------------------------|----------------------------------------------------------------------------| | **App** | `index.html` + `src/dispatch.ts` | UI, app-specific UX, **and full freedom over framework / tooling choices** | -| **Host** | `@pollen-robotics/reachy-mini-host/auto` (CDN) | OAuth, robot discovery, robot picker, connecting overlay, end-session flow | -| **Embed** | `@pollen-robotics/reachy-mini-host/embed` (CDN) | SDK lifecycle inside the iframe (`startSession`, `ensureAwake`, teardown) | +| **Host** | `@pollen-robotics/reachy-mini-sdk/host/auto` (CDN) | OAuth, robot discovery, robot picker, connecting overlay, end-session flow | +| **Embed** | `@pollen-robotics/reachy-mini-sdk/host/embed` (CDN) | SDK lifecycle inside the iframe (`startSession`, `ensureAwake`, teardown) | The **App** consumes: - the Reachy Mini SDK (loaded as a CDN module by `index.html`, exposed as `window.ReachyMini`), -- the `@pollen-robotics/reachy-mini-host` package (npm dep for types, CDN entries +- the `@pollen-robotics/reachy-mini-sdk/host` package (npm dep for types, CDN entries at runtime). The App contains zero auth code, zero picker code, zero @@ -111,7 +111,7 @@ files in §5. ### Why React + MUI for the host (and only the host) -The host shell (`@pollen-robotics/reachy-mini-host/auto`) is built with **React + +The host shell (`@pollen-robotics/reachy-mini-sdk/host/auto`) is built with **React + MUI + Emotion**. This is an explicit, assumed choice: - The shell needs a real component library: forms (sign-in), @@ -146,7 +146,7 @@ The same `index.html` is served for both modes. The dispatcher if (URL.searchParams.has("embedded") && URL.hash.startsWith("#creds=")): boot embed -> import("./embed") else: - boot host -> import("@pollen-robotics/reachy-mini-host/auto").mountHost({...}) + boot host -> import("@pollen-robotics/reachy-mini-sdk/host/auto").mountHost({...}) ``` `?embedded=1` without creds is an invalid mode - render `ErrorView`. @@ -474,8 +474,8 @@ can be rolled out by the SDK team, not the app teams. on deploy. - Reachy Mini SDK in `index.html`: pinned to a **git tag or commit SHA**, never to a branch. jsdelivr caches branches for ~12 h. -- `@pollen-robotics/reachy-mini-host` from CDN: pinned to a **major version** - (`@pollen-robotics/reachy-mini-host@1`) on jsdelivr/unpkg. +- `@pollen-robotics/reachy-mini-sdk/host` from CDN: pinned to a **major version** + (`@pollen-robotics/reachy-mini-sdk@1`) on jsdelivr/unpkg. On detected mismatch (e.g. unknown protocol version, structurally invalid `host:init`), the host's ErrorView's primary button calls @@ -588,11 +588,12 @@ narrow but not zero. Items below are tracked but **not** in §8 yet. Promote to §8 only when implemented and the cost of regressing them is justified. -- **CDN-only host**: today every app vendors `dist/` into - `vendor/reachy-mini-host/` to work around HF Spaces' Docker - build refusing `file:../` deps. Once `@pollen-robotics/reachy-mini-host` is - published to npm, the dispatcher should import from the jsdelivr - bundle so a host fix reaches every Space without per-app +- **CDN-only host**: the historical workaround of vendoring + `dist/` into `vendor/reachy-mini-host/` to placate HF Spaces' + Docker build (which refused `file:../` deps) is no longer + needed now that `@pollen-robotics/reachy-mini-sdk/host` is + published to npm. The dispatcher imports from the jsdelivr + bundle, so a host fix reaches every Space without per-app redeploys. - **Heartbeat / liveness**: detect a frozen embed (JS infinite loop in a same-tab iframe). Trigger if we observe a freeze in @@ -608,7 +609,7 @@ when implemented and the cost of regressing them is justified. `resumeSession()` so we re-run the WebRTC handshake without going through the picker again. - **Mobile parity for ConnectingView**: export a tiny - `` from `@pollen-robotics/reachy-mini-host/embed` so + `` from `@pollen-robotics/reachy-mini-sdk/host/embed` so Mode B apps reuse the 3-step visual without pulling React. - **Observability hook**: opt-in callback for app authors / HF ops to collect a redacted trace of every session. diff --git a/js/host/src/ReachyHost.tsx b/js/sdk/host/src/ReachyHost.tsx similarity index 100% rename from js/host/src/ReachyHost.tsx rename to js/sdk/host/src/ReachyHost.tsx diff --git a/js/host/src/assets/connection.svg b/js/sdk/host/src/assets/connection.svg similarity index 100% rename from js/host/src/assets/connection.svg rename to js/sdk/host/src/assets/connection.svg diff --git a/js/host/src/assets/hf-logo.svg b/js/sdk/host/src/assets/hf-logo.svg similarity index 100% rename from js/host/src/assets/hf-logo.svg rename to js/sdk/host/src/assets/hf-logo.svg diff --git a/js/host/src/assets/index.ts b/js/sdk/host/src/assets/index.ts similarity index 100% rename from js/host/src/assets/index.ts rename to js/sdk/host/src/assets/index.ts diff --git a/js/host/src/assets/reachy-buste.svg b/js/sdk/host/src/assets/reachy-buste.svg similarity index 100% rename from js/host/src/assets/reachy-buste.svg rename to js/sdk/host/src/assets/reachy-buste.svg diff --git a/js/host/src/assets/reachy-head.svg b/js/sdk/host/src/assets/reachy-head.svg similarity index 100% rename from js/host/src/assets/reachy-head.svg rename to js/sdk/host/src/assets/reachy-head.svg diff --git a/js/host/src/assets/reachy-standard.svg b/js/sdk/host/src/assets/reachy-standard.svg similarity index 100% rename from js/host/src/assets/reachy-standard.svg rename to js/sdk/host/src/assets/reachy-standard.svg diff --git a/js/host/src/assets/svg.d.ts b/js/sdk/host/src/assets/svg.d.ts similarity index 100% rename from js/host/src/assets/svg.d.ts rename to js/sdk/host/src/assets/svg.d.ts diff --git a/js/host/src/components/ConnectingView.tsx b/js/sdk/host/src/components/ConnectingView.tsx similarity index 100% rename from js/host/src/components/ConnectingView.tsx rename to js/sdk/host/src/components/ConnectingView.tsx diff --git a/js/host/src/components/EmbedFrame.tsx b/js/sdk/host/src/components/EmbedFrame.tsx similarity index 100% rename from js/host/src/components/EmbedFrame.tsx rename to js/sdk/host/src/components/EmbedFrame.tsx diff --git a/js/host/src/components/ErrorView.tsx b/js/sdk/host/src/components/ErrorView.tsx similarity index 100% rename from js/host/src/components/ErrorView.tsx rename to js/sdk/host/src/components/ErrorView.tsx diff --git a/js/host/src/components/LeavingView.tsx b/js/sdk/host/src/components/LeavingView.tsx similarity index 100% rename from js/host/src/components/LeavingView.tsx rename to js/sdk/host/src/components/LeavingView.tsx diff --git a/js/host/src/components/PickerView.tsx b/js/sdk/host/src/components/PickerView.tsx similarity index 100% rename from js/host/src/components/PickerView.tsx rename to js/sdk/host/src/components/PickerView.tsx diff --git a/js/host/src/components/ReachyHostShell.tsx b/js/sdk/host/src/components/ReachyHostShell.tsx similarity index 99% rename from js/host/src/components/ReachyHostShell.tsx rename to js/sdk/host/src/components/ReachyHostShell.tsx index 345686caa..d484c1512 100644 --- a/js/host/src/components/ReachyHostShell.tsx +++ b/js/sdk/host/src/components/ReachyHostShell.tsx @@ -324,7 +324,7 @@ function ReachyHostShellNormal({ setHostPhase('error'); } else { console.warn( - '[reachy-mini-host] embed reported non-fatal error:', + '[reachy-mini-sdk/host] embed reported non-fatal error:', message, detail, ); diff --git a/js/host/src/components/SignInView.tsx b/js/sdk/host/src/components/SignInView.tsx similarity index 100% rename from js/host/src/components/SignInView.tsx rename to js/sdk/host/src/components/SignInView.tsx diff --git a/js/host/src/components/StepsProgressIndicator.tsx b/js/sdk/host/src/components/StepsProgressIndicator.tsx similarity index 100% rename from js/host/src/components/StepsProgressIndicator.tsx rename to js/sdk/host/src/components/StepsProgressIndicator.tsx diff --git a/js/host/src/components/TopBar.tsx b/js/sdk/host/src/components/TopBar.tsx similarity index 100% rename from js/host/src/components/TopBar.tsx rename to js/sdk/host/src/components/TopBar.tsx diff --git a/js/host/src/components/WelcomeBackOverlay.tsx b/js/sdk/host/src/components/WelcomeBackOverlay.tsx similarity index 100% rename from js/host/src/components/WelcomeBackOverlay.tsx rename to js/sdk/host/src/components/WelcomeBackOverlay.tsx diff --git a/js/host/src/embed/index.ts b/js/sdk/host/src/embed/index.ts similarity index 98% rename from js/host/src/embed/index.ts rename to js/sdk/host/src/embed/index.ts index 0db9f44a3..2c0aefc0b 100644 --- a/js/host/src/embed/index.ts +++ b/js/sdk/host/src/embed/index.ts @@ -3,10 +3,10 @@ * * Vanilla TypeScript helper that lives in the iframe side of the * host / app split. Consumed by `src/embed.{ts,tsx}` in each app - * (or via the CDN entry `@pollen-robotics/reachy-mini-host/embed` - * script tag). + * (or via the CDN entry + * `@pollen-robotics/reachy-mini-sdk/host/embed` script tag). * - * import { connectToHost } from '@pollen-robotics/reachy-mini-host/embed'; + * import { connectToHost } from '@pollen-robotics/reachy-mini-sdk/host/embed'; * * const handle = await connectToHost(); * handle.onLeave(() => { /* clean up before unmount *\/ }); @@ -230,7 +230,7 @@ async function bootOnce( if (!creds) { throw new Error( - '[reachy-mini-host/embed] no creds bundle found in URL hash. ' + + '[reachy-mini-sdk/host/embed] no creds bundle found in URL hash. ' + 'Was the embed mounted directly without ?embedded=1#creds=...?', ); } @@ -239,7 +239,7 @@ async function bootOnce( const sdkReady = await waitForSdkReady(SDK_READY_TIMEOUT_MS); if (!sdkReady) { throw new Error( - '[reachy-mini-host/embed] window.ReachyMini did not become ' + + '[reachy-mini-sdk/host/embed] window.ReachyMini did not become ' + `available within ${SDK_READY_TIMEOUT_MS}ms - check the SDK CDN tag.`, ); } @@ -402,7 +402,7 @@ function createBridge(expectedOrigin: string) { try { void cb(); } catch (err) { - console.warn('[reachy-mini-host/embed] onLeave threw', err); + console.warn('[reachy-mini-sdk/host/embed] onLeave threw', err); } }); } @@ -602,7 +602,7 @@ function postToHost(msg: EmbedToHostMsg): void { try { window.parent.postMessage(msg, parentTargetOrigin); } catch (err) { - console.warn('[reachy-mini-host/embed] postMessage to host failed', err); + console.warn('[reachy-mini-sdk/host/embed] postMessage to host failed', err); } } diff --git a/js/host/src/entry/auto.ts b/js/sdk/host/src/entry/auto.ts similarity index 53% rename from js/host/src/entry/auto.ts rename to js/sdk/host/src/entry/auto.ts index 184585594..056432dd2 100644 --- a/js/host/src/entry/auto.ts +++ b/js/sdk/host/src/entry/auto.ts @@ -10,7 +10,19 @@ * The auto bundle exposes `mountHost` on `window.ReachyMiniHost` * for legacy / non-module callers. ESM consumers import it the * normal way. + * + * SDK auto-install + * ──────────────── + * The host depends on `window.ReachyMini` for its OAuth helpers + * (`useSdk`) and the embed client. Now that the SDK lives in the + * same npm package, we import it directly and assign it to + * `window.ReachyMini` here as a side effect, then dispatch the + * `reachymini:ready` event the host's wait loops listen for. + * Existing apps that still set `window.ReachyMini` themselves + * (e.g. via the old jsdelivr ` -## Migration from `@pollen-robotics/reachy-mini-host` + + +``` -The host package has been folded into the SDK. Update your app: +Both bundles auto-install the SDK on `window.ReachyMini` at load time, so an app that uses the host no longer needs a separate ` - - - -``` - -Both bundles auto-install the SDK on `window.ReachyMini` at load time, so an app that uses the host no longer needs a separate `