Official
opencode
OpenCode agent provider: runs opencode serve and talks to it over HTTP and server-sent events.
The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/opencode@0.2.0
Permissions in 0.2.0
Files
legacy.test.ts2.7 KB
// Scripted scenarios for the opencode conversion.
import { test } from "node:test";
import assert from "node:assert/strict";
import * as Effect from "effect/Effect";
import { mkdtemp, mkdir, writeFile } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import { liveLayer } from "convergence/effect";
import type { Api } from "convergence";
import { activate } from "./main.ts";
import { adoptLegacy as adoptLegacyEffect, legacyDir, mergeInstances } from "./legacy.ts";
import type { HostScript } from "../sdk/testing.ts";
import { diskFs, fakeApi, memoryStorage, settle } from "../sdk/testing.ts";
async function dataFolder(instances: string) {
const data = await mkdtemp(join(tmpdir(), "opencode-legacy-"));
await mkdir(join(data, "legacy"), { recursive: true });
await writeFile(join(data, "legacy", "instances.json"), instances);
return data;
}
function apiWith(data: string, storage: HostScript, fs = diskFs()) {
const api = fakeApi({ onHost: storage, fs });
api.paths = { data };
return api;
}
test("the entry without an id counts as one id: the stored one wins", () => {
assert.deepEqual(mergeInstances([{ name: "Mine" }], [{ name: "Old" }, { id: "team" }]), [
{ name: "Mine" },
{ id: "team" },
]);
assert.deepEqual(mergeInstances(null, [{ command: "~/bin/opencode" }]), [{ command: "~/bin/opencode" }]);
});
test("the Rust plugin's servers register, taken into storage once", async (t) => {
const data = await dataFolder(
JSON.stringify([{ id: "team", name: "OpenCode (team)", serverUrl: "https://oc.example:4096" }]),
);
const storage = memoryStorage({ instances: [{ id: "", name: "My OpenCode" }] });
const api = apiWith(data, storage);
t.after(activate(api));
await settle(20);
assert.deepEqual(
api.registered.map((def) => def.id),
["opencode", "opencode:team"],
);
assert.deepEqual(
(storage.values.get("instances") as { id: string }[]).map((entry) => entry.id),
["", "team"],
);
await writeFile(join(data, "legacy", "instances.json"), JSON.stringify([{ id: "late" }]));
assert.equal(await adoptLegacy(api), false, "read once");
});
test("a refused read is tried again at the next load", async () => {
const data = await dataFolder("[]");
const refused = memoryStorage();
assert.equal(await adoptLegacy(apiWith(data, refused, diskFs({ allow: () => false }))), false);
assert.equal(refused.values.has("legacy"), false);
});
const adoptLegacy = (api: Api) =>
Effect.runPromise(
adoptLegacyEffect(legacyDir(api)).pipe(
Effect.provide(
liveLayer(
api,
() => Promise.reject(new Error("unexpected handler")),
() => {},
),
),
),
);Versions
| Version | Published | Plugin API | Size | Permissions | Status |
|---|---|---|---|---|---|
| 0.2.0latest | Oct 5, 2026 | >=2 <3 | 94.8 KB | 5 permissions | Listed |
No comments yet.