openvibers/openvibe.bot Commits
OpenVibe.AI lists a person's machines whose models their AI may use, with its own capability (bot.machine.ai), for a node credential with machine any (contracts 0.221.0) (#192)
Changes
@@@ -449,8 +449,11 @@ function v1Router({ domain, apiAuth, limits, hub, jobs: jobService, config, submconst subject = actedFor(req, req.query.owner);const svc = req.principal.kind === 'service' ? MACHINE_SERVICES[req.principal.sub] : null;if (req.principal.kind === 'service') {-- if (!svc) fail(403, 'bot.forbidden', 'machines are listed for their owner, their agent and their watches');-- if (!apiAuth.granted(req.principal, CAP.computer)) fail(403, 'capability.denied', `${CAP.computer} not granted`);++ if (!svc) fail(403, 'bot.forbidden', 'machines are listed for their owner, their agent, their watches and their AI');++ // Each service with its own capability: OpenVibe.AI lists those whose models the owner's AI may use++ // (a node credential with machine `any`, contracts 0.221.0).++ const cap = svc.cap || CAP.computer;++ if (!apiAuth.granted(req.principal, cap)) fail(403, 'capability.denied', `${cap} not granted`);}const rows = await domain.machines.list(subject);const mine = svc ? rows.filter((d) => d[svc.flag]) : rows;@@@ -411,9 +411,13 @@ const { boot, check, done } = require('./helpers/app');const asAi = (subject) => ({ token: ai, headers: { 'X-OV-Subject': subject } });const before = await cmd(gpu.deviceId, ask, asAi(alex.subject));assert.deepStrictEqual([before.status, before.json.code], [403, 'bot.agent_not_allowed'], 'off until the owner turns it on');++ const listAi = async () => (await t.call('GET', '/api/v1/machines', asAi(alex.subject))).json.machines.map((m) => m.id);++ assert.ok(!(await listAi()).includes(gpu.deviceId), 'OpenVibe.AI lists only machines whose models it may use');const on = await t.call('POST', `/api/v1/machines/${gpu.deviceId}/ai-models`, { user: alex, body: { on: true } });assert.strictEqual(on.json.machine.ai_models, true);assert.deepStrictEqual(on.json.machine.computer.ai.models, ['llama3.1:8b']);++ assert.deepStrictEqual(await listAi(), [gpu.deviceId], 'once on, it is listed to OpenVibe.AI (a node credential with machine any)');++ assert.strictEqual((await t.call('GET', '/api/v1/machines', { token: t.network.signService({ sub: 'svc:ai', cap: ['bot.computer.control'] }), headers: { 'X-OV-Subject': alex.subject } })).status, 403, 'with its own capability only');const viaAi = await cmd(gpu.deviceId, ask, asAi(alex.subject));assert.strictEqual(viaAi.json.result, 'ack', viaAi.text);// Nobody else: another person through OpenVibe.AI, a service without the capability, another service, another kind.
@@ -449,8 +449,11 @@ function v1Router({ domain, apiAuth, limits, hub, jobs: jobService, config, subm
@@ -449,8 +449,11 @@ function v1Router({ domain, apiAuth, limits, hub, jobs: jobService, config, subm
const subject = actedFor(req, req.query.owner);
const subject = actedFor(req, req.query.owner);
const svc = req.principal.kind === 'service' ? MACHINE_SERVICES[req.principal.sub] : null;
const svc = req.principal.kind === 'service' ? MACHINE_SERVICES[req.principal.sub] : null;
if (req.principal.kind === 'service') {
if (req.principal.kind === 'service') {
- if (!svc) fail(403, 'bot.forbidden', 'machines are listed for their owner, their agent and their watches');
- if (!apiAuth.granted(req.principal, CAP.computer)) fail(403, 'capability.denied', `${CAP.computer} not granted`);
+ if (!svc) fail(403, 'bot.forbidden', 'machines are listed for their owner, their agent, their watches and their AI');
+ // Each service with its own capability: OpenVibe.AI lists those whose models the owner's AI may use
+ // (a node credential with machine `any`, contracts 0.221.0).
+ const cap = svc.cap || CAP.computer;
+ if (!apiAuth.granted(req.principal, cap)) fail(403, 'capability.denied', `${cap} not granted`);
}
}
const rows = await domain.machines.list(subject);
const rows = await domain.machines.list(subject);
const mine = svc ? rows.filter((d) => d[svc.flag]) : rows;
const mine = svc ? rows.filter((d) => d[svc.flag]) : rows;
@@ -411,9 +411,13 @@ const { boot, check, done } = require('./helpers/app');
@@ -411,9 +411,13 @@ const { boot, check, done } = require('./helpers/app');
const asAi = (subject) => ({ token: ai, headers: { 'X-OV-Subject': subject } });
const asAi = (subject) => ({ token: ai, headers: { 'X-OV-Subject': subject } });
const before = await cmd(gpu.deviceId, ask, asAi(alex.subject));
const before = await cmd(gpu.deviceId, ask, asAi(alex.subject));
assert.deepStrictEqual([before.status, before.json.code], [403, 'bot.agent_not_allowed'], 'off until the owner turns it on');
assert.deepStrictEqual([before.status, before.json.code], [403, 'bot.agent_not_allowed'], 'off until the owner turns it on');
+ const listAi = async () => (await t.call('GET', '/api/v1/machines', asAi(alex.subject))).json.machines.map((m) => m.id);
+ assert.ok(!(await listAi()).includes(gpu.deviceId), 'OpenVibe.AI lists only machines whose models it may use');
const on = await t.call('POST', `/api/v1/machines/${gpu.deviceId}/ai-models`, { user: alex, body: { on: true } });
const on = await t.call('POST', `/api/v1/machines/${gpu.deviceId}/ai-models`, { user: alex, body: { on: true } });
assert.strictEqual(on.json.machine.ai_models, true);
assert.strictEqual(on.json.machine.ai_models, true);
assert.deepStrictEqual(on.json.machine.computer.ai.models, ['llama3.1:8b']);
assert.deepStrictEqual(on.json.machine.computer.ai.models, ['llama3.1:8b']);
+ assert.deepStrictEqual(await listAi(), [gpu.deviceId], 'once on, it is listed to OpenVibe.AI (a node credential with machine any)');
+ assert.strictEqual((await t.call('GET', '/api/v1/machines', { token: t.network.signService({ sub: 'svc:ai', cap: ['bot.computer.control'] }), headers: { 'X-OV-Subject': alex.subject } })).status, 403, 'with its own capability only');
const viaAi = await cmd(gpu.deviceId, ask, asAi(alex.subject));
const viaAi = await cmd(gpu.deviceId, ask, asAi(alex.subject));
assert.strictEqual(viaAi.json.result, 'ack', viaAi.text);
assert.strictEqual(viaAi.json.result, 'ack', viaAi.text);
// Nobody else: another person through OpenVibe.AI, a service without the capability, another service, another kind.
// Nobody else: another person through OpenVibe.AI, a service without the capability, another service, another kind.