Official
codex
Codex agent provider: runs the Codex CLI's app-server for each account.
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/codex@0.2.0
Permissions in 0.2.0
Files
instances.ts3.1 KB
// Codex accounts, each served as its own Convergence agent.
//
// One Codex install can hold several logins, kept apart by `CODEX_HOME`.
// The accounts are the plugin's stored value `instances` (plugin storage,
// key `instances`), a list like
//
// [{ "id": "personal", "name": "Codex Personal", "home": "~/.codex_personal", "args": [] }]
//
// Each entry becomes the agent id `codex:<id>`, beside the default
// `codex`. Instances that share a home share Codex's own thread storage,
// so a chat can move between them; instances with different homes cannot,
// which is what `continuationKey` tells the UI.
import * as z from "zod";
export interface Instance {
id: string;
name: string | null;
home: string | null;
args: string[];
}
const entrySchema = z.object({
id: z.unknown().optional(),
name: z.unknown().optional(),
home: z.unknown().optional(),
args: z.array(z.unknown()).optional(),
});
/// The default account, which uses the ambient environment.
export function defaultAccount(): Instance {
return { id: "", name: null, home: null, args: [] };
}
export function agentId(instance: Pick<Instance, "id">) {
return instance.id ? `codex:${instance.id}` : "codex";
}
export function displayName(instance: Pick<Instance, "id" | "name">) {
if (typeof instance.name === "string" && instance.name.trim()) return instance.name;
return instance.id ? `Codex (${instance.id})` : "Codex";
}
/// Where this account keeps its threads and its login: its own home,
/// else `CODEX_HOME`, else `~/.codex`. `env` holds the login environment's
/// `HOME` and `CODEX_HOME` (either may be missing). `null` when no home
/// can be named.
export function homeOf(instance: Pick<Instance, "home">, env: Record<string, string> = {}) {
const own = typeof instance.home === "string" ? instance.home.trim() : "";
if (own) return expand(own, env.HOME);
if (env.CODEX_HOME) return env.CODEX_HOME;
return env.HOME ? `${env.HOME.replace(/\/+$/, "")}/.codex` : null;
}
function expand(path: string, home?: string) {
if (path.startsWith("~/")) return home ? `${home.replace(/\/+$/, "")}/${path.slice(2)}` : path;
return path;
}
/// Every configured account, the default one first. Entries with a
/// missing or repeated id are left out (and reported), never the default.
export function parseInstances(value: unknown) {
const all = [defaultAccount()];
if (value === null || value === undefined) return all;
if (!Array.isArray(value)) {
console.warn("the stored codex instances are not a list; only the default account is served");
return all;
}
for (const raw of value) {
const parsed = entrySchema.safeParse(raw);
const entry = parsed.success ? parsed.data : {};
const id = typeof entry?.id === "string" ? entry.id.trim() : "";
if (!id || all.some((known) => known.id === id)) {
console.warn("ignoring a codex instance with a missing or repeated id");
continue;
}
all.push({
id,
name: typeof entry.name === "string" ? entry.name : null,
home: typeof entry.home === "string" ? entry.home : null,
args: Array.isArray(entry.args) ? entry.args.filter((arg) => typeof arg === "string") : [],
});
}
return all;
}Versions
| Version | Published | Plugin API | Size | Permissions | Status |
|---|---|---|---|---|---|
| 0.2.0latest | Oct 5, 2026 | >=2 <3 | 78.1 KB | 4 permissions | Listed |
No comments yet.