openvibers/openvibe.codes Commits
Add the opencode harness to the registry and update its tests (#13)
Changes
@@@ -34,6 +34,7 @@ A place where a developer outside the network goes from an OpenVibe account to a| OAuth helper | `/oauth`, `/oauth/test-callback` | Explains authorization code + PKCE (S256) for apps, builds a test authorize URL, and a callback that shows `code`/`state`/`error` and **never exchanges** the code. || Webhook tools | `/tools/webhooks` | Verify a delivery as a receiver that requires signature v2 does: `X-OpenVibe-Signature-V2: t=<ts>,v2=<HMAC of "<ts>.<raw body>">` must match and `t` must be within ±300 s of now (the page shows the age and the window, and tells a stale-but-correct signature from a wrong one); `X-OpenVibe-Timestamp` must equal `t`. v2 only: the v1 header (`X-OpenVibe-Signature`) was retired on 2026-09-28 and has no field. In the browser with Web Crypto, or on the server without JavaScript with openvibe-sdk's `verifyDeliveryV2`; constant-time; secret dropped. Generate a sample delivery signed as Events signs it (`X-OpenVibe-Timestamp` and v2, `signDeliveryHeaders`) for any event type the contracts catalog lists. || Docs | `/docs/*` | Contracts (field tables, fixtures as examples, raw schema), capability catalog (grantable ones highlighted), event types, SDK reference from its `.d.ts` files, ADRs. Every page states the versions it was generated from. The service registry is read live from Network, with health as Network reports it. |++| Coding-harness registry | `/docs/harnesses`, `/api/v1/harnesses` | Lists the coding harnesses Codes can route to with their agents (`GET /api/v1/harnesses`) and picks an offer for a task (`POST /api/v1/harnesses/route`). || Playgrounds | `/projects/:project/apps/:app/playground` | Media upload (sandbox, `media.object.upload`) and Events publish (`events.app.publish`: types `app.<project_key>.*`, source `app-<app ULID>`), run **as the app** with its own token (from its secret typed for that one request, or a pasted token). Refused — with the missing grant named, and nothing requested or called — unless Network lists the grant as approved. Sandbox apps only. || Manifests | `/manifests/validate`, `/projects/:project/apps/:app/releases/new` | Validate `codes.app-manifest@1` and `mods.mod-manifest@1` with openvibe-contracts; create, publish, deprecate and revoke releases. || Policy | `/policy/*` | Proposal process and the decision record; compatibility and deprecation policy rendered from ADR-002 and ADR-016 as published; licensing read from package metadata; transparency (what Codes stores and does not); governance — code of conduct ([CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)), contribution guide ([CONTRIBUTING.md](CONTRIBUTING.md)), [contributor ladder](docs/governance/contributor-ladder.md) and [moderation policy](docs/governance/moderation.md) — rendered from those files. All four are **drafts pending owner review**: marked at the top, shown with a notice, not indexed; deleting the draft line publishes one. |@@@ -110,6 +110,31 @@},"agents": []},++ {++ "id": "opencode",++ "name": "OpenCode",++ "provider": "opencode-go",++ "capabilities": {++ "host_access": true,++ "mcp": true,++ "long_autonomy": false,++ "resume": true++ },++ "address": {++ "kind": "cli",++ "target": "opencode"++ },++ "price": {++ "amount_usd": 0,++ "unit": "hour"++ },++ "limits": {++ "max_duration_seconds": 7200,++ "max_concurrent_runs": 2,++ "max_context_tokens": 128000++ },++ "agents": []++ },{"id": "aider","name": "Aider",@@@ -20,9 +20,10 @@ const rows = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'server', 'daassert.strictEqual(r.status, 200, r.text.slice(0, 300));const { harnesses } = r.json();assert.deepStrictEqual(harnesses.map((h) => h.id), rows.map((row) => row.id));++ assert.ok(harnesses.some((h) => h.id === 'opencode'), "GET /harnesses includes the 'opencode' harness");for (const row of rows) {const h = harnesses.find((x) => x.id === row.id);-- // Seed rows without an agent yet (command-code, aider) come back with agents: [].++ // Seed rows without an agent yet (command-code, aider, opencode) come back with agents: [].assert.deepStrictEqual(h, { ...row, agents: row.agents || [] });}});@@@ -13,7 +13,7 @@ const rows = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));(async () => {await check('every seed row matches the pinned harness and agent contracts', async () => {-- assert.strictEqual(rows.length, 5);++ assert.strictEqual(rows.length, 6);for (const { agents, ...offer } of rows) {const harnessCheck = contracts.validate('platform.harness-offer@1', offer);assert.strictEqual(harnessCheck.valid, true, JSON.stringify(harnessCheck.errors));
@@ -34,6 +34,7 @@ A place where a developer outside the network goes from an OpenVibe account to a
@@ -34,6 +34,7 @@ A place where a developer outside the network goes from an OpenVibe account to a
| OAuth helper | `/oauth`, `/oauth/test-callback` | Explains authorization code + PKCE (S256) for apps, builds a test authorize URL, and a callback that shows `code`/`state`/`error` and **never exchanges** the code. |
| OAuth helper | `/oauth`, `/oauth/test-callback` | Explains authorization code + PKCE (S256) for apps, builds a test authorize URL, and a callback that shows `code`/`state`/`error` and **never exchanges** the code. |
| Webhook tools | `/tools/webhooks` | Verify a delivery as a receiver that requires signature v2 does: `X-OpenVibe-Signature-V2: t=<ts>,v2=<HMAC of "<ts>.<raw body>">` must match and `t` must be within ±300 s of now (the page shows the age and the window, and tells a stale-but-correct signature from a wrong one); `X-OpenVibe-Timestamp` must equal `t`. v2 only: the v1 header (`X-OpenVibe-Signature`) was retired on 2026-09-28 and has no field. In the browser with Web Crypto, or on the server without JavaScript with openvibe-sdk's `verifyDeliveryV2`; constant-time; secret dropped. Generate a sample delivery signed as Events signs it (`X-OpenVibe-Timestamp` and v2, `signDeliveryHeaders`) for any event type the contracts catalog lists. |
| Webhook tools | `/tools/webhooks` | Verify a delivery as a receiver that requires signature v2 does: `X-OpenVibe-Signature-V2: t=<ts>,v2=<HMAC of "<ts>.<raw body>">` must match and `t` must be within ±300 s of now (the page shows the age and the window, and tells a stale-but-correct signature from a wrong one); `X-OpenVibe-Timestamp` must equal `t`. v2 only: the v1 header (`X-OpenVibe-Signature`) was retired on 2026-09-28 and has no field. In the browser with Web Crypto, or on the server without JavaScript with openvibe-sdk's `verifyDeliveryV2`; constant-time; secret dropped. Generate a sample delivery signed as Events signs it (`X-OpenVibe-Timestamp` and v2, `signDeliveryHeaders`) for any event type the contracts catalog lists. |
| Docs | `/docs/*` | Contracts (field tables, fixtures as examples, raw schema), capability catalog (grantable ones highlighted), event types, SDK reference from its `.d.ts` files, ADRs. Every page states the versions it was generated from. The service registry is read live from Network, with health as Network reports it. |
| Docs | `/docs/*` | Contracts (field tables, fixtures as examples, raw schema), capability catalog (grantable ones highlighted), event types, SDK reference from its `.d.ts` files, ADRs. Every page states the versions it was generated from. The service registry is read live from Network, with health as Network reports it. |
+| Coding-harness registry | `/docs/harnesses`, `/api/v1/harnesses` | Lists the coding harnesses Codes can route to with their agents (`GET /api/v1/harnesses`) and picks an offer for a task (`POST /api/v1/harnesses/route`). |
| Playgrounds | `/projects/:project/apps/:app/playground` | Media upload (sandbox, `media.object.upload`) and Events publish (`events.app.publish`: types `app.<project_key>.*`, source `app-<app ULID>`), run **as the app** with its own token (from its secret typed for that one request, or a pasted token). Refused — with the missing grant named, and nothing requested or called — unless Network lists the grant as approved. Sandbox apps only. |
| Playgrounds | `/projects/:project/apps/:app/playground` | Media upload (sandbox, `media.object.upload`) and Events publish (`events.app.publish`: types `app.<project_key>.*`, source `app-<app ULID>`), run **as the app** with its own token (from its secret typed for that one request, or a pasted token). Refused — with the missing grant named, and nothing requested or called — unless Network lists the grant as approved. Sandbox apps only. |
| Manifests | `/manifests/validate`, `/projects/:project/apps/:app/releases/new` | Validate `codes.app-manifest@1` and `mods.mod-manifest@1` with openvibe-contracts; create, publish, deprecate and revoke releases. |
| Manifests | `/manifests/validate`, `/projects/:project/apps/:app/releases/new` | Validate `codes.app-manifest@1` and `mods.mod-manifest@1` with openvibe-contracts; create, publish, deprecate and revoke releases. |
| Policy | `/policy/*` | Proposal process and the decision record; compatibility and deprecation policy rendered from ADR-002 and ADR-016 as published; licensing read from package metadata; transparency (what Codes stores and does not); governance — code of conduct ([CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)), contribution guide ([CONTRIBUTING.md](CONTRIBUTING.md)), [contributor ladder](docs/governance/contributor-ladder.md) and [moderation policy](docs/governance/moderation.md) — rendered from those files. All four are **drafts pending owner review**: marked at the top, shown with a notice, not indexed; deleting the draft line publishes one. |
| Policy | `/policy/*` | Proposal process and the decision record; compatibility and deprecation policy rendered from ADR-002 and ADR-016 as published; licensing read from package metadata; transparency (what Codes stores and does not); governance — code of conduct ([CODE_OF_CONDUCT.md](CODE_OF_CONDUCT.md)), contribution guide ([CONTRIBUTING.md](CONTRIBUTING.md)), [contributor ladder](docs/governance/contributor-ladder.md) and [moderation policy](docs/governance/moderation.md) — rendered from those files. All four are **drafts pending owner review**: marked at the top, shown with a notice, not indexed; deleting the draft line publishes one. |
@@ -110,6 +110,31 @@
@@ -110,6 +110,31 @@
},
},
"agents": []
"agents": []
},
},
+ {
+ "id": "opencode",
+ "name": "OpenCode",
+ "provider": "opencode-go",
+ "capabilities": {
+ "host_access": true,
+ "mcp": true,
+ "long_autonomy": false,
+ "resume": true
+ },
+ "address": {
+ "kind": "cli",
+ "target": "opencode"
+ },
+ "price": {
+ "amount_usd": 0,
+ "unit": "hour"
+ },
+ "limits": {
+ "max_duration_seconds": 7200,
+ "max_concurrent_runs": 2,
+ "max_context_tokens": 128000
+ },
+ "agents": []
+ },
{
{
"id": "aider",
"id": "aider",
"name": "Aider",
"name": "Aider",
@@ -20,9 +20,10 @@ const rows = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'server', 'da
@@ -20,9 +20,10 @@ const rows = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'server', 'da
assert.strictEqual(r.status, 200, r.text.slice(0, 300));
assert.strictEqual(r.status, 200, r.text.slice(0, 300));
const { harnesses } = r.json();
const { harnesses } = r.json();
assert.deepStrictEqual(harnesses.map((h) => h.id), rows.map((row) => row.id));
assert.deepStrictEqual(harnesses.map((h) => h.id), rows.map((row) => row.id));
+ assert.ok(harnesses.some((h) => h.id === 'opencode'), "GET /harnesses includes the 'opencode' harness");
for (const row of rows) {
for (const row of rows) {
const h = harnesses.find((x) => x.id === row.id);
const h = harnesses.find((x) => x.id === row.id);
- // Seed rows without an agent yet (command-code, aider) come back with agents: [].
+ // Seed rows without an agent yet (command-code, aider, opencode) come back with agents: [].
assert.deepStrictEqual(h, { ...row, agents: row.agents || [] });
assert.deepStrictEqual(h, { ...row, agents: row.agents || [] });
}
}
});
});
@@ -13,7 +13,7 @@ const rows = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
@@ -13,7 +13,7 @@ const rows = JSON.parse(fs.readFileSync(catalogPath, 'utf8'));
(async () => {
(async () => {
await check('every seed row matches the pinned harness and agent contracts', async () => {
await check('every seed row matches the pinned harness and agent contracts', async () => {
- assert.strictEqual(rows.length, 5);
+ assert.strictEqual(rows.length, 6);
for (const { agents, ...offer } of rows) {
for (const { agents, ...offer } of rows) {
const harnessCheck = contracts.validate('platform.harness-offer@1', offer);
const harnessCheck = contracts.validate('platform.harness-offer@1', offer);
assert.strictEqual(harnessCheck.valid, true, JSON.stringify(harnessCheck.errors));
assert.strictEqual(harnessCheck.valid, true, JSON.stringify(harnessCheck.errors));