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

  • Provide agents agents.provideMediumAdds agents to the app.Provide the Codex agent and pass its tool calls to plugin tools
  • Run named programs processMediumStarts the listed programs.Run the Codex CLI, and ask or tell the installer that owns it (npm or Homebrew) about a newer versionPrograms: codexnpmbrew
  • Environment variables envMediumReads the listed environment variables.Find each account's Codex home, and pass extra app-server arguments set in CODEX_ARGSVariables: HOMECODEX_HOMECODEX_ARGS
  • Read files fs.readMediumReads files in the listed places.Read, once, the accounts the previous Codex provider keptPlaces: its own data folder

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

VersionPublishedPlugin APISizePermissionsStatus
0.2.0latestOct 5, 2026>=2 <378.1 KB4 permissionsListed

Reviews and comments

0 threads · 0 reviews

No comments yet.