Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions docs/resources/(resources)/codex.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ title: codex
description: A reference page for the codex resource
---

The codex resource installs the [Codex CLI](https://developers.openai.com/codex) — OpenAI's terminal-based coding agent — and manages its global configuration. It handles installation via the official installer script and gives you declarative control over `~/.codex/config.toml` settings and global MCP servers.
The codex resource installs the [Codex CLI](https://developers.openai.com/codex) — OpenAI's terminal-based coding agent — and manages its global configuration. It handles installation via OpenAI's official `@openai/codex` npm package and gives you declarative control over `~/.codex/config.toml` settings and global MCP servers.

## Parameters

Expand Down Expand Up @@ -79,8 +79,7 @@ The codex resource installs the [Codex CLI](https://developers.openai.com/codex)

## Notes

- Codex is installed via the official installer (`curl -fsSL https://chatgpt.com/codex/install.sh | sh`) on both macOS and Linux. The binary is placed at `~/.local/bin/codex`.
- The installer adds `~/.local/bin` to your PATH. This entry remains after destroy — remove it manually if you no longer want it.
- Codex is installed via `npm install --global @openai/codex` on both macOS and Linux, matching OpenAI's officially published npm distribution.
- The `config` parameter merges only the declared top-level keys of `~/.codex/config.toml`. Existing keys not in your Codify config (including `mcp_servers`) are left untouched.
- MCP servers are stored under `[mcp_servers.<name>]` in `~/.codex/config.toml`. Removing an MCP server from your config removes its table; other servers are untouched.
- For per-project configuration (AGENTS.md, project-scoped settings and MCP servers), see [`codex-project`](/docs/resources/codex/codex-project). For the Codex desktop app, see [`codex-app`](/docs/resources/codex/codex-app).
Expand Down
29 changes: 12 additions & 17 deletions src/resources/codex/codex.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import {
ExampleConfig,
Resource,
ResourceSettings,
SpawnStatus,
getPty,
z,
} from '@codifycli/plugin-core';
Expand Down Expand Up @@ -149,31 +150,25 @@ export class CodexResource extends Resource<CodexConfig> {
}

async refresh(): Promise<Partial<CodexConfig> | null> {
const codexBin = path.join(os.homedir(), '.local', 'bin', 'codex');
try {
await fs.access(codexBin);
} catch {
return null;
}

return {};
const $ = getPty();
const { status } = await $.spawnSafe('which codex');
return status === SpawnStatus.SUCCESS ? {} : null;
}

async create(_plan: CreatePlan<CodexConfig>): Promise<void> {
const $ = getPty();

await $.spawn(
'bash -c "curl -fsSL https://chatgpt.com/codex/install.sh | CODEX_NON_INTERACTIVE=1 sh"',
{ interactive: true },
);

// Ensure PATH is updated so subsequent lifecycle methods can call `codex`
const localBin = path.join(os.homedir(), '.local', 'bin');
process.env['PATH'] = `${localBin}:${process.env['PATH'] ?? ''}`;
// Installed via npm (OpenAI's official @openai/codex package) rather than the
// curl | sh installer: the installer's version resolution hits api.github.com
// unauthenticated, which shared CI runner IPs frequently exhaust.
await $.spawn('npm install --global @openai/codex', { interactive: true });
}

async destroy(_plan: DestroyPlan<CodexConfig>): Promise<void> {
// Native uninstall: remove the binary and standalone release artifacts
const $ = getPty();
await $.spawnSafe('npm uninstall --global @openai/codex', { interactive: true });

// Best-effort cleanup of artifacts left by the legacy curl | sh installer
await fs.rm(path.join(os.homedir(), '.local', 'bin', 'codex'), { force: true });
await fs.rm(path.join(os.homedir(), '.codex', 'packages', 'standalone'), { recursive: true, force: true });
}
Expand Down
6 changes: 3 additions & 3 deletions test/codex/codex.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,14 @@ describe('codex resource integration tests', async () => {
const pluginPath = path.resolve('./src/index.ts');

it('Can install codex', { timeout: 300_000 }, async () => {
const codexBin = path.join(os.homedir(), '.local', 'bin', 'codex');
await PluginTester.fullTest(
pluginPath,
[{ type: 'codex' }],
{
skipUninstall: true,
validateApply: async () => {
const exists = await fs.access(codexBin).then(() => true).catch(() => false);
expect(exists).toBe(true);
const { data } = await testSpawn('which codex');
expect(data.trim().length).toBeGreaterThan(0);
},
},
);
Expand Down Expand Up @@ -102,6 +101,7 @@ describe('codex resource integration tests', async () => {

afterAll(async () => {
// Best-effort cleanup in case tests left codex installed
await testSpawn('npm uninstall --global @openai/codex');
await testSpawn('rm -f ~/.local/bin/codex');
await testSpawn('rm -rf ~/.codex/packages/standalone');
}, 60_000);
Expand Down