openvibers/openvibe.bot Commits
Bot directory: the simulated rover is always the first card (#131)
Changes
@@@ -61,5 +61,5 @@"server-side ONVIF cameras (profile camera.onvif, driver onvif, server/onvif): an in-process device beside the simulator attaches a camera configured with BOT_ONVIF_CAMERAS (its URL plus username_ref/password_ref secret references, never in the profile or in clear), writes a real kind server device row, translates ptz commands to ONVIF ContinuousMove/Stop with the profile deadline as a deadman, and carries the camera's GetStatus readout in telemetry; every request goes only to the configured host, with a timeout and no redirects","a person's own machines that run their jobs (/machines: add with a --worker command, list, remove); their worker offers reported to Network as user-owned offers naming the owner; jobs reach a machine only for its owner (plan T14/T16)"],-- "updated": "2026-10-15"++ "updated": "2026-10-16"}@@@ -138,6 +138,12 @@ function robotCard(r) {+ `<p class="dir-card-foot">${esc(POLICY_WORDS[r.access_policy] || '')}</p></li>`;}++// The simulated rover anyone can drive (/try): always the first card, so the directory shows a robot to drive today.++const DEMO_CARD = '<li class="dir-card"><a class="dir-card-link" href="/try"><span class="dir-card-head"><b>Simulated rover</b>'++ + '<span class="dir-state" data-state="online"><span class="dir-dot"></span>Online</span></span>'++ + '<span class="dir-card-text">Runs in your browser · OpenVibe.Bot</span></a>'++ + '<p class="dir-card-foot">Anyone can drive it, no account needed</p></li>';++// ── GET /robots ─────────────────────────────────────────────────────────────────────────────────────function renderDirectory({ config, listed = [], mine = null, models = [], signedIn = false }) {const yours = !signedIn ? `<section class="dir-sec dir-cta"><h2>Have a robot?</h2>@@@ -152,8 +158,8 @@ ${mine && mine.length ? `<ul class="dir-list">${mine.map((r) => `<li class="dir-<p class="muted">Robots people share on OpenVibe.Bot, and every model it drives. Watch any listed robot live; some take visitors at the controls in turns.</p></header>${yours}<section class="dir-sec" aria-labelledby="listed-h"><h2 id="listed-h">In the directory</h2>--${listed.length ? `<ul class="dir-grid">${listed.map(robotCard).join('')}</ul>`-- : `<p class="muted">No robot is listed yet. An owner lists theirs from its panel (Directory), and it shows here with its live camera.</p>`}</section>++<ul class="dir-grid">${DEMO_CARD}${listed.map(robotCard).join('')}</ul>++${listed.length ? '' : '<p class="muted">Owners list their robots from the panel (Directory), and each shows here with its live camera.</p>'}</section><section class="dir-sec" aria-labelledby="models-h"><div class="dir-sec-head"><h2 id="models-h">Models it drives</h2>${link('/models', 'All models')}</div><ul class="dir-grid">${sortModels(models).map(modelCard).join('')}</ul></section><section class="dir-sec" aria-labelledby="byo-h"><h2 id="byo-h">Something else?</h2>@@@ -28,7 +28,7 @@ const { loadProfiles } = require('../server/profiles');for (const p of profiles) assert.ok(html.includes(`href="/models/${p.id}"`), p.id);assert.ok(html.includes('href="/try"') && html.includes('Sign in and add a robot'));assert.ok(!html.includes('Your robots') && !html.includes('Garage rover'), 'an unlisted robot is nobody else\'s business');-- assert.ok(html.includes('No robot is listed yet.'));++ assert.ok(html.includes('<a class="dir-card-link" href="/try"><span class="dir-card-head"><b>Simulated rover</b>'), 'the simulated rover is always the first robot to drive');});await check('signed in: your robots, each marked listed or not, and Add a robot', async () => {
@@ -61,5 +61,5 @@
@@ -61,5 +61,5 @@
"server-side ONVIF cameras (profile camera.onvif, driver onvif, server/onvif): an in-process device beside the simulator attaches a camera configured with BOT_ONVIF_CAMERAS (its URL plus username_ref/password_ref secret references, never in the profile or in clear), writes a real kind server device row, translates ptz commands to ONVIF ContinuousMove/Stop with the profile deadline as a deadman, and carries the camera's GetStatus readout in telemetry; every request goes only to the configured host, with a timeout and no redirects",
"server-side ONVIF cameras (profile camera.onvif, driver onvif, server/onvif): an in-process device beside the simulator attaches a camera configured with BOT_ONVIF_CAMERAS (its URL plus username_ref/password_ref secret references, never in the profile or in clear), writes a real kind server device row, translates ptz commands to ONVIF ContinuousMove/Stop with the profile deadline as a deadman, and carries the camera's GetStatus readout in telemetry; every request goes only to the configured host, with a timeout and no redirects",
"a person's own machines that run their jobs (/machines: add with a --worker command, list, remove); their worker offers reported to Network as user-owned offers naming the owner; jobs reach a machine only for its owner (plan T14/T16)"
"a person's own machines that run their jobs (/machines: add with a --worker command, list, remove); their worker offers reported to Network as user-owned offers naming the owner; jobs reach a machine only for its owner (plan T14/T16)"
],
],
- "updated": "2026-10-15"
+ "updated": "2026-10-16"
}
}
@@ -138,6 +138,12 @@ function robotCard(r) {
@@ -138,6 +138,12 @@ function robotCard(r) {
+ `<p class="dir-card-foot">${esc(POLICY_WORDS[r.access_policy] || '')}</p></li>`;
+ `<p class="dir-card-foot">${esc(POLICY_WORDS[r.access_policy] || '')}</p></li>`;
}
}
+// The simulated rover anyone can drive (/try): always the first card, so the directory shows a robot to drive today.
+const DEMO_CARD = '<li class="dir-card"><a class="dir-card-link" href="/try"><span class="dir-card-head"><b>Simulated rover</b>'
+ + '<span class="dir-state" data-state="online"><span class="dir-dot"></span>Online</span></span>'
+ + '<span class="dir-card-text">Runs in your browser · OpenVibe.Bot</span></a>'
+ + '<p class="dir-card-foot">Anyone can drive it, no account needed</p></li>';
+
// ── GET /robots ─────────────────────────────────────────────────────────────────────────────────────
// ── GET /robots ─────────────────────────────────────────────────────────────────────────────────────
function renderDirectory({ config, listed = [], mine = null, models = [], signedIn = false }) {
function renderDirectory({ config, listed = [], mine = null, models = [], signedIn = false }) {
const yours = !signedIn ? `<section class="dir-sec dir-cta"><h2>Have a robot?</h2>
const yours = !signedIn ? `<section class="dir-sec dir-cta"><h2>Have a robot?</h2>
@@ -152,8 +158,8 @@ ${mine && mine.length ? `<ul class="dir-list">${mine.map((r) => `<li class="dir-
@@ -152,8 +158,8 @@ ${mine && mine.length ? `<ul class="dir-list">${mine.map((r) => `<li class="dir-
<p class="muted">Robots people share on OpenVibe.Bot, and every model it drives. Watch any listed robot live; some take visitors at the controls in turns.</p></header>
<p class="muted">Robots people share on OpenVibe.Bot, and every model it drives. Watch any listed robot live; some take visitors at the controls in turns.</p></header>
${yours}
${yours}
<section class="dir-sec" aria-labelledby="listed-h"><h2 id="listed-h">In the directory</h2>
<section class="dir-sec" aria-labelledby="listed-h"><h2 id="listed-h">In the directory</h2>
-${listed.length ? `<ul class="dir-grid">${listed.map(robotCard).join('')}</ul>`
- : `<p class="muted">No robot is listed yet. An owner lists theirs from its panel (Directory), and it shows here with its live camera.</p>`}</section>
+<ul class="dir-grid">${DEMO_CARD}${listed.map(robotCard).join('')}</ul>
+${listed.length ? '' : '<p class="muted">Owners list their robots from the panel (Directory), and each shows here with its live camera.</p>'}</section>
<section class="dir-sec" aria-labelledby="models-h"><div class="dir-sec-head"><h2 id="models-h">Models it drives</h2>${link('/models', 'All models')}</div>
<section class="dir-sec" aria-labelledby="models-h"><div class="dir-sec-head"><h2 id="models-h">Models it drives</h2>${link('/models', 'All models')}</div>
<ul class="dir-grid">${sortModels(models).map(modelCard).join('')}</ul></section>
<ul class="dir-grid">${sortModels(models).map(modelCard).join('')}</ul></section>
<section class="dir-sec" aria-labelledby="byo-h"><h2 id="byo-h">Something else?</h2>
<section class="dir-sec" aria-labelledby="byo-h"><h2 id="byo-h">Something else?</h2>
@@ -28,7 +28,7 @@ const { loadProfiles } = require('../server/profiles');
@@ -28,7 +28,7 @@ const { loadProfiles } = require('../server/profiles');
for (const p of profiles) assert.ok(html.includes(`href="/models/${p.id}"`), p.id);
for (const p of profiles) assert.ok(html.includes(`href="/models/${p.id}"`), p.id);
assert.ok(html.includes('href="/try"') && html.includes('Sign in and add a robot'));
assert.ok(html.includes('href="/try"') && html.includes('Sign in and add a robot'));
assert.ok(!html.includes('Your robots') && !html.includes('Garage rover'), 'an unlisted robot is nobody else\'s business');
assert.ok(!html.includes('Your robots') && !html.includes('Garage rover'), 'an unlisted robot is nobody else\'s business');
- assert.ok(html.includes('No robot is listed yet.'));
+ assert.ok(html.includes('<a class="dir-card-link" href="/try"><span class="dir-card-head"><b>Simulated rover</b>'), 'the simulated rover is always the first robot to drive');
});
});
await check('signed in: your robots, each marked listed or not, and Add a robot', async () => {
await check('signed in: your robots, each marked listed or not, and Add a robot', async () => {