fix: propagate private-space __sign JWT to all same-origin requests
Browse files- duck.js +7 -2
- index.html +8 -1
- rl.js +22 -7
duck.js
CHANGED
|
@@ -19,6 +19,11 @@ export const MESH_VERSION = "8";
|
|
| 19 |
// targets them.
|
| 20 |
export const MODEL_DIR = "./robot/mjlab";
|
| 21 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 22 |
// Meshes fully occluded inside the shells at every demo camera angle,
|
| 23 |
// verified empirically by per-mesh pixel-diff (front 3/4, back, low
|
| 24 |
// front close-up): hiding each changes exactly 0 pixels. Skipped at
|
|
@@ -29,7 +34,7 @@ const HIDDEN_MESHES = new Set([]);
|
|
| 29 |
export async function loadKinematics(url) {
|
| 30 |
// Same cache-buster as the STLs: force-cache would otherwise keep
|
| 31 |
// serving a stale kinematics.json after the mesh list changes.
|
| 32 |
-
const r = await fetch(`${url}?v=${MESH_VERSION}`, { cache: "force-cache" });
|
| 33 |
if (!r.ok) throw new Error(`kinematics fetch ${r.status}`);
|
| 34 |
const k = await r.json();
|
| 35 |
// Full-resolution meshes by default (the reduction will be redone
|
|
@@ -75,7 +80,7 @@ export async function buildRig(k, opts = {}) {
|
|
| 75 |
if (!meshCache.has(name)) {
|
| 76 |
meshCache.set(
|
| 77 |
name,
|
| 78 |
-
loader.loadAsync(`${k.mesh_dir}/${name}?v=${MESH_VERSION}`).then((raw) => {
|
| 79 |
raw.deleteAttribute("normal");
|
| 80 |
const welded = mergeVertices(raw, 1e-4);
|
| 81 |
welded.scale(1000, 1000, 1000);
|
|
|
|
| 19 |
// targets them.
|
| 20 |
export const MODEL_DIR = "./robot/mjlab";
|
| 21 |
|
| 22 |
+
// On a private HF Space, rl.js installs a URL signer that appends the
|
| 23 |
+
// ?__sign JWT to same-origin requests (auth cookies may be blocked in the
|
| 24 |
+
// hub iframe). Identity everywhere else.
|
| 25 |
+
const signed = (url) => (window.__hfSigned ? window.__hfSigned(url) : url);
|
| 26 |
+
|
| 27 |
// Meshes fully occluded inside the shells at every demo camera angle,
|
| 28 |
// verified empirically by per-mesh pixel-diff (front 3/4, back, low
|
| 29 |
// front close-up): hiding each changes exactly 0 pixels. Skipped at
|
|
|
|
| 34 |
export async function loadKinematics(url) {
|
| 35 |
// Same cache-buster as the STLs: force-cache would otherwise keep
|
| 36 |
// serving a stale kinematics.json after the mesh list changes.
|
| 37 |
+
const r = await fetch(signed(`${url}?v=${MESH_VERSION}`), { cache: "force-cache" });
|
| 38 |
if (!r.ok) throw new Error(`kinematics fetch ${r.status}`);
|
| 39 |
const k = await r.json();
|
| 40 |
// Full-resolution meshes by default (the reduction will be redone
|
|
|
|
| 80 |
if (!meshCache.has(name)) {
|
| 81 |
meshCache.set(
|
| 82 |
name,
|
| 83 |
+
loader.loadAsync(signed(`${k.mesh_dir}/${name}?v=${MESH_VERSION}`)).then((raw) => {
|
| 84 |
raw.deleteAttribute("normal");
|
| 85 |
const welded = mergeVertices(raw, 1e-4);
|
| 86 |
welded.scale(1000, 1000, 1000);
|
index.html
CHANGED
|
@@ -178,6 +178,13 @@
|
|
| 178 |
}
|
| 179 |
}
|
| 180 |
</script>
|
| 181 |
-
<
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 182 |
</body>
|
| 183 |
</html>
|
|
|
|
| 178 |
}
|
| 179 |
}
|
| 180 |
</script>
|
| 181 |
+
<!-- rl.js is imported dynamically with the private-space ?__sign JWT
|
| 182 |
+
appended: a plain src attribute can 401 when the browser blocks the
|
| 183 |
+
*.static.hf.space auth cookie inside the hub iframe. -->
|
| 184 |
+
<script type="module">
|
| 185 |
+
const sign = new URLSearchParams(location.search).get("__sign");
|
| 186 |
+
const suffix = sign ? `?__sign=${encodeURIComponent(sign)}` : "";
|
| 187 |
+
import(`./rl.js${suffix}`);
|
| 188 |
+
</script>
|
| 189 |
</body>
|
| 190 |
</html>
|
rl.js
CHANGED
|
@@ -17,11 +17,26 @@
|
|
| 17 |
import * as THREE from "three";
|
| 18 |
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
| 19 |
import { RoomEnvironment } from "three/addons/environments/RoomEnvironment.js";
|
| 20 |
-
import { buildRig, loadKinematics, setJoint, setJawOpen, MODEL_DIR, MESH_VERSION } from "./duck.js";
|
| 21 |
-
import { VARIANTS, VARIANT_NAMES, materialHookFor, randomVariantName, applyVariant, specToHex } from "./variants.js";
|
| 22 |
import loadMujoco from "https://cdn.jsdelivr.net/npm/@mujoco/mujoco@3.11.0/mujoco.js";
|
| 23 |
import * as ort from "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.27.0/dist/ort.min.mjs";
|
| 24 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 25 |
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.27.0/dist/";
|
| 26 |
ort.env.wasm.numThreads = 1; // static hosting sends no COOP/COEP headers
|
| 27 |
|
|
@@ -94,7 +109,7 @@ const traced = (label, p) => {
|
|
| 94 |
// Stripping them means the MuJoCo VFS only needs the ~10 meshes referenced
|
| 95 |
// by collision geoms.
|
| 96 |
async function buildPhysicsXml() {
|
| 97 |
-
const src = await (await fetch(`${MODEL_DIR}/robot_allcollisions.xml`)).text();
|
| 98 |
const doc = new DOMParser().parseFromString(src, "text/xml");
|
| 99 |
for (const g of [...doc.querySelectorAll('geom[class="visual"]')]) g.remove();
|
| 100 |
const usedMeshes = new Set(
|
|
@@ -138,7 +153,7 @@ await Promise.all(
|
|
| 138 |
meshFiles.map(async (f) => {
|
| 139 |
// Same cache-busted URL as duck.js so the browser reuses the render
|
| 140 |
// meshes instead of downloading the collision subset a second time.
|
| 141 |
-
const buf = await (await fetch(`${MODEL_DIR}/meshes/${f}?v=${MESH_VERSION}`, { cache: "force-cache" })).arrayBuffer();
|
| 142 |
// meshdir="assets" in the MJCF, so the compiler looks up "assets/<f>".
|
| 143 |
vfs.addBuffer(`assets/${f}`, new Uint8Array(buf));
|
| 144 |
}),
|
|
@@ -151,9 +166,9 @@ const rigPromise = (async () => {
|
|
| 151 |
})();
|
| 152 |
const sessionOpts = { executionProviders: ["wasm"] };
|
| 153 |
[sessions.walk, sessions.sitstand, sessions.roulade] = await Promise.all([
|
| 154 |
-
ort.InferenceSession.create(POLICIES.walk, sessionOpts),
|
| 155 |
-
ort.InferenceSession.create(POLICIES.sitstand, sessionOpts),
|
| 156 |
-
ort.InferenceSession.create(POLICIES.roulade, sessionOpts),
|
| 157 |
]);
|
| 158 |
|
| 159 |
setLoading("Compiling physics\u2026");
|
|
|
|
| 17 |
import * as THREE from "three";
|
| 18 |
import { OrbitControls } from "three/addons/controls/OrbitControls.js";
|
| 19 |
import { RoomEnvironment } from "three/addons/environments/RoomEnvironment.js";
|
|
|
|
|
|
|
| 20 |
import loadMujoco from "https://cdn.jsdelivr.net/npm/@mujoco/mujoco@3.11.0/mujoco.js";
|
| 21 |
import * as ort from "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.27.0/dist/ort.min.mjs";
|
| 22 |
|
| 23 |
+
// Private HF Space auth: the hub iframe URL carries a ?__sign JWT, but
|
| 24 |
+
// subresource requests normally rely on a *.static.hf.space cookie that
|
| 25 |
+
// browsers often block inside the iframe (third-party cookie blocking),
|
| 26 |
+
// which 401s every same-origin fetch. Appending the JWT to each request
|
| 27 |
+
// authenticates them regardless of cookie policy. No-op locally.
|
| 28 |
+
const HF_SIGN = new URLSearchParams(location.search).get("__sign");
|
| 29 |
+
const signed = (url) =>
|
| 30 |
+
HF_SIGN ? `${url}${url.includes("?") ? "&" : "?"}__sign=${encodeURIComponent(HF_SIGN)}` : url;
|
| 31 |
+
window.__hfSigned = signed; // duck.js uses it for kinematics + STL requests
|
| 32 |
+
|
| 33 |
+
// Local modules are imported dynamically through signed() for the same
|
| 34 |
+
// reason: a static import of ./duck.js would 401 without the cookie.
|
| 35 |
+
const { buildRig, loadKinematics, setJoint, setJawOpen, MODEL_DIR, MESH_VERSION } =
|
| 36 |
+
await import(signed("./duck.js"));
|
| 37 |
+
const { VARIANTS, VARIANT_NAMES, materialHookFor, randomVariantName, applyVariant, specToHex } =
|
| 38 |
+
await import(signed("./variants.js"));
|
| 39 |
+
|
| 40 |
ort.env.wasm.wasmPaths = "https://cdn.jsdelivr.net/npm/onnxruntime-web@1.27.0/dist/";
|
| 41 |
ort.env.wasm.numThreads = 1; // static hosting sends no COOP/COEP headers
|
| 42 |
|
|
|
|
| 109 |
// Stripping them means the MuJoCo VFS only needs the ~10 meshes referenced
|
| 110 |
// by collision geoms.
|
| 111 |
async function buildPhysicsXml() {
|
| 112 |
+
const src = await (await fetch(signed(`${MODEL_DIR}/robot_allcollisions.xml`))).text();
|
| 113 |
const doc = new DOMParser().parseFromString(src, "text/xml");
|
| 114 |
for (const g of [...doc.querySelectorAll('geom[class="visual"]')]) g.remove();
|
| 115 |
const usedMeshes = new Set(
|
|
|
|
| 153 |
meshFiles.map(async (f) => {
|
| 154 |
// Same cache-busted URL as duck.js so the browser reuses the render
|
| 155 |
// meshes instead of downloading the collision subset a second time.
|
| 156 |
+
const buf = await (await fetch(signed(`${MODEL_DIR}/meshes/${f}?v=${MESH_VERSION}`), { cache: "force-cache" })).arrayBuffer();
|
| 157 |
// meshdir="assets" in the MJCF, so the compiler looks up "assets/<f>".
|
| 158 |
vfs.addBuffer(`assets/${f}`, new Uint8Array(buf));
|
| 159 |
}),
|
|
|
|
| 166 |
})();
|
| 167 |
const sessionOpts = { executionProviders: ["wasm"] };
|
| 168 |
[sessions.walk, sessions.sitstand, sessions.roulade] = await Promise.all([
|
| 169 |
+
ort.InferenceSession.create(signed(POLICIES.walk), sessionOpts),
|
| 170 |
+
ort.InferenceSession.create(signed(POLICIES.sitstand), sessionOpts),
|
| 171 |
+
ort.InferenceSession.create(signed(POLICIES.roulade), sessionOpts),
|
| 172 |
]);
|
| 173 |
|
| 174 |
setLoading("Compiling physics\u2026");
|