openvibers/openvibe.bot Commits
Bot: bot.robot.online/offline name the robot's owner (contracts 0.192.0), for Quest's robot quest (#129)
Changes
@@@ -146,7 +146,7 @@ Set `BOT_ONVIF_CAMERAS` (JSON, unset by default) to the server-side cameras BotOpenVibe.Sites no longer generates `deploy/nginx/openvibe.bot.conf` (sites.json marks the vhost as owned by OpenVibe.Bot), and Bot serves nothing from a Sites checkout: the legal pages, `robots.txt`, `sitemap.xml`, `manifest.webmanifest` and the 404 page all come from the app. A Sites deploy leaves an installed vhost it no longer generates in place, so the current Sites vhost keeps serving openvibe.bot until Bot's first deploy installs this file over it; later Sites deploys never overwrite Bot's `/device` and `/control` routes.<!-- versions:start -->--- openvibe-contracts: v0.188.2++- openvibe-contracts: v0.192.0- openvibe-sdk: v0.46.0- openvibe-shared: v4.2.1<!-- versions:end -->@@@ -5,11 +5,11 @@"runtime": "node 22, express 4, ws; PostgreSQL 18 via openvibe-sdk/db (pg, or embedded PGlite in tests); Valkey optional; no build step","port": 4630,"domain": "openvibe.bot (www -> apex; /device and /control WebSockets; /install)",-- "contracts": "openvibe-contracts v0.188.2",++ "contracts": "openvibe-contracts v0.192.0","sdk": "openvibe-sdk v0.46.0","shared": "openvibe-shared v4.2.1","packages": [-- "openvibe-contracts v0.188.2",++ "openvibe-contracts v0.192.0","openvibe-sdk v0.46.0","openvibe-shared v4.2.1"],@@@ -13,7 +13,7 @@"dotenv": "^16.4.7","express": "^4.21.2","iovalkey": "^0.4.0",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.188.2",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.192.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.46.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.2.1","pg": "^8.23.0",@@@ -845,9 +845,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.188.2",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.188.2",-- "integrity": "sha512-E64gQI7roKudmmJFnenyh1BjViJyI0j0MoVT4o35/jUD4rpzMkElwEh/fGbsPpnstdDFIcJR6gEcScbL0ZQhbg==",++ "version": "0.192.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.192.0",++ "integrity": "sha512-cNHARCKL9EqhXVsHTNT79JCSLwWimER8MxRH22Ko11h+ITxOPONmzQjPnLMScIEStQfGiFlbpVrbCXvSE9/GVw==","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -25,7 +25,7 @@"dotenv": "^16.4.7","express": "^4.21.2","iovalkey": "^0.4.0",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.188.2",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.192.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.46.0","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.2.1","pg": "^8.23.0",@@@ -794,9 +794,12 @@ function createDomain({ db, config, outbox, link = null, nodes = null, openre =const at = iso(now());await db.tx(async (t) => {if (online) await t.query('UPDATE devices SET last_seen = $2 WHERE id = $1', [device.id, at]);++ // The owner rides along (contracts 0.192.0): OpenVibe.Quest credits "Bring a robot online" from it.++ const owners = new Map(robotIds.length ? (await t.many('SELECT id, owner_subject FROM robots WHERE id = ANY($1::text[])', [robotIds])).map((r) => [r.id, r.owner_subject]) : []);for (const robotId of robotIds) {++ const owner = owners.get(robotId);await emit(t, online ? 'bot.robot.online' : 'bot.robot.offline', { type: 'robot', id: robotId },-- { robot_id: robotId, device_id: device.id });++ { robot_id: robotId, device_id: device.id, ...(/^usr_[0-9A-HJKMNP-TV-Z]{26}$/.test(String(owner || '')) ? { owner } : {}) });}});}@@@ -73,6 +73,10 @@ const { boot, check, done } = require('./helpers/app');await t.wait(50);const rows = await t.outboxRows('bot.robot.offline');assert.ok(rows.some((e) => e.payload.robot_id === robot.id), 'bot.robot.offline for the robot');++ // Both events name the robot's owner (contracts 0.192.0), so Quest can credit "Bring a robot online".++ assert.ok(rows.some((e) => e.payload.robot_id === robot.id && e.payload.owner === alex.subject), 'the owner on bot.robot.offline');++ const online = await t.outboxRows('bot.robot.online');++ assert.ok(online.some((e) => e.payload.robot_id === robot.id && e.payload.owner === alex.subject), 'the owner on bot.robot.online');dev.close();});
@@ -146,7 +146,7 @@ Set `BOT_ONVIF_CAMERAS` (JSON, unset by default) to the server-side cameras Bot
@@ -146,7 +146,7 @@ Set `BOT_ONVIF_CAMERAS` (JSON, unset by default) to the server-side cameras Bot
OpenVibe.Sites no longer generates `deploy/nginx/openvibe.bot.conf` (sites.json marks the vhost as owned by OpenVibe.Bot), and Bot serves nothing from a Sites checkout: the legal pages, `robots.txt`, `sitemap.xml`, `manifest.webmanifest` and the 404 page all come from the app. A Sites deploy leaves an installed vhost it no longer generates in place, so the current Sites vhost keeps serving openvibe.bot until Bot's first deploy installs this file over it; later Sites deploys never overwrite Bot's `/device` and `/control` routes.
OpenVibe.Sites no longer generates `deploy/nginx/openvibe.bot.conf` (sites.json marks the vhost as owned by OpenVibe.Bot), and Bot serves nothing from a Sites checkout: the legal pages, `robots.txt`, `sitemap.xml`, `manifest.webmanifest` and the 404 page all come from the app. A Sites deploy leaves an installed vhost it no longer generates in place, so the current Sites vhost keeps serving openvibe.bot until Bot's first deploy installs this file over it; later Sites deploys never overwrite Bot's `/device` and `/control` routes.
<!-- versions:start -->
<!-- versions:start -->
-- openvibe-contracts: v0.188.2
+- openvibe-contracts: v0.192.0
- openvibe-sdk: v0.46.0
- openvibe-sdk: v0.46.0
- openvibe-shared: v4.2.1
- openvibe-shared: v4.2.1
<!-- versions:end -->
<!-- versions:end -->
@@ -5,11 +5,11 @@
@@ -5,11 +5,11 @@
"runtime": "node 22, express 4, ws; PostgreSQL 18 via openvibe-sdk/db (pg, or embedded PGlite in tests); Valkey optional; no build step",
"runtime": "node 22, express 4, ws; PostgreSQL 18 via openvibe-sdk/db (pg, or embedded PGlite in tests); Valkey optional; no build step",
"port": 4630,
"port": 4630,
"domain": "openvibe.bot (www -> apex; /device and /control WebSockets; /install)",
"domain": "openvibe.bot (www -> apex; /device and /control WebSockets; /install)",
- "contracts": "openvibe-contracts v0.188.2",
+ "contracts": "openvibe-contracts v0.192.0",
"sdk": "openvibe-sdk v0.46.0",
"sdk": "openvibe-sdk v0.46.0",
"shared": "openvibe-shared v4.2.1",
"shared": "openvibe-shared v4.2.1",
"packages": [
"packages": [
- "openvibe-contracts v0.188.2",
+ "openvibe-contracts v0.192.0",
"openvibe-sdk v0.46.0",
"openvibe-sdk v0.46.0",
"openvibe-shared v4.2.1"
"openvibe-shared v4.2.1"
],
],
@@ -13,7 +13,7 @@
@@ -13,7 +13,7 @@
"dotenv": "^16.4.7",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"express": "^4.21.2",
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.188.2",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.192.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.46.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.46.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.2.1",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.2.1",
"pg": "^8.23.0",
"pg": "^8.23.0",
@@ -845,9 +845,9 @@
@@ -845,9 +845,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.188.2",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.188.2",
- "integrity": "sha512-E64gQI7roKudmmJFnenyh1BjViJyI0j0MoVT4o35/jUD4rpzMkElwEh/fGbsPpnstdDFIcJR6gEcScbL0ZQhbg==",
+ "version": "0.192.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.192.0",
+ "integrity": "sha512-cNHARCKL9EqhXVsHTNT79JCSLwWimER8MxRH22Ko11h+ITxOPONmzQjPnLMScIEStQfGiFlbpVrbCXvSE9/GVw==",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -25,7 +25,7 @@
@@ -25,7 +25,7 @@
"dotenv": "^16.4.7",
"dotenv": "^16.4.7",
"express": "^4.21.2",
"express": "^4.21.2",
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.188.2",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.192.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.46.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.46.0",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.2.1",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v4.2.1",
"pg": "^8.23.0",
"pg": "^8.23.0",
@@ -794,9 +794,12 @@ function createDomain({ db, config, outbox, link = null, nodes = null, openre =
@@ -794,9 +794,12 @@ function createDomain({ db, config, outbox, link = null, nodes = null, openre =
const at = iso(now());
const at = iso(now());
await db.tx(async (t) => {
await db.tx(async (t) => {
if (online) await t.query('UPDATE devices SET last_seen = $2 WHERE id = $1', [device.id, at]);
if (online) await t.query('UPDATE devices SET last_seen = $2 WHERE id = $1', [device.id, at]);
+ // The owner rides along (contracts 0.192.0): OpenVibe.Quest credits "Bring a robot online" from it.
+ const owners = new Map(robotIds.length ? (await t.many('SELECT id, owner_subject FROM robots WHERE id = ANY($1::text[])', [robotIds])).map((r) => [r.id, r.owner_subject]) : []);
for (const robotId of robotIds) {
for (const robotId of robotIds) {
+ const owner = owners.get(robotId);
await emit(t, online ? 'bot.robot.online' : 'bot.robot.offline', { type: 'robot', id: robotId },
await emit(t, online ? 'bot.robot.online' : 'bot.robot.offline', { type: 'robot', id: robotId },
- { robot_id: robotId, device_id: device.id });
+ { robot_id: robotId, device_id: device.id, ...(/^usr_[0-9A-HJKMNP-TV-Z]{26}$/.test(String(owner || '')) ? { owner } : {}) });
}
}
});
});
}
}
@@ -73,6 +73,10 @@ const { boot, check, done } = require('./helpers/app');
@@ -73,6 +73,10 @@ const { boot, check, done } = require('./helpers/app');
await t.wait(50);
await t.wait(50);
const rows = await t.outboxRows('bot.robot.offline');
const rows = await t.outboxRows('bot.robot.offline');
assert.ok(rows.some((e) => e.payload.robot_id === robot.id), 'bot.robot.offline for the robot');
assert.ok(rows.some((e) => e.payload.robot_id === robot.id), 'bot.robot.offline for the robot');
+ // Both events name the robot's owner (contracts 0.192.0), so Quest can credit "Bring a robot online".
+ assert.ok(rows.some((e) => e.payload.robot_id === robot.id && e.payload.owner === alex.subject), 'the owner on bot.robot.offline');
+ const online = await t.outboxRows('bot.robot.online');
+ assert.ok(online.some((e) => e.payload.robot_id === robot.id && e.payload.owner === alex.subject), 'the owner on bot.robot.online');
dev.close();
dev.close();
});
});