openvibers/openvibe.bot Commits

Adding a machine is a choice, not numbered steps: "What should this computer do?", a copy button on every command (each copies its own), the code's expiry in local time (#201)

OP OpenVibers committed 17 days ago · a053bfb087 · 5 files, +25 −21

Changes

ddiff --git a/public/panel.css b/public/panel.css
iindex 71e1429..1eb858a 100644
---- a/public/panel.css
++++ b/public/panel.css
@@@ -241,6 +241,9 @@ main { padding: 1rem max(1rem, env(safe-area-inset-right)) calc(1.5rem + env(saf
.pair-status .button { margin-left: auto; }
.steps { list-style: none; padding: 0; margin: 0 0 1rem; display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.75rem; counter-reset: step; }
.steps > li { counter-increment: step; }
++/* Adding a machine: alternatives, one card each, not numbered steps. */
++.choices { list-style: none; padding: 0; margin: 0 0 1rem; display: grid; grid-template-columns: minmax(0, 1fr); gap: 0.75rem; }
++.choices h2 { text-transform: none; letter-spacing: 0; font-size: 1rem; color: var(--text); }
.steps h2 { display: flex; flex-wrap: wrap; align-items: center; gap: 0.5rem; text-transform: none; letter-spacing: 0; font-size: 1rem; color: var(--text); }
.steps h2::before { content: counter(step); display: inline-grid; place-items: center; width: 1.6rem; height: 1.6rem; border-radius: 50%; background: var(--fill-accent); color: var(--on-fill-accent); font-size: 0.85rem; }
.installer { margin: 0 0 0.75rem; overflow-x: auto; padding: 0.85rem; background: var(--bg); border: 1px solid var(--line); border-radius: 10px; font-size: 0.85rem; }
ddiff --git a/public/panel.js b/public/panel.js
iindex 5f973c6..dfdcbf9 100644
---- a/public/panel.js
++++ b/public/panel.js
@@@ -46,7 +46,8 @@
for (const b of $$('[data-copy]')) {
b.addEventListener('click', () => {
-- const src = $('[data-copy-source]');
++ // The command in the button's own card (adding a machine offers several), else the page's one.
++ const src = $('[data-copy-source]', b.closest('.card') || document) || $('[data-copy-source]');
const label = $('[data-copy-label]', b);
if (src && navigator.clipboard) {
navigator.clipboard.writeText(src.textContent).then(() => {
ddiff --git a/server/web/docs.js b/server/web/docs.js
iindex c4487e5..a5fa7d5 100644
---- a/server/web/docs.js
++++ b/server/web/docs.js
@@@ -366,7 +366,7 @@ function renderComputersPage({ config }) {
<p class="muted">The OpenVibe Node's <b>computer</b> plugin lets your agents use your own computer: its terminal, its files, its screen and (when you allow it) its mouse and keyboard. It is off unless you install it, it never runs as root, and you decide what it may do on the computer itself. Nothing on OpenVibe can raise that level.</p>
<section class="card" aria-labelledby="install-h"><h2 id="install-h">Install it</h2>
--<p>On ${inLink('/machines', 'Your machines')}, choose <b>Add a machine</b> and run the second command it shows on the computer, from your own account (the installer uses sudo itself):</p>
++<p>On ${inLink('/machines', 'Your machines')}, choose <b>Add a machine</b> and run the command under <b>Let your agents use it</b> on the computer, from your own account (the installer uses sudo itself):</p>
${code(COMPUTER_INSTALL)}
<p>The Node then runs as you, never as root, and its only folder is your home folder. Linux for now; elsewhere, run the Node in your own session with the plugin in its config (${out(`${BLOB}/docs/computer.md`, 'computer.md')}).</p></section>
@@@ -410,7 +410,7 @@ ${code(COMPUTER_APPROVE)}
<section class="card" aria-labelledby="models-c-h"><h2 id="models-c-h">Your own models</h2>
<p>If you run a model on your computer (Ollama, LM Studio or llama.cpp's server), your OpenVibe AI can think with it: free, private to you, and with no port opened.</p>
<ul>
--<li><b>Set it up.</b> On ${inLink('/machines', 'Your machines')}, choose <b>Add a machine</b> and run the command under <b>Or use the models you run on it</b>, or add <code>--ai</code> to any other one (OpenVibe Node v0.28.0 or newer). The installer says which model server it finds.</li>
++<li><b>Set it up.</b> On ${inLink('/machines', 'Your machines')}, choose <b>Add a machine</b> and run the command under <b>Use the models you run on it</b>, or add <code>--ai</code> to any other one (OpenVibe Node v0.28.0 or newer). The installer says which model server it finds.</li>
<li><b>Turn it on.</b> The machine's card lists its models and how fast they answer there. <b>Let my AI use this computer's models</b> lets OpenVibe ask them for you; nothing reaches them before that.</li>
<li><b>Use it.</b> <b>Use these models in OpenVibe.Chat</b> fills in "Your AI key" on OpenVibe.Chat. Your Vibe and the AI members you set up can then think with your computer. On OpenVibe.Live, a streamer's AI viewers can too: choose <b>A model on my own computer</b> as their AI source.</li>
<li><b>Any of your computers.</b> Choose <b>any of my computers</b> instead of one, and OpenVibe asks the fastest of yours that is on and has the model, then the next when one is busy.</li>
ddiff --git a/server/web/render.js b/server/web/render.js
iindex 0c844cb..28bf3f2 100644
---- a/server/web/render.js
++++ b/server/web/render.js
@@@ -791,22 +791,21 @@ function renderMachinesPage({ machines = [], pairing = null }) {
+ computerRow(m) + voiceRow(m)
+ `<span class="robot-actions"><form method="post" action="/machines/${esc(m.id)}/remove"><button type="submit" class="button quiet">Remove</button></form></span></li>`).join('')}</ul>`
: `<div class="empty">${icon('plug', 'icon empty-icon')}<p class="empty-title">No machines yet.</p><p>Add your computer or a server you own. It runs the jobs you start, such as a coding task on openvibe.codes, and nobody else's.</p></div>`;
-- const add = pairing
-- ? `<ol class="steps"><li class="card"><h2>Run this on the machine</h2>
--<pre tabindex="0" class="installer"><code data-copy-source>${esc(pairing.installer)}</code></pre>
++ // Adding a machine is a choice of what it does, each with its own command (one copy button each), not steps.
++ const choice = (title, flag, text) => `<li class="card"><h2>${title}</h2>
++<pre tabindex="0" class="installer"><code data-copy-source>${esc(pairing.installer.replace(/ --worker$/, flag))}</code></pre>
<button type="button" class="copy" data-copy>${icon('copy')}<span data-copy-label>Copy the command</span></button>
--<p class="muted">Linux with systemd. It installs OpenVibe Node, turns its job worker on (jobs run as a separate user, in a sandbox, with the limits you set) and pairs the machine with your account. For coding jobs the machine also needs Node.js 22 or newer and git, and a coding agent: Claude Code, Codex, OpenCode, or a DeepSeek key.</p></li>
--<li class="card"><h2>Or let your agents use this computer</h2>
--<pre tabindex="0" class="installer"><code>${esc(pairing.installer.replace(/ --worker$/, ' --computer ask'))}</code></pre>
--<p class="muted">Instead of running jobs, the machine's terminal, files and screen become usable by your agents, at the level you set on the machine: <code>ask</code> waits for <code>openvibe-node approve</code> there before anything runs or changes. The Node runs as your own account, never root. <a href="https://github.com/OpenVibers/OpenVibe.Node/blob/main/docs/computer.md">How it works</a>.</p></li>
--<li class="card"><h2>Or make a spare computer a desk host</h2>
--<pre tabindex="0" class="installer"><code>${esc(pairing.installer.replace(/ --worker$/, ' --desk-host'))}</code></pre>
--<p class="muted">An old laptop, a mini PC or a spare workstation becomes a place where your agents work: each task gets a desk of its own, with its own browser, and you can watch any desk and take it over. No screen, keyboard or sign-in needed on it. It runs as its own account and only for your agents; the installer adds what desks need and sizes them to its memory. Linux. <a href="https://github.com/OpenVibers/OpenVibe.Node/blob/main/docs/computer.md#desk-hosts---desk-host-node-v0200">How it works</a>.</p></li>
--<li class="card"><h2>Or use the models you run on it</h2>
--<pre tabindex="0" class="installer"><code>${esc(pairing.installer.replace(/ --worker$/, ' --ai'))}</code></pre>
--<p class="muted">If this computer runs Ollama, LM Studio or llama.cpp's server, your Vibe and your AI members can think with its models: free, and no port is opened. The installer says which server it finds. Add <code>--ai</code> to any command above to do both. Once it is paired, turn on "Let my AI use this computer's models" for it here. Node v0.28.0 or newer. <a href="https://github.com/OpenVibers/OpenVibe.Node/blob/main/docs/ai.md">How it works</a>.</p></li>
++<p class="muted">${text}</p></li>`;
++ const add = pairing
++ ? `<h2 id="add-h">What should this computer do?</h2>
++<p class="muted">Pick one and run its command on the computer: it installs OpenVibe Node and pairs the computer with your account. Add <code>--ai</code> to any of them to use its models too.</p>
++<ul class="choices" aria-labelledby="add-h">
++${choice('Run your coding jobs', ' --worker', 'Linux with systemd. It turns the Node\'s job worker on: jobs run as a separate user, in a sandbox, with the limits you set. For coding jobs the computer also needs Node.js 22 or newer and git, and a coding agent: Claude Code, Codex, OpenCode, or a DeepSeek key.')}
++${choice('Let your agents use it', ' --computer ask', 'The computer\'s terminal, files and screen become usable by your agents, at the level you set on the computer: <code>ask</code> waits for <code>openvibe-node approve</code> there before anything runs or changes. The Node runs as your own account, never root. <a href="https://github.com/OpenVibers/OpenVibe.Node/blob/main/docs/computer.md">How it works</a>.')}
++${choice('Make a spare computer a desk host', ' --desk-host', 'An old laptop, a mini PC or a spare workstation becomes a place where your agents work: each task gets a desk of its own, with its own browser, and you can watch any desk and take it over. No screen, keyboard or sign-in needed on it. It runs as its own account and only for your agents; the installer adds what desks need and sizes them to its memory. Linux. <a href="https://github.com/OpenVibers/OpenVibe.Node/blob/main/docs/computer.md#desk-hosts---desk-host-node-v0200">How it works</a>.')}
++${choice('Use the models you run on it', ' --ai', 'If this computer runs Ollama, LM Studio or llama.cpp\'s server, your Vibe and your AI members can think with its models: free, and no port is opened. The installer says which server it finds. Once it is paired, turn on "Let my AI use this computer\'s models" for it here. Node v0.28.0 or newer. <a href="https://github.com/OpenVibers/OpenVibe.Node/blob/main/docs/ai.md">How it works</a>.')}
<li class="card"><h2>Or enter the pairing code</h2><p><strong class="code">${esc(pairing.code)}</strong></p>
--<p class="muted">Valid until <time datetime="${esc(pairing.expires_at)}">${esc(pairing.expires_at)}</time>. It works once.</p></li></ol>
++<p class="muted">Valid until <time datetime="${esc(pairing.expires_at)}" data-local-time>${esc(String(pairing.expires_at).slice(11, 16))} UTC</time>. It works once.</p></li></ul>
<p class="muted">Reload this page once the installer says it is paired: the machine shows up above.</p>`
: `<form class="add-robot card" method="post" action="/machines"><h2>Add a machine</h2>
<p class="muted">You get a one-line install command with a one-time code.</p><button type="submit" class="primary">Add a machine</button></form>`;
ddiff --git a/test/machine-computer.test.js b/test/machine-computer.test.js
iindex 93b1339..1426916 100644
---- a/test/machine-computer.test.js
++++ b/test/machine-computer.test.js
@@@ -357,12 +357,13 @@ const { boot, check, done } = require('./helpers/app');
const add = await fetch(`${t.base}/machines`, { method: 'POST', redirect: 'manual', body: '', headers: { 'Content-Type': 'application/x-www-form-urlencoded', ...cookie(alex) } });
assert.strictEqual(add.status, 201);
const page = await add.text();
-- assert.ok(page.includes('Or make a spare computer a desk host'));
-- const hostCmd = /<code>(curl -fsSL [^<]+ --desk-host)<\/code>/.exec(page);
++ assert.ok(page.includes('What should this computer do?') && page.includes('Make a spare computer a desk host'));
++ assert.strictEqual((page.match(/data-copy-source/g) || []).length, 4, 'every command has its own copy button');
++ const hostCmd = /<code[^>]*>(curl -fsSL [^<]+ --desk-host)<\/code>/.exec(page);
assert.ok(hostCmd, 'the desk host install command');
assert.ok(!hostCmd[1].includes('--worker'), 'a desk host never runs the job worker too');
-- assert.ok(page.includes('Or use the models you run on it'), 'AI on your own machines (Node v0.28.0 --ai)');
-- const aiCmd = /<code>(curl -fsSL [^<]+ --ai)<\/code>/.exec(page);
++ assert.ok(page.includes('Use the models you run on it'), 'AI on your own machines (Node v0.28.0 --ai)');
++ const aiCmd = /<code[^>]*>(curl -fsSL [^<]+ --ai)<\/code>/.exec(page);
assert.ok(aiCmd && !aiCmd[1].includes('--worker'), 'the models install command');
host.c.close();
mac.c.close();