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
main.ts2.9 KB
// Register one agent per Codex account. The default is visible immediately;
// the remaining accounts follow the same legacy/storage/environment sequence.
import type { PluginServices } from "convergence/effect";
import * as Queue from "effect/Queue";
import * as Effect from "effect/Effect";
import * as Scope from "effect/Scope";
import { Agents, Env, Host, Plugin, runPlugin } from "convergence/effect";
import { RpcTransport } from "../sdk/effect.ts";
import { CodexAgent } from "./agent.ts";
import { defaultAccount, parseInstances } from "./instances.ts";
import type { Instance } from "./instances.ts";
import { adoptLegacy, legacyDir } from "./legacy.ts";
const loginEnv = Effect.fn("Codex.loginEnv")(function* () {
const env = yield* Env;
const values: Record<string, string> = {};
for (const name of ["HOME", "CODEX_HOME", "CODEX_ARGS"]) {
const value = yield* env.get(name).pipe(
Effect.catchTag(["HostCallFailed", "PermissionNotGranted", "NeedsReview"], (error) =>
Effect.sync(() => {
console.warn(`codex: reading ${name} failed: ${error.message}`);
return null;
}),
),
);
if (typeof value === "string" && value) values[name] = value;
}
return values;
});
// The optional collection exposes the accounts to lifecycle tests; the entry
// point uses exactly the same program without retaining another copy of state.
export const registerAccounts = Effect.fn("Codex.registerAccounts")(function* (accounts: CodexAgent[] = []) {
const agents = yield* Agents;
const scope = yield* Scope.Scope;
const transport = yield* RpcTransport;
const plugin = yield* Plugin;
const host = yield* Host;
const env = yield* Effect.cached(loginEnv());
const register = Effect.fn("Codex.register")(function* (instance: Instance) {
const jobs = yield* Effect.acquireRelease(
Queue.make<Effect.Effect<unknown, unknown, PluginServices>>(),
Queue.shutdown,
);
const agent = new CodexAgent({ instance, scope, transport, jobs });
const handle = yield* agents.register(agent.definition(env));
agent.emit = handle.emit;
accounts.push(agent);
yield* agent.background();
});
yield* register(defaultAccount());
yield* Effect.all(
[
env,
Effect.gen(function* () {
yield* adoptLegacy(legacyDir(plugin.api));
const stored = yield* host.call("host/storage.get", { key: "instances" }).pipe(
Effect.catchTag(["HostCallFailed", "PermissionNotGranted", "NeedsReview"], (error) =>
Effect.sync(() => {
console.warn(`codex: the stored accounts could not be read: ${error.message}`);
return { value: null };
}),
),
);
for (const instance of parseInstances(stored.value).slice(1)) yield* register(instance);
}),
],
{ concurrency: "unbounded" },
);
});
export const activate = runPlugin(RpcTransport.layer, registerAccounts());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.