openvibers/openvibe.bot Commits
Document the device panel path and profile-driver mapping in README
Changes
@@@ -23,6 +23,11 @@ OpenVibe.Bot pairs a robot's machines, keeps their state and gates every operato- The agent that runs on the robot; it ships with the T15 device-agent job.- The public openvibe.bot front page and legal pages, built by OpenVibe.Sites.++## Devices++++- A device (the machine that serves a robot) pairs with one pasted command, shown on the robot's pairing page: `curl -fsSL <BOT_INSTALLER_URL> | sh -s -- --robot rob_… --code XXXX-XXXX [--driver …]` (the Network form in `docs/protocol.md` §1). The `--driver` follows the robot's profile: `adeept` for `adeept.adr036`, `adeept-mecanum` for `adeept.adr036.mecanum`, `cozmo` for `cozmo`; any other profile gets none (`server/domain/index.js` `driverForProfile`), the installer's dry-run `none`.++- The robot's panel is `openvibe.bot/panel/<rob_…>` (`GET /panel/:id`, rendered from the profile; `docs/protocol.md` §3). The device-side bring-up steps for the Adeept ADR036 kit live with the driver, in OpenVibe.Node's `docs/hardware-adeept.md` (T15 step 1).++## Depends on- PostgreSQL 18: robots, devices, profile rows, pairing codes, the command audit and the event outbox; `migrations/` applies on boot.@@@ -7,6 +7,7 @@ const fs = require('fs');const path = require('path');const { execFileSync } = require('child_process');const { check, done } = require('./helpers/app');++const { driverForProfile } = require('../server/domain');const ROOT = path.join(__dirname, '..');const MANIFEST_REPOSITORY = 'OpenVibers/OpenVibe.Bot';@@@ -33,6 +34,16 @@ const REQUIRED = [for (const [label, re] of REQUIRED) assert.ok(headings.some((h) => re.test(h)), `README: no "## ${label}" section`);});++ await check('the README names the panel path and the driver mapping the device runbook cites', () => {++ assert.match(readme, /openvibe\.bot\/panel\//, 'README: no openvibe.bot/panel/ URL');++ assert.match(readme, /adeept\.adr036\.mecanum/, 'README: no adeept.adr036.mecanum profile');++ assert.match(readme, /adeept-mecanum/, 'README: no adeept-mecanum installer driver');++ // …and the claims are real: the profile ships, its installer driver is adeept-mecanum, and the panel route exists.++ assert.ok(fs.existsSync(path.join(ROOT, 'server/profiles/adeept.adr036.mecanum.json')), 'no adeept.adr036.mecanum profile file');++ assert.strictEqual(driverForProfile('adeept.adr036.mecanum'), 'adeept-mecanum');++ assert.match(fs.readFileSync(path.join(ROOT, 'server/web/routes.js'), 'utf8'), /r\.get\('\/panel\/:id'/, 'no GET /panel/:id route');++ });++await check('STATUS.json has the required fields and the manifest repository', () => {for (const f of STATUS_FIELDS) assert.ok(status[f] !== undefined, `STATUS.json: no "${f}"`);assert.strictEqual(status.repository, MANIFEST_REPOSITORY);
@@ -23,6 +23,11 @@ OpenVibe.Bot pairs a robot's machines, keeps their state and gates every operato
@@ -23,6 +23,11 @@ OpenVibe.Bot pairs a robot's machines, keeps their state and gates every operato
- The agent that runs on the robot; it ships with the T15 device-agent job.
- The agent that runs on the robot; it ships with the T15 device-agent job.
- The public openvibe.bot front page and legal pages, built by OpenVibe.Sites.
- The public openvibe.bot front page and legal pages, built by OpenVibe.Sites.
+## Devices
+
+- A device (the machine that serves a robot) pairs with one pasted command, shown on the robot's pairing page: `curl -fsSL <BOT_INSTALLER_URL> | sh -s -- --robot rob_… --code XXXX-XXXX [--driver …]` (the Network form in `docs/protocol.md` §1). The `--driver` follows the robot's profile: `adeept` for `adeept.adr036`, `adeept-mecanum` for `adeept.adr036.mecanum`, `cozmo` for `cozmo`; any other profile gets none (`server/domain/index.js` `driverForProfile`), the installer's dry-run `none`.
+- The robot's panel is `openvibe.bot/panel/<rob_…>` (`GET /panel/:id`, rendered from the profile; `docs/protocol.md` §3). The device-side bring-up steps for the Adeept ADR036 kit live with the driver, in OpenVibe.Node's `docs/hardware-adeept.md` (T15 step 1).
+
## Depends on
## Depends on
- PostgreSQL 18: robots, devices, profile rows, pairing codes, the command audit and the event outbox; `migrations/` applies on boot.
- PostgreSQL 18: robots, devices, profile rows, pairing codes, the command audit and the event outbox; `migrations/` applies on boot.
@@ -7,6 +7,7 @@ const fs = require('fs');
@@ -7,6 +7,7 @@ const fs = require('fs');
const path = require('path');
const path = require('path');
const { execFileSync } = require('child_process');
const { execFileSync } = require('child_process');
const { check, done } = require('./helpers/app');
const { check, done } = require('./helpers/app');
+const { driverForProfile } = require('../server/domain');
const ROOT = path.join(__dirname, '..');
const ROOT = path.join(__dirname, '..');
const MANIFEST_REPOSITORY = 'OpenVibers/OpenVibe.Bot';
const MANIFEST_REPOSITORY = 'OpenVibers/OpenVibe.Bot';
@@ -33,6 +34,16 @@ const REQUIRED = [
@@ -33,6 +34,16 @@ const REQUIRED = [
for (const [label, re] of REQUIRED) assert.ok(headings.some((h) => re.test(h)), `README: no "## ${label}" section`);
for (const [label, re] of REQUIRED) assert.ok(headings.some((h) => re.test(h)), `README: no "## ${label}" section`);
});
});
+ await check('the README names the panel path and the driver mapping the device runbook cites', () => {
+ assert.match(readme, /openvibe\.bot\/panel\//, 'README: no openvibe.bot/panel/ URL');
+ assert.match(readme, /adeept\.adr036\.mecanum/, 'README: no adeept.adr036.mecanum profile');
+ assert.match(readme, /adeept-mecanum/, 'README: no adeept-mecanum installer driver');
+ // …and the claims are real: the profile ships, its installer driver is adeept-mecanum, and the panel route exists.
+ assert.ok(fs.existsSync(path.join(ROOT, 'server/profiles/adeept.adr036.mecanum.json')), 'no adeept.adr036.mecanum profile file');
+ assert.strictEqual(driverForProfile('adeept.adr036.mecanum'), 'adeept-mecanum');
+ assert.match(fs.readFileSync(path.join(ROOT, 'server/web/routes.js'), 'utf8'), /r\.get\('\/panel\/:id'/, 'no GET /panel/:id route');
+ });
+
await check('STATUS.json has the required fields and the manifest repository', () => {
await check('STATUS.json has the required fields and the manifest repository', () => {
for (const f of STATUS_FIELDS) assert.ok(status[f] !== undefined, `STATUS.json: no "${f}"`);
for (const f of STATUS_FIELDS) assert.ok(status[f] !== undefined, `STATUS.json: no "${f}"`);
assert.strictEqual(status.repository, MANIFEST_REPOSITORY);
assert.strictEqual(status.repository, MANIFEST_REPOSITORY);