openvibers/openvibe.network Commits
Network: OpenVibe.Actor signs in (OAuth client actor, https://openvibe.actor/auth/callback), is on the home page and in the frame, and contracts move to v0.115.0. (#84)
Changes
@@@ -21,7 +21,7 @@"iovalkey": "^0.4.0","jsonwebtoken": "^9.0.0","multer": "^1.4.5-lts.1",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.114.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.115.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.35.1","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.2","pg": "^8.23.0",@@@ -1556,9 +1556,9 @@}},"node_modules/openvibe-contracts": {-- "version": "0.114.0",-- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.114.0",-- "integrity": "sha512-MLtahxYztHGzN09L0D6TuXzPoueuqb/6+v9QVivaaGMO92PDhGK3Q9mg9f0oWX6pYa+iL25Au5sI2MEK0spsRw==",++ "version": "0.115.0",++ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.115.0",++ "integrity": "sha512-2si6od4+RLgZEIAuWMmTrLZL65TzvCwB/sKoe24vSzC9+sNPt2RXWGXDCwE2fB90vmaRvX6pI+DQyu18QN2f0Q==","license": "AGPL-3.0-only","dependencies": {"ajv": "^8.17.1",@@@ -30,7 +30,7 @@"iovalkey": "^0.4.0","jsonwebtoken": "^9.0.0","multer": "^1.4.5-lts.1",-- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.114.0",++ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.115.0","openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.35.1","openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.2","pg": "^8.23.0",@@@ -108,6 +108,8 @@ async function seedDb(db, { log = console } = {}) {{ client_id: 'billing', name: 'OpenVibe.Billing', redirect_uris: ['https://billing.openvibe.network/auth/callback'] },{ client_id: 'ai', name: 'OpenVibe.AI', redirect_uris: ['https://ai.openvibe.services/auth/callback'] },{ client_id: 'bot', name: 'OpenVibe.Bot', redirect_uris: ['https://openvibe.bot/auth/callback'] },++ // OpenVibe's own agent and the router for agent work (plan T17); the client is created first as Actor's principal.++ { client_id: 'actor', name: 'OpenVibe.Actor', redirect_uris: ['https://openvibe.actor/auth/callback'] },];let seededAny = false;for (const c of contractClients) {@@@ -133,6 +135,7 @@ async function seedDb(db, { log = console } = {}) {{ clientId: 'media', extraUris: ['http://localhost:4100/auth/callback'] },{ clientId: 'community', extraUris: ['http://localhost:4200/auth/callback'] },{ clientId: 'space', extraUris: ['http://localhost:4940/auth/callback'] },++ { clientId: 'actor', extraUris: ['http://localhost:4950/auth/callback'] },];for (const { clientId, extraUris } of localClients) {try { await mergeRedirectUris(db, clientId, extraUris, { log, local: true }); }@@@ -47,6 +47,7 @@ const INTENTS = [['wiki', 'fa-book-open', 'Look it up', 'Wiki pages with sources and history.', 'https://openvibe.wiki/', 'wiki learn read knowledge research look'],['services', 'fa-server', 'Build on OpenVibe', 'One console for projects, keys, grants and the API docs of every service.', 'https://openvibe.services/', 'build api sdk developer developers oauth webhook webhooks app integrate console platform'],['codes', 'fa-code', 'Code with any agent', 'Claude Code, Codex, OpenCode or your own model on one task, with hand-offs.', 'https://openvibe.codes/', 'code coding agent agents ai claude codex opencode deepseek harness contribute'],++ ['actor', 'fa-user-astronaut', 'Give a task to an agent', 'Actor picks the best agent for it, checks the answer and shows the cost and why.', 'https://openvibe.actor/', 'agent agents ai assistant task tasks automate research router openrouter do it for me'],];function intents(catalog) {@@@ -69,7 +69,7 @@ function createUpdatesRoutes({ blogUrl = 'http://127.0.0.1:4810', fetchImpl = glreturn { router: r, feed, queryOf };}--const SITES = ['live', 'network', 'tools', 'media', 'community', 'space', 'chat', 'games', 'blog', 'wiki', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes', 'services', 'host', 'ai', 'search', 'sources', 'events', 'billing', 'tips', 'vip', 'openre', 'bot', 'sites'];++const SITES = ['live', 'network', 'tools', 'media', 'community', 'space', 'chat', 'games', 'blog', 'wiki', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes', 'services', 'host', 'ai', 'search', 'sources', 'events', 'billing', 'tips', 'vip', 'openre', 'bot', 'actor', 'sites'];const NAMES = { ai: 'AI', vip: 'VIP', openre: 'OpenRe' };const nameOf = (id) => NAMES[id] || (id ? id.charAt(0).toUpperCase() + id.slice(1) : '');@@@ -22,7 +22,8 @@ console.log = log;const PEOPLE = [[10, 'owner'], [11, 'dev'], [12, 'viewer'], [13, 'stranger'], [14, 'staff'], [15, 'admin']];await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES(10, 'owner', 'x', 'user'), (11, 'dev', 'x', 'user'), (12, 'viewer', 'x', 'user'), (13, 'stranger', 'x', 'user'), (14, 'staff', 'x', 'admin'), (15, 'admin', 'x', 'user')`).run();--await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]', 1)").run();++// The seed creates Actor's client (server/db/database.js); this file sets the fields it relies on.++await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]', 1) ON CONFLICT (client_id) DO UPDATE SET client_secret = EXCLUDED.client_secret, name = EXCLUDED.name, redirect_uris = EXCLUDED.redirect_uris, is_first_party = EXCLUDED.is_first_party").run();const S = {};for (const [id, name] of PEOPLE) S[name] = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = ?').get(id));@@@ -24,7 +24,8 @@ const confirmations = require('../server/developer/confirmations');const log = console.log; console.log = () => {};const db = getDb();console.log = log;--await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'actor-secret', 'OpenVibe.Actor', '[]', 1)").run();++// The seed creates Actor's client (server/db/database.js); this file sets the fields it relies on.++await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'actor-secret', 'OpenVibe.Actor', '[]', 1) ON CONFLICT (client_id) DO UPDATE SET client_secret = EXCLUDED.client_secret, name = EXCLUDED.name, redirect_uris = EXCLUDED.redirect_uris, is_first_party = EXCLUDED.is_first_party").run();await db.prepare("UPDATE oauth_clients SET client_secret = 'live-secret' WHERE client_id = 'live'").run();await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES (10, 'owner', 'x', 'user'), (14, 'staff', 'x', 'admin')`).run();const owner = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = 10').get());@@@ -22,7 +22,8 @@ console.log = log;const PG = process.env.NETWORK_TEST_STORE === 'pg';const USERS = [[10, 'owner'], [11, 'dev'], [12, 'leaver'], [13, 'stayer']];await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES ${USERS.map(([id, n]) => `(${id}, '${n}', 'x', 'user')`).join(', ')}`).run();--await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]')").run();++// The seed creates Actor's client (server/db/database.js); this file sets the fields it relies on.++await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]') ON CONFLICT (client_id) DO UPDATE SET client_secret = EXCLUDED.client_secret, name = EXCLUDED.name, redirect_uris = EXCLUDED.redirect_uris, is_first_party = 0").run();const S = {};for (const [id, name] of USERS) S[name] = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = ?').get(id));@@@ -21,7 +21,8 @@ console.log = log;await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES(10, 'owner', 'x', 'user'), (11, 'dev', 'x', 'user'), (12, 'viewer', 'x', 'user'), (13, 'stranger', 'x', 'user'),(14, 'staff', 'x', 'admin'), (15, 'admin', 'x', 'user'), (16, 'leaver', 'x', 'user'), (17, 'erased', 'x', 'user')`).run();--await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]')").run();++// The seed creates Actor's client (server/db/database.js); this file sets the fields it relies on.++await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]') ON CONFLICT (client_id) DO UPDATE SET client_secret = EXCLUDED.client_secret, name = EXCLUDED.name, redirect_uris = EXCLUDED.redirect_uris, is_first_party = 0").run();const S = {};for (const [id, name] of [[10, 'owner'], [11, 'dev'], [12, 'viewer'], [13, 'stranger'], [14, 'staff'], [15, 'admin'], [16, 'leaver'], [17, 'erased']]) {S[name] = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = ?').get(id));@@@ -26,7 +26,8 @@ console.log = log;const PEOPLE = [[10, 'owner'], [11, 'admin'], [12, 'dev'], [13, 'stranger'], [14, 'staff'], [17, 'erased']];await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES(10, 'owner', 'x', 'user'), (11, 'admin', 'x', 'user'), (12, 'dev', 'x', 'user'), (13, 'stranger', 'x', 'user'), (14, 'staff', 'x', 'admin'), (17, 'erased', 'x', 'user')`).run();--await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]', 1)").run();++// The seed creates Actor's client (server/db/database.js); this file sets the fields it relies on.++await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]', 1) ON CONFLICT (client_id) DO UPDATE SET client_secret = EXCLUDED.client_secret, name = EXCLUDED.name, redirect_uris = EXCLUDED.redirect_uris, is_first_party = EXCLUDED.is_first_party").run();// No service holds network.confirmation.manage by default (no receiver ships yet): Media and Tools get it here, Live does not.for (const c of ['media', 'tools', 'live']) await db.prepare(`UPDATE oauth_clients SET client_secret = '${c}-secret' WHERE client_id = ?`).run(c);for (const c of ['media', 'tools']) await db.prepare("INSERT INTO principal_grants (client_id, capability, audience, namespaces, granted_by) VALUES (?, 'network.confirmation.manage', 'openvibe.network', '[]', 'test')").run(c);@@@ -14,7 +14,7 @@ assert.equal(siteForHost('yt.openvibe.tools').id, 'tools');assert.equal(siteForHost('evil.example'), null);assert.strictEqual(svc.ranking().at, null, 'cold start: no use counted yet (the registry\'s featured list says so)');let p = svc.payloadFor('openvibe.media');--assert.deepEqual(p.nav.map(n => n.id), ['live', 'tools', 'space', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'host', 'bot', 'codes'], 'cold start follows the base order');++assert.deepEqual(p.nav.map(n => n.id), ['live', 'tools', 'space', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'host', 'bot', 'codes', 'actor'], 'cold start follows the base order');assert.strictEqual(p.nav.find(n => n.id === 'space').url, 'https://openvibe.space/', 'the forum goes to Space');assert.strictEqual(p.nav.find(n => n.id === 'community').url, 'https://openvibe.community/', 'Community remains for pastes and Pulse');assert.equal(p.footer.legal.dmca, 'https://openvibe.media/dmca', 'legal links stay on the site\'s own domain');@@@ -22,6 +22,7 @@ const PINNED = {ai: 'live', // the public home and developer-app API at ai.openvibe.services since 2026-10-07 (contracts 0.110.0)// openvibe-contracts v0.86-v0.90: Bot runs (its own exposure row); Zone is a charter only.bot: 'live', zone: 'placeholder',++ actor: 'live', // OpenVibe's own agent and the router for agent work at openvibe.actor since 2026-10-08 (contracts 0.115.0)run: 'internal', // OpenVibe.Run runs on loopback since 2026-10-07 (contracts 0.109.0)services: 'live', // OpenVibe.Services: the developer platform at openvibe.services since 2026-10-08 (contracts 0.113.0; the portal moved from Codes)// openvibe-contracts v0.102.0: OpenVibe.Watch is a charter only (plan T18).@@@ -110,7 +111,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], chassert.strictEqual(sBy.shared.label, `not running (library, released v${require('openvibe-shared/package.json').version})`);// Contracts 0.83.0 added two placeholder services (media-hub, space), v0.86-v0.90 Bot (live) and two more (zone, run),// v0.95.0 Services; Space and AI went live in v0.110.0; none is retired-- assert.deepStrictEqual(st.exposure_summary, { live: 19, internal: 11, library: 4, repository: 1, placeholder: 2, retired: 0 });++ assert.deepStrictEqual(st.exposure_summary, { live: 20, internal: 11, library: 4, repository: 1, placeholder: 2, retired: 0 });// The page: no row for a service whose domain serves a placeholder shows a bare "Up".const html = await fetch(base + '/status').then(x => x.text());@@@ -126,7 +127,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch// 3. The Frame: the nav lists only sites whose domain serves the service; the rest are soon with a reason.const open = SITES.filter(s => s.status === 'open').map(s => s.id);-- assert.deepStrictEqual(open, ['live', 'tools', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'host', 'space', 'bot', 'codes']); // by site position; bot: a site since openvibe-contracts 0.106.0; host: live since 0.107.0; space: live since 0.110.0 (the forum, 2026-10-07)++ assert.deepStrictEqual(open, ['live', 'tools', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'host', 'space', 'bot', 'codes', 'actor']); // by site position; bot: a site since openvibe-contracts 0.106.0; host: live since 0.107.0; space: live since 0.110.0 (the forum, 2026-10-07)for (const s of SITES) {assert.ok(s.service && PINNED[s.service], `${s.id} names its service`);assert.strictEqual(s.status === 'open', PINNED[s.service] === 'live', `${s.id} nav status follows exposure`);
@@ -21,7 +21,7 @@
@@ -21,7 +21,7 @@
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
"jsonwebtoken": "^9.0.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
"multer": "^1.4.5-lts.1",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.114.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.115.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.35.1",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.35.1",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.2",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.2",
"pg": "^8.23.0",
"pg": "^8.23.0",
@@ -1556,9 +1556,9 @@
@@ -1556,9 +1556,9 @@
}
}
},
},
"node_modules/openvibe-contracts": {
"node_modules/openvibe-contracts": {
- "version": "0.114.0",
- "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.114.0",
- "integrity": "sha512-MLtahxYztHGzN09L0D6TuXzPoueuqb/6+v9QVivaaGMO92PDhGK3Q9mg9f0oWX6pYa+iL25Au5sI2MEK0spsRw==",
+ "version": "0.115.0",
+ "resolved": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.115.0",
+ "integrity": "sha512-2si6od4+RLgZEIAuWMmTrLZL65TzvCwB/sKoe24vSzC9+sNPt2RXWGXDCwE2fB90vmaRvX6pI+DQyu18QN2f0Q==",
"license": "AGPL-3.0-only",
"license": "AGPL-3.0-only",
"dependencies": {
"dependencies": {
"ajv": "^8.17.1",
"ajv": "^8.17.1",
@@ -30,7 +30,7 @@
@@ -30,7 +30,7 @@
"iovalkey": "^0.4.0",
"iovalkey": "^0.4.0",
"jsonwebtoken": "^9.0.0",
"jsonwebtoken": "^9.0.0",
"multer": "^1.4.5-lts.1",
"multer": "^1.4.5-lts.1",
- "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.114.0",
+ "openvibe-contracts": "https://codeload.github.com/OpenVibers/OpenVibe.Contracts/tar.gz/refs/tags/v0.115.0",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.35.1",
"openvibe-sdk": "https://codeload.github.com/OpenVibers/OpenVibe.SDK/tar.gz/refs/tags/v0.35.1",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.2",
"openvibe-shared": "https://codeload.github.com/OpenVibers/OpenVibe.Shared/tar.gz/refs/tags/v2.13.2",
"pg": "^8.23.0",
"pg": "^8.23.0",
@@ -108,6 +108,8 @@ async function seedDb(db, { log = console } = {}) {
@@ -108,6 +108,8 @@ async function seedDb(db, { log = console } = {}) {
{ client_id: 'billing', name: 'OpenVibe.Billing', redirect_uris: ['https://billing.openvibe.network/auth/callback'] },
{ client_id: 'billing', name: 'OpenVibe.Billing', redirect_uris: ['https://billing.openvibe.network/auth/callback'] },
{ client_id: 'ai', name: 'OpenVibe.AI', redirect_uris: ['https://ai.openvibe.services/auth/callback'] },
{ client_id: 'ai', name: 'OpenVibe.AI', redirect_uris: ['https://ai.openvibe.services/auth/callback'] },
{ client_id: 'bot', name: 'OpenVibe.Bot', redirect_uris: ['https://openvibe.bot/auth/callback'] },
{ client_id: 'bot', name: 'OpenVibe.Bot', redirect_uris: ['https://openvibe.bot/auth/callback'] },
+ // OpenVibe's own agent and the router for agent work (plan T17); the client is created first as Actor's principal.
+ { client_id: 'actor', name: 'OpenVibe.Actor', redirect_uris: ['https://openvibe.actor/auth/callback'] },
];
];
let seededAny = false;
let seededAny = false;
for (const c of contractClients) {
for (const c of contractClients) {
@@ -133,6 +135,7 @@ async function seedDb(db, { log = console } = {}) {
@@ -133,6 +135,7 @@ async function seedDb(db, { log = console } = {}) {
{ clientId: 'media', extraUris: ['http://localhost:4100/auth/callback'] },
{ clientId: 'media', extraUris: ['http://localhost:4100/auth/callback'] },
{ clientId: 'community', extraUris: ['http://localhost:4200/auth/callback'] },
{ clientId: 'community', extraUris: ['http://localhost:4200/auth/callback'] },
{ clientId: 'space', extraUris: ['http://localhost:4940/auth/callback'] },
{ clientId: 'space', extraUris: ['http://localhost:4940/auth/callback'] },
+ { clientId: 'actor', extraUris: ['http://localhost:4950/auth/callback'] },
];
];
for (const { clientId, extraUris } of localClients) {
for (const { clientId, extraUris } of localClients) {
try { await mergeRedirectUris(db, clientId, extraUris, { log, local: true }); }
try { await mergeRedirectUris(db, clientId, extraUris, { log, local: true }); }
@@ -47,6 +47,7 @@ const INTENTS = [
@@ -47,6 +47,7 @@ const INTENTS = [
['wiki', 'fa-book-open', 'Look it up', 'Wiki pages with sources and history.', 'https://openvibe.wiki/', 'wiki learn read knowledge research look'],
['wiki', 'fa-book-open', 'Look it up', 'Wiki pages with sources and history.', 'https://openvibe.wiki/', 'wiki learn read knowledge research look'],
['services', 'fa-server', 'Build on OpenVibe', 'One console for projects, keys, grants and the API docs of every service.', 'https://openvibe.services/', 'build api sdk developer developers oauth webhook webhooks app integrate console platform'],
['services', 'fa-server', 'Build on OpenVibe', 'One console for projects, keys, grants and the API docs of every service.', 'https://openvibe.services/', 'build api sdk developer developers oauth webhook webhooks app integrate console platform'],
['codes', 'fa-code', 'Code with any agent', 'Claude Code, Codex, OpenCode or your own model on one task, with hand-offs.', 'https://openvibe.codes/', 'code coding agent agents ai claude codex opencode deepseek harness contribute'],
['codes', 'fa-code', 'Code with any agent', 'Claude Code, Codex, OpenCode or your own model on one task, with hand-offs.', 'https://openvibe.codes/', 'code coding agent agents ai claude codex opencode deepseek harness contribute'],
+ ['actor', 'fa-user-astronaut', 'Give a task to an agent', 'Actor picks the best agent for it, checks the answer and shows the cost and why.', 'https://openvibe.actor/', 'agent agents ai assistant task tasks automate research router openrouter do it for me'],
];
];
function intents(catalog) {
function intents(catalog) {
@@ -69,7 +69,7 @@ function createUpdatesRoutes({ blogUrl = 'http://127.0.0.1:4810', fetchImpl = gl
@@ -69,7 +69,7 @@ function createUpdatesRoutes({ blogUrl = 'http://127.0.0.1:4810', fetchImpl = gl
return { router: r, feed, queryOf };
return { router: r, feed, queryOf };
}
}
-const SITES = ['live', 'network', 'tools', 'media', 'community', 'space', 'chat', 'games', 'blog', 'wiki', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes', 'services', 'host', 'ai', 'search', 'sources', 'events', 'billing', 'tips', 'vip', 'openre', 'bot', 'sites'];
+const SITES = ['live', 'network', 'tools', 'media', 'community', 'space', 'chat', 'games', 'blog', 'wiki', 'news', 'reviews', 'deals', 'coupons', 'trade', 'codes', 'services', 'host', 'ai', 'search', 'sources', 'events', 'billing', 'tips', 'vip', 'openre', 'bot', 'actor', 'sites'];
const NAMES = { ai: 'AI', vip: 'VIP', openre: 'OpenRe' };
const NAMES = { ai: 'AI', vip: 'VIP', openre: 'OpenRe' };
const nameOf = (id) => NAMES[id] || (id ? id.charAt(0).toUpperCase() + id.slice(1) : '');
const nameOf = (id) => NAMES[id] || (id ? id.charAt(0).toUpperCase() + id.slice(1) : '');
@@ -22,7 +22,8 @@ console.log = log;
@@ -22,7 +22,8 @@ console.log = log;
const PEOPLE = [[10, 'owner'], [11, 'dev'], [12, 'viewer'], [13, 'stranger'], [14, 'staff'], [15, 'admin']];
const PEOPLE = [[10, 'owner'], [11, 'dev'], [12, 'viewer'], [13, 'stranger'], [14, 'staff'], [15, 'admin']];
await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES
await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES
(10, 'owner', 'x', 'user'), (11, 'dev', 'x', 'user'), (12, 'viewer', 'x', 'user'), (13, 'stranger', 'x', 'user'), (14, 'staff', 'x', 'admin'), (15, 'admin', 'x', 'user')`).run();
(10, 'owner', 'x', 'user'), (11, 'dev', 'x', 'user'), (12, 'viewer', 'x', 'user'), (13, 'stranger', 'x', 'user'), (14, 'staff', 'x', 'admin'), (15, 'admin', 'x', 'user')`).run();
-await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]', 1)").run();
+// The seed creates Actor's client (server/db/database.js); this file sets the fields it relies on.
+await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]', 1) ON CONFLICT (client_id) DO UPDATE SET client_secret = EXCLUDED.client_secret, name = EXCLUDED.name, redirect_uris = EXCLUDED.redirect_uris, is_first_party = EXCLUDED.is_first_party").run();
const S = {};
const S = {};
for (const [id, name] of PEOPLE) S[name] = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = ?').get(id));
for (const [id, name] of PEOPLE) S[name] = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = ?').get(id));
@@ -24,7 +24,8 @@ const confirmations = require('../server/developer/confirmations');
@@ -24,7 +24,8 @@ const confirmations = require('../server/developer/confirmations');
const log = console.log; console.log = () => {};
const log = console.log; console.log = () => {};
const db = getDb();
const db = getDb();
console.log = log;
console.log = log;
-await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'actor-secret', 'OpenVibe.Actor', '[]', 1)").run();
+// The seed creates Actor's client (server/db/database.js); this file sets the fields it relies on.
+await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'actor-secret', 'OpenVibe.Actor', '[]', 1) ON CONFLICT (client_id) DO UPDATE SET client_secret = EXCLUDED.client_secret, name = EXCLUDED.name, redirect_uris = EXCLUDED.redirect_uris, is_first_party = EXCLUDED.is_first_party").run();
await db.prepare("UPDATE oauth_clients SET client_secret = 'live-secret' WHERE client_id = 'live'").run();
await db.prepare("UPDATE oauth_clients SET client_secret = 'live-secret' WHERE client_id = 'live'").run();
await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES (10, 'owner', 'x', 'user'), (14, 'staff', 'x', 'admin')`).run();
await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES (10, 'owner', 'x', 'user'), (14, 'staff', 'x', 'admin')`).run();
const owner = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = 10').get());
const owner = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = 10').get());
@@ -22,7 +22,8 @@ console.log = log;
@@ -22,7 +22,8 @@ console.log = log;
const PG = process.env.NETWORK_TEST_STORE === 'pg';
const PG = process.env.NETWORK_TEST_STORE === 'pg';
const USERS = [[10, 'owner'], [11, 'dev'], [12, 'leaver'], [13, 'stayer']];
const USERS = [[10, 'owner'], [11, 'dev'], [12, 'leaver'], [13, 'stayer']];
await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES ${USERS.map(([id, n]) => `(${id}, '${n}', 'x', 'user')`).join(', ')}`).run();
await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES ${USERS.map(([id, n]) => `(${id}, '${n}', 'x', 'user')`).join(', ')}`).run();
-await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]')").run();
+// The seed creates Actor's client (server/db/database.js); this file sets the fields it relies on.
+await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]') ON CONFLICT (client_id) DO UPDATE SET client_secret = EXCLUDED.client_secret, name = EXCLUDED.name, redirect_uris = EXCLUDED.redirect_uris, is_first_party = 0").run();
const S = {};
const S = {};
for (const [id, name] of USERS) S[name] = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = ?').get(id));
for (const [id, name] of USERS) S[name] = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = ?').get(id));
@@ -21,7 +21,8 @@ console.log = log;
@@ -21,7 +21,8 @@ console.log = log;
await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES
await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES
(10, 'owner', 'x', 'user'), (11, 'dev', 'x', 'user'), (12, 'viewer', 'x', 'user'), (13, 'stranger', 'x', 'user'),
(10, 'owner', 'x', 'user'), (11, 'dev', 'x', 'user'), (12, 'viewer', 'x', 'user'), (13, 'stranger', 'x', 'user'),
(14, 'staff', 'x', 'admin'), (15, 'admin', 'x', 'user'), (16, 'leaver', 'x', 'user'), (17, 'erased', 'x', 'user')`).run();
(14, 'staff', 'x', 'admin'), (15, 'admin', 'x', 'user'), (16, 'leaver', 'x', 'user'), (17, 'erased', 'x', 'user')`).run();
-await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]')").run();
+// The seed creates Actor's client (server/db/database.js); this file sets the fields it relies on.
+await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]') ON CONFLICT (client_id) DO UPDATE SET client_secret = EXCLUDED.client_secret, name = EXCLUDED.name, redirect_uris = EXCLUDED.redirect_uris, is_first_party = 0").run();
const S = {};
const S = {};
for (const [id, name] of [[10, 'owner'], [11, 'dev'], [12, 'viewer'], [13, 'stranger'], [14, 'staff'], [15, 'admin'], [16, 'leaver'], [17, 'erased']]) {
for (const [id, name] of [[10, 'owner'], [11, 'dev'], [12, 'viewer'], [13, 'stranger'], [14, 'staff'], [15, 'admin'], [16, 'leaver'], [17, 'erased']]) {
S[name] = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = ?').get(id));
S[name] = await subjects.ensureUserSubject(db, await db.prepare('SELECT * FROM users WHERE id = ?').get(id));
@@ -26,7 +26,8 @@ console.log = log;
@@ -26,7 +26,8 @@ console.log = log;
const PEOPLE = [[10, 'owner'], [11, 'admin'], [12, 'dev'], [13, 'stranger'], [14, 'staff'], [17, 'erased']];
const PEOPLE = [[10, 'owner'], [11, 'admin'], [12, 'dev'], [13, 'stranger'], [14, 'staff'], [17, 'erased']];
await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES
await db.prepare(`INSERT INTO users (id, username, password_hash, role) VALUES
(10, 'owner', 'x', 'user'), (11, 'admin', 'x', 'user'), (12, 'dev', 'x', 'user'), (13, 'stranger', 'x', 'user'), (14, 'staff', 'x', 'admin'), (17, 'erased', 'x', 'user')`).run();
(10, 'owner', 'x', 'user'), (11, 'admin', 'x', 'user'), (12, 'dev', 'x', 'user'), (13, 'stranger', 'x', 'user'), (14, 'staff', 'x', 'admin'), (17, 'erased', 'x', 'user')`).run();
-await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]', 1)").run();
+// The seed creates Actor's client (server/db/database.js); this file sets the fields it relies on.
+await db.prepare("INSERT INTO oauth_clients (client_id, client_secret, name, redirect_uris, is_first_party) VALUES ('actor', 'x', 'OpenVibe.Actor', '[]', 1) ON CONFLICT (client_id) DO UPDATE SET client_secret = EXCLUDED.client_secret, name = EXCLUDED.name, redirect_uris = EXCLUDED.redirect_uris, is_first_party = EXCLUDED.is_first_party").run();
// No service holds network.confirmation.manage by default (no receiver ships yet): Media and Tools get it here, Live does not.
// No service holds network.confirmation.manage by default (no receiver ships yet): Media and Tools get it here, Live does not.
for (const c of ['media', 'tools', 'live']) await db.prepare(`UPDATE oauth_clients SET client_secret = '${c}-secret' WHERE client_id = ?`).run(c);
for (const c of ['media', 'tools', 'live']) await db.prepare(`UPDATE oauth_clients SET client_secret = '${c}-secret' WHERE client_id = ?`).run(c);
for (const c of ['media', 'tools']) await db.prepare("INSERT INTO principal_grants (client_id, capability, audience, namespaces, granted_by) VALUES (?, 'network.confirmation.manage', 'openvibe.network', '[]', 'test')").run(c);
for (const c of ['media', 'tools']) await db.prepare("INSERT INTO principal_grants (client_id, capability, audience, namespaces, granted_by) VALUES (?, 'network.confirmation.manage', 'openvibe.network', '[]', 'test')").run(c);
@@ -14,7 +14,7 @@ assert.equal(siteForHost('yt.openvibe.tools').id, 'tools');
@@ -14,7 +14,7 @@ assert.equal(siteForHost('yt.openvibe.tools').id, 'tools');
assert.equal(siteForHost('evil.example'), null);
assert.equal(siteForHost('evil.example'), null);
assert.strictEqual(svc.ranking().at, null, 'cold start: no use counted yet (the registry\'s featured list says so)');
assert.strictEqual(svc.ranking().at, null, 'cold start: no use counted yet (the registry\'s featured list says so)');
let p = svc.payloadFor('openvibe.media');
let p = svc.payloadFor('openvibe.media');
-assert.deepEqual(p.nav.map(n => n.id), ['live', 'tools', 'space', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'host', 'bot', 'codes'], 'cold start follows the base order');
+assert.deepEqual(p.nav.map(n => n.id), ['live', 'tools', 'space', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'host', 'bot', 'codes', 'actor'], 'cold start follows the base order');
assert.strictEqual(p.nav.find(n => n.id === 'space').url, 'https://openvibe.space/', 'the forum goes to Space');
assert.strictEqual(p.nav.find(n => n.id === 'space').url, 'https://openvibe.space/', 'the forum goes to Space');
assert.strictEqual(p.nav.find(n => n.id === 'community').url, 'https://openvibe.community/', 'Community remains for pastes and Pulse');
assert.strictEqual(p.nav.find(n => n.id === 'community').url, 'https://openvibe.community/', 'Community remains for pastes and Pulse');
assert.equal(p.footer.legal.dmca, 'https://openvibe.media/dmca', 'legal links stay on the site\'s own domain');
assert.equal(p.footer.legal.dmca, 'https://openvibe.media/dmca', 'legal links stay on the site\'s own domain');
@@ -22,6 +22,7 @@ const PINNED = {
@@ -22,6 +22,7 @@ const PINNED = {
ai: 'live', // the public home and developer-app API at ai.openvibe.services since 2026-10-07 (contracts 0.110.0)
ai: 'live', // the public home and developer-app API at ai.openvibe.services since 2026-10-07 (contracts 0.110.0)
// openvibe-contracts v0.86-v0.90: Bot runs (its own exposure row); Zone is a charter only.
// openvibe-contracts v0.86-v0.90: Bot runs (its own exposure row); Zone is a charter only.
bot: 'live', zone: 'placeholder',
bot: 'live', zone: 'placeholder',
+ actor: 'live', // OpenVibe's own agent and the router for agent work at openvibe.actor since 2026-10-08 (contracts 0.115.0)
run: 'internal', // OpenVibe.Run runs on loopback since 2026-10-07 (contracts 0.109.0)
run: 'internal', // OpenVibe.Run runs on loopback since 2026-10-07 (contracts 0.109.0)
services: 'live', // OpenVibe.Services: the developer platform at openvibe.services since 2026-10-08 (contracts 0.113.0; the portal moved from Codes)
services: 'live', // OpenVibe.Services: the developer platform at openvibe.services since 2026-10-08 (contracts 0.113.0; the portal moved from Codes)
// openvibe-contracts v0.102.0: OpenVibe.Watch is a charter only (plan T18).
// openvibe-contracts v0.102.0: OpenVibe.Watch is a charter only (plan T18).
@@ -110,7 +111,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch
@@ -110,7 +111,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch
assert.strictEqual(sBy.shared.label, `not running (library, released v${require('openvibe-shared/package.json').version})`);
assert.strictEqual(sBy.shared.label, `not running (library, released v${require('openvibe-shared/package.json').version})`);
// Contracts 0.83.0 added two placeholder services (media-hub, space), v0.86-v0.90 Bot (live) and two more (zone, run),
// Contracts 0.83.0 added two placeholder services (media-hub, space), v0.86-v0.90 Bot (live) and two more (zone, run),
// v0.95.0 Services; Space and AI went live in v0.110.0; none is retired
// v0.95.0 Services; Space and AI went live in v0.110.0; none is retired
- assert.deepStrictEqual(st.exposure_summary, { live: 19, internal: 11, library: 4, repository: 1, placeholder: 2, retired: 0 });
+ assert.deepStrictEqual(st.exposure_summary, { live: 20, internal: 11, library: 4, repository: 1, placeholder: 2, retired: 0 });
// The page: no row for a service whose domain serves a placeholder shows a bare "Up".
// The page: no row for a service whose domain serves a placeholder shows a bare "Up".
const html = await fetch(base + '/status').then(x => x.text());
const html = await fetch(base + '/status').then(x => x.text());
@@ -126,7 +127,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch
@@ -126,7 +127,7 @@ const ready = [200, { ready: true, status: 'ready', failed: [], degraded: [], ch
// 3. The Frame: the nav lists only sites whose domain serves the service; the rest are soon with a reason.
// 3. The Frame: the nav lists only sites whose domain serves the service; the rest are soon with a reason.
const open = SITES.filter(s => s.status === 'open').map(s => s.id);
const open = SITES.filter(s => s.status === 'open').map(s => s.id);
- assert.deepStrictEqual(open, ['live', 'tools', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'host', 'space', 'bot', 'codes']); // by site position; bot: a site since openvibe-contracts 0.106.0; host: live since 0.107.0; space: live since 0.110.0 (the forum, 2026-10-07)
+ assert.deepStrictEqual(open, ['live', 'tools', 'community', 'games', 'media', 'network', 'chat', 'services', 'blog', 'wiki', 'host', 'space', 'bot', 'codes', 'actor']); // by site position; bot: a site since openvibe-contracts 0.106.0; host: live since 0.107.0; space: live since 0.110.0 (the forum, 2026-10-07)
for (const s of SITES) {
for (const s of SITES) {
assert.ok(s.service && PINNED[s.service], `${s.id} names its service`);
assert.ok(s.service && PINNED[s.service], `${s.id} names its service`);
assert.strictEqual(s.status === 'open', PINNED[s.service] === 'live', `${s.id} nav status follows exposure`);
assert.strictEqual(s.status === 'open', PINNED[s.service] === 'live', `${s.id} nav status follows exposure`);