openvibers/openvibe.codes Commits
One 'make' mode: new or existing, worked out from the words (#448)
Changes
@@@ -44,11 +44,31 @@ const USER = /^usr_[0-9A-HJKMNP-TV-Z]{26}$/;const TASK = /^tsk_[0-9A-HJKMNP-TV-Z]{26}$/;const FIL = /^fil_[0-9A-HJKMNP-TV-Z]{26}$/;// The Dock's Code tab shape (the home composer's three ways, as JSON): POST /tasks dispatches on it.--const DOCK_MODES = ['create', 'openvibe', 'repo'];++const DOCK_MODES = ['create', 'openvibe', 'repo', 'make'];++// A Git address in the words: a URL on a Git host, or git@host:owner/name. A bare owner/name counts only when it is one++// of the person's own recent repositories (prose like "and/or" is not a repository).++const GIT_URL = /(?:https?:\/\/(?:www\.)?(?:github\.com|gitlab\.com|codeberg\.org|bitbucket\.org|git\.openvibe\.codes)\/[\w.-]+\/[\w.-]+|git@[\w.-]+:[\w.-]+\/[\w.-]+)/i;function createTasksApi({ config, store, keys, run, harnesses, actorLimits, tasks = null, modelPlans = null, fleet = null, sharing = null, conductor = null, attachments = null, races = null, log = console }) {const r = asyncRouter();const db = store.db;++ /** The repository the words name: a Git address in them, or one of the person's recent repositories by its name. */++ async function repoFromWords(subject, text) {++ const hit = GIT_URL.exec(text);++ if (hit && start.normalizeRepo(hit[0])) return hit[0];++ let rows = [];++ try { rows = await db.many(`SELECT DISTINCT repo FROM run_tasks WHERE subject = $1 AND repo IS NOT NULL AND repo <> '' AND (mode IS NULL OR mode <> 'openvibe') ORDER BY repo LIMIT 50`, [subject]); } catch { rows = []; }++ const words = String(text).toLowerCase();++ for (const r of rows) {++ const full = String(r.repo || '').replace(/\.git$/, '');++ const path = full.replace(/^https?:\/\/[^/]+\//, '').toLowerCase();++ const name = path.split('/').pop();++ if (!name || name.length < 3) continue;++ const esc = (x) => x.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');++ if (new RegExp(`(^|[^\\w-])(${esc(path)}|${esc(name)})([^\\w-]|$)`).test(words)) return full;++ }++ return '';++ }const taskStore = tasks || createRunTasks({ store, run, config, log });const taskEvents = createTaskEvents({ store, config, log, attempts: taskStore.attempts });const problem = (req, res, status, code, detail) => http.sendProblem(res, status, code, { detail, ctx: req.ov });@@@ -259,10 +279,17 @@ function createTasksApi({ config, store, keys, run, harnesses, actorLimits, task}const first = (v) => (Array.isArray(v) ? v[0] : v);const text = String(first(b.text) || '').trim().slice(0, 16000);-- const mode = String(first(b.mode) || '').trim();-- if (!DOCK_MODES.includes(mode)) return problem(req, res, 422, 'codes.task_invalid', 'mode is create, openvibe or repo');++ let mode = String(first(b.mode) || '').trim();++ if (!DOCK_MODES.includes(mode)) return problem(req, res, 422, 'codes.task_invalid', 'mode is make, openvibe, create or repo');if (!text) return problem(req, res, 422, 'codes.task_invalid', 'write what you want made or changed first');-- const repoIn = String(first(b.repo) || '').trim().slice(0, 500);++ let repoIn = String(first(b.repo) || '').trim().slice(0, 500);++ // Make or change (2026-11-13, the owner: "combine … create and my code … infer that information based on the++ // prompt"): a repository named in the words, or one of yours mentioned by name, means work on it; otherwise it++ // is something new.++ if (mode === 'make') {++ if (!repoIn) repoIn = await repoFromWords(p.subject, text);++ mode = repoIn ? 'repo' : 'create';++ }const named = [...(Array.isArray(b.attachments) ? b.attachments : b.attachments ? [b.attachments] : []),...(Array.isArray(b.attachment_ids) ? b.attachment_ids : b.attachment_ids ? [b.attachment_ids] : []),@@@ -104,6 +104,26 @@ function assertSummary(s) {assert.ok(String(r.json().url).startsWith('https://openvibe.space'), r.json().url);});++ await check('make works out new or existing from the words (2026-11-13: Create and My code are one)', async () => {++ // Nothing named and nothing of yours mentioned: something new, handed over like create.++ let r = await api('POST', '/tasks', { as: ada, body: { text: 'A bakery site with today\u2019s hours', mode: 'make' } });++ assert.strictEqual(r.status, 200, r.text);++ assert.strictEqual(r.json().id, null, 'something new: no task yet');++ assert.ok(String(r.json().url).startsWith('https://openvibe.space'), r.json().url);++ // A Git address in the words: work on that repository.++ r = await api('POST', '/tasks', { as: ada, body: { text: 'Add a test for the date parser in https://github.com/octocat/Hello-World', mode: 'make' } });++ assert.ok(r.status === 200 || r.status === 201, r.text);++ assert.match(String(r.json().id), /^tsk_/, 'a task on the named repository');++ let row = await t.ctx.store.db.maybe('SELECT mode, repo FROM run_tasks WHERE id = $1', [r.json().id]);++ assert.strictEqual(row.mode, 'repo');++ // One of yours by its name (the task above made Hello-World one of ada's repositories).++ r = await api('POST', '/tasks', { as: ada, body: { text: 'Now fix the README typo in hello-world', mode: 'make' } });++ assert.ok(r.status === 200 || r.status === 201, r.text);++ assert.match(String(r.json().id), /^tsk_/, 'a task on your repository, found by its name');++ row = await t.ctx.store.db.maybe('SELECT mode, repo FROM run_tasks WHERE id = $1', [r.json().id]);++ assert.match(String(row.repo), /octocat\/Hello-World/i);++ });++await check('openvibe mode works out the project from the words, like the site', async () => {const r = await api('POST', '/tasks', { as: ada, body: { text: 'Make Live faster on phones', mode: 'openvibe' } });assert.strictEqual(r.status, 201, r.text);
@@ -44,11 +44,31 @@ const USER = /^usr_[0-9A-HJKMNP-TV-Z]{26}$/;
@@ -44,11 +44,31 @@ const USER = /^usr_[0-9A-HJKMNP-TV-Z]{26}$/;
const TASK = /^tsk_[0-9A-HJKMNP-TV-Z]{26}$/;
const TASK = /^tsk_[0-9A-HJKMNP-TV-Z]{26}$/;
const FIL = /^fil_[0-9A-HJKMNP-TV-Z]{26}$/;
const FIL = /^fil_[0-9A-HJKMNP-TV-Z]{26}$/;
// The Dock's Code tab shape (the home composer's three ways, as JSON): POST /tasks dispatches on it.
// The Dock's Code tab shape (the home composer's three ways, as JSON): POST /tasks dispatches on it.
-const DOCK_MODES = ['create', 'openvibe', 'repo'];
+const DOCK_MODES = ['create', 'openvibe', 'repo', 'make'];
+// A Git address in the words: a URL on a Git host, or git@host:owner/name. A bare owner/name counts only when it is one
+// of the person's own recent repositories (prose like "and/or" is not a repository).
+const GIT_URL = /(?:https?:\/\/(?:www\.)?(?:github\.com|gitlab\.com|codeberg\.org|bitbucket\.org|git\.openvibe\.codes)\/[\w.-]+\/[\w.-]+|git@[\w.-]+:[\w.-]+\/[\w.-]+)/i;
function createTasksApi({ config, store, keys, run, harnesses, actorLimits, tasks = null, modelPlans = null, fleet = null, sharing = null, conductor = null, attachments = null, races = null, log = console }) {
function createTasksApi({ config, store, keys, run, harnesses, actorLimits, tasks = null, modelPlans = null, fleet = null, sharing = null, conductor = null, attachments = null, races = null, log = console }) {
const r = asyncRouter();
const r = asyncRouter();
const db = store.db;
const db = store.db;
+ /** The repository the words name: a Git address in them, or one of the person's recent repositories by its name. */
+ async function repoFromWords(subject, text) {
+ const hit = GIT_URL.exec(text);
+ if (hit && start.normalizeRepo(hit[0])) return hit[0];
+ let rows = [];
+ try { rows = await db.many(`SELECT DISTINCT repo FROM run_tasks WHERE subject = $1 AND repo IS NOT NULL AND repo <> '' AND (mode IS NULL OR mode <> 'openvibe') ORDER BY repo LIMIT 50`, [subject]); } catch { rows = []; }
+ const words = String(text).toLowerCase();
+ for (const r of rows) {
+ const full = String(r.repo || '').replace(/\.git$/, '');
+ const path = full.replace(/^https?:\/\/[^/]+\//, '').toLowerCase();
+ const name = path.split('/').pop();
+ if (!name || name.length < 3) continue;
+ const esc = (x) => x.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
+ if (new RegExp(`(^|[^\\w-])(${esc(path)}|${esc(name)})([^\\w-]|$)`).test(words)) return full;
+ }
+ return '';
+ }
const taskStore = tasks || createRunTasks({ store, run, config, log });
const taskStore = tasks || createRunTasks({ store, run, config, log });
const taskEvents = createTaskEvents({ store, config, log, attempts: taskStore.attempts });
const taskEvents = createTaskEvents({ store, config, log, attempts: taskStore.attempts });
const problem = (req, res, status, code, detail) => http.sendProblem(res, status, code, { detail, ctx: req.ov });
const problem = (req, res, status, code, detail) => http.sendProblem(res, status, code, { detail, ctx: req.ov });
@@ -259,10 +279,17 @@ function createTasksApi({ config, store, keys, run, harnesses, actorLimits, task
@@ -259,10 +279,17 @@ function createTasksApi({ config, store, keys, run, harnesses, actorLimits, task
}
}
const first = (v) => (Array.isArray(v) ? v[0] : v);
const first = (v) => (Array.isArray(v) ? v[0] : v);
const text = String(first(b.text) || '').trim().slice(0, 16000);
const text = String(first(b.text) || '').trim().slice(0, 16000);
- const mode = String(first(b.mode) || '').trim();
- if (!DOCK_MODES.includes(mode)) return problem(req, res, 422, 'codes.task_invalid', 'mode is create, openvibe or repo');
+ let mode = String(first(b.mode) || '').trim();
+ if (!DOCK_MODES.includes(mode)) return problem(req, res, 422, 'codes.task_invalid', 'mode is make, openvibe, create or repo');
if (!text) return problem(req, res, 422, 'codes.task_invalid', 'write what you want made or changed first');
if (!text) return problem(req, res, 422, 'codes.task_invalid', 'write what you want made or changed first');
- const repoIn = String(first(b.repo) || '').trim().slice(0, 500);
+ let repoIn = String(first(b.repo) || '').trim().slice(0, 500);
+ // Make or change (2026-11-13, the owner: "combine … create and my code … infer that information based on the
+ // prompt"): a repository named in the words, or one of yours mentioned by name, means work on it; otherwise it
+ // is something new.
+ if (mode === 'make') {
+ if (!repoIn) repoIn = await repoFromWords(p.subject, text);
+ mode = repoIn ? 'repo' : 'create';
+ }
const named = [
const named = [
...(Array.isArray(b.attachments) ? b.attachments : b.attachments ? [b.attachments] : []),
...(Array.isArray(b.attachments) ? b.attachments : b.attachments ? [b.attachments] : []),
...(Array.isArray(b.attachment_ids) ? b.attachment_ids : b.attachment_ids ? [b.attachment_ids] : []),
...(Array.isArray(b.attachment_ids) ? b.attachment_ids : b.attachment_ids ? [b.attachment_ids] : []),
@@ -104,6 +104,26 @@ function assertSummary(s) {
@@ -104,6 +104,26 @@ function assertSummary(s) {
assert.ok(String(r.json().url).startsWith('https://openvibe.space'), r.json().url);
assert.ok(String(r.json().url).startsWith('https://openvibe.space'), r.json().url);
});
});
+ await check('make works out new or existing from the words (2026-11-13: Create and My code are one)', async () => {
+ // Nothing named and nothing of yours mentioned: something new, handed over like create.
+ let r = await api('POST', '/tasks', { as: ada, body: { text: 'A bakery site with today\u2019s hours', mode: 'make' } });
+ assert.strictEqual(r.status, 200, r.text);
+ assert.strictEqual(r.json().id, null, 'something new: no task yet');
+ assert.ok(String(r.json().url).startsWith('https://openvibe.space'), r.json().url);
+ // A Git address in the words: work on that repository.
+ r = await api('POST', '/tasks', { as: ada, body: { text: 'Add a test for the date parser in https://github.com/octocat/Hello-World', mode: 'make' } });
+ assert.ok(r.status === 200 || r.status === 201, r.text);
+ assert.match(String(r.json().id), /^tsk_/, 'a task on the named repository');
+ let row = await t.ctx.store.db.maybe('SELECT mode, repo FROM run_tasks WHERE id = $1', [r.json().id]);
+ assert.strictEqual(row.mode, 'repo');
+ // One of yours by its name (the task above made Hello-World one of ada's repositories).
+ r = await api('POST', '/tasks', { as: ada, body: { text: 'Now fix the README typo in hello-world', mode: 'make' } });
+ assert.ok(r.status === 200 || r.status === 201, r.text);
+ assert.match(String(r.json().id), /^tsk_/, 'a task on your repository, found by its name');
+ row = await t.ctx.store.db.maybe('SELECT mode, repo FROM run_tasks WHERE id = $1', [r.json().id]);
+ assert.match(String(row.repo), /octocat\/Hello-World/i);
+ });
+
await check('openvibe mode works out the project from the words, like the site', async () => {
await check('openvibe mode works out the project from the words, like the site', async () => {
const r = await api('POST', '/tasks', { as: ada, body: { text: 'Make Live faster on phones', mode: 'openvibe' } });
const r = await api('POST', '/tasks', { as: ada, body: { text: 'Make Live faster on phones', mode: 'openvibe' } });
assert.strictEqual(r.status, 201, r.text);
assert.strictEqual(r.status, 201, r.text);