← Back to Plugins
Tools

Codex Image Gen

jkf87 By jkf87 👁 121 views ▲ 0 votes

OpenClaw plugin for Codex CLI image generation ($imagegen)

GitHub

Configuration Example

{
  "tool": "codex_image_generate",
  "input": {
    "prompt": "A futuristic city skyline at sunset, cyberpunk style",
    "aspect_ratio": "landscape",
    "quality": "high",
    "background": "opaque"
  }
}

README

# openclaw-codex-image-gen

OpenClaw plugin for generating images via [OpenAI Codex CLI](https://github.com/openai/codex) Responses API (`image_generation` tool).

Inspired by [hermes-gpt-image-gen](https://github.com/Jinbro98/hermes-gpt-image-gen) and [codex-image-generation-skill](https://github.com/Gyu-bot/codex-image-generation-skill).

## Examples

| Prompt | Result |
|--------|--------|
| "A cute robot cat sitting on a cloud, digital art, vibrant colors" | <img src="examples/robot_cat.png" width="300"> |
| Korean e-commerce poster (see below) | <img src="examples/summer_collection.png" width="200"> |

<details>
<summary>Full Korean prompt for the poster</summary>

```
์‹ ์ƒํ’ˆ ์ปฌ๋ ‰์…˜ ๋Ÿฐ์นญ ํฌ์Šคํ„ฐ, 3:4 ์„ธ๋กœํ˜•. ์ƒ๋‹จ์— "NEW COLLECTION" ํ…์ŠคํŠธ,
์ œ๋ชฉ "2025 Summer Edition". ์ปฌ๋ ‰์…˜ ์†Œ๊ฐœ: ์—ฌ๋ฆ„ ํ•œ์ • ๋ธ”๋ Œ๋“œ ์›๋‘ - ์‹œํŠธ๋Ÿฌ์Šค ํ–ฅ,
๊น”๋”ํ•œ ์• ํ”„ํ„ฐํ…Œ์ด์ŠคํŠธ, ์•„์ด์Šค ์ถ”์ถœ ์ตœ์ ํ™”. ์ƒํ’ˆ ๋ผ์ธ์—…: ์ธ๋จธ ๋ธ”๋ Œ๋“œ ์›๋‘,
์ฝœ๋“œ๋ธŒ๋ฃจ ์›์•ก, ๋ ˆ๋ชฌ ์‹œ๋Ÿฝ ์‹ ์ œํ’ˆ. ํ•˜๋‹จ์— "7์›” 15์ผ ์ถœ์‹œ | ์–ผ๋ฆฌ๋ฒ„๋“œ 20% ํ• ์ธ".
ํ”„๋ฆฌ๋ฏธ์—„ e-์ปค๋จธ์Šค ๋””์ž์ธ, ์—ฌ๋ฆ„ ์ปฌ๋Ÿฌ ํŒ”๋ ˆํŠธ.
```
</details>

## How It Works

```
User prompt --> JSON payload --> codex responses (stdin) --> JSONL stream --> base64 decode --> PNG file
```

Calls `codex responses` with a Responses API payload that **forces** the `image_generation` tool via `tool_choice`. The streamed JSONL response is parsed to extract the base64-encoded PNG. Uses your Codex CLI auth session (ChatGPT/OAuth), so image generation uses subscription credits rather than API billing.

## Prerequisites

1. [OpenAI Codex CLI](https://github.com/openai/codex) installed
2. Logged in: `codex login` (ChatGPT/OAuth recommended)
3. Verify: `codex login status` should show "Logged in"

## Installation

```bash
git clone https://github.com/jkf87/openclaw-codex-image-gen.git
cp -r openclaw-codex-image-gen ~/.openclaw/workspace-<your-bot>/local-plugins/codex-image-gen
```

Or download directly:

```bash
mkdir -p ~/.openclaw/workspace-<your-bot>/local-plugins/codex-image-gen
cd ~/.openclaw/workspace-<your-bot>/local-plugins/codex-image-gen
curl -sLO https://raw.githubusercontent.com/jkf87/openclaw-codex-image-gen/main/index.ts
curl -sLO https://raw.githubusercontent.com/jkf87/openclaw-codex-image-gen/main/openclaw.plugin.json
curl -sLO https://raw.githubusercontent.com/jkf87/openclaw-codex-image-gen/main/package.json
```

## Usage in OpenClaw

Once installed, the plugin registers the `codex_image_generate` tool automatically.

### Natural Language (Korean auto-routing)

Just ask naturally -- the `pre_llm_call` hook detects image requests:

```
"๊ณ ์–‘์ด ์ผ๋Ÿฌ์ŠคํŠธ ๊ทธ๋ ค์ค˜"  -->  auto-detect  -->  codex_image_generate  -->  PNG saved
```

### Direct Tool Call

```json
{
  "tool": "codex_image_generate",
  "input": {
    "prompt": "A futuristic city skyline at sunset, cyberpunk style",
    "aspect_ratio": "landscape",
    "quality": "high",
    "background": "opaque"
  }
}
```

### Tool Parameters

| Parameter | Required | Default | Description |
|-----------|----------|---------|-------------|
| `prompt` | Yes | -- | Creative description of the image |
| `aspect_ratio` | No | `square` | `landscape` (1536x1024) / `square` (1024x1024) / `portrait` (1024x1536) |
| `file_name` | No | auto | Output filename (sanitized, .png) |
| `output_dir` | No | temp | Directory to save the image |
| `background` | No | `auto` | `auto` / `transparent` / `opaque` |
| `quality` | No | `high` | `auto` / `low` / `medium` / `high` |
| `timeout_seconds` | No | `120` | Max wait time in seconds |

### Tool Response

```json
{
  "image_path": "/tmp/openclaw-codex-imagegen-abc123/robot_cat.png",
  "file_name": "robot_cat.png",
  "output_dir": "/tmp/openclaw-codex-imagegen-abc123",
  "size_bytes": 1465634,
  "assistant_hint": "Image generated at: /tmp/..."
}
```

### Standalone Test (without OpenClaw)

```bash
node -e "
const { spawn } = require('child_process');
const fs = require('fs');
const payload = JSON.stringify({
  model: 'gpt-5.4',
  instructions: 'Use the image_generation tool to create the requested image.',
  input: [{ role: 'user', content: [{ type: 'input_text', text: 'A cute robot cat on a cloud' }] }],
  tools: [{ type: 'image_generation', size: '1024x1024', quality: 'high', background: 'auto', action: 'generate' }],
  tool_choice: { type: 'image_generation' },
  store: false, stream: true,
});
const proc = spawn('codex', ['responses'], { stdio: ['pipe', 'pipe', 'pipe'] });
let out = '';
proc.stdout.on('data', d => out += d);
proc.on('close', () => {
  for (const line of out.split('\n')) {
    try {
      const ev = JSON.parse(line);
      if (ev.type === 'response.output_item.done' && ev.item?.type === 'image_generation_call') {
        fs.writeFileSync('output.png', Buffer.from(ev.item.result, 'base64'));
        console.log('Saved output.png');
      }
    } catch {}
  }
});
proc.stdin.write(payload);
proc.stdin.end();
"
```

## Configuration

| Key | Type | Default | Description |
|-----|------|---------|-------------|
| `outputDir` | string | `""` (temp) | Default output directory |
| `timeoutSeconds` | number | `120` | Max wait for codex responses |
| `tempDirMaxAgeHours` | number | `24` | Auto-cleanup threshold for temp dirs |
| `cleanupIntervalMinutes` | number | `60` | Cleanup check interval |

## Korean Trigger Routing

The `pre_llm_call` hook auto-detects image generation requests:

| Category | Keywords |
|----------|----------|
| Engine | codex, ์ฝ”๋ฑ์Šค, gpt, openai |
| Nouns | ์ด๋ฏธ์ง€, ๊ทธ๋ฆผ, ์‚ฌ์ง„, ์•„์ด์ฝ˜, ์ผ๋Ÿฌ์ŠคํŠธ, ๋ฐฐ๊ฒฝ, ๋กœ๊ณ , image, picture, icon |
| Verbs | ์ƒ์„ฑ, ๋งŒ๋“ค, ๊ทธ๋ ค, ๊ทธ๋ฆฌ, ์ œ์ž‘, generate, create, draw, make |

Triggers when: (engine + noun) OR (noun + verb) detected.

## License

MIT

---

# openclaw-codex-image-gen (ํ•œ๊ตญ์–ด)

[OpenAI Codex CLI](https://github.com/openai/codex)์˜ Responses API๋ฅผ ์ด์šฉํ•˜์—ฌ ์ด๋ฏธ์ง€๋ฅผ ์ƒ์„ฑํ•˜๋Š” OpenClaw ํ”Œ๋Ÿฌ๊ทธ์ธ์ž…๋‹ˆ๋‹ค.

[hermes-gpt-image-gen](https://github.com/Jinbro98/hermes-gpt-image-gen) ๋ฐ [codex-image-generation-skill](https://github.com/Gyu-bot/codex-image-generation-skill)์„ ์ฐธ๊ณ ํ•˜์—ฌ ๋งŒ๋“ค์—ˆ์Šต๋‹ˆ๋‹ค.

## ์ƒ์„ฑ ์˜ˆ์‹œ

| ํ”„๋กฌํ”„ํŠธ | ๊ฒฐ๊ณผ |
|---------|------|
| "A cute robot cat sitting on a cloud, digital art, vibrant colors" | <img src="examples/robot_cat.png" width="300"> |
| ์ปคํ”ผ ์‹ ์ƒํ’ˆ ์ปฌ๋ ‰์…˜ ํฌ์Šคํ„ฐ (์„ธ๋กœํ˜•) | <img src="examples/summer_collection.png" width="200"> |

## ์ž‘๋™ ์›๋ฆฌ

```
์‚ฌ์šฉ์ž ํ”„๋กฌํ”„ํŠธ --> JSON ํŽ˜์ด๋กœ๋“œ --> codex responses (stdin) --> JSONL ์ŠคํŠธ๋ฆผ --> base64 ๋””์ฝ”๋”ฉ --> PNG ํŒŒ์ผ
```

`codex responses` ๋ช…๋ น์— `tool_choice: { type: "image_generation" }`์„ ์ง€์ •ํ•œ JSON์„ stdin์œผ๋กœ ๋ณด๋ƒ…๋‹ˆ๋‹ค. Codex CLI์˜ ์ธ์ฆ ์„ธ์…˜(ChatGPT/OAuth)์„ ์‚ฌ์šฉํ•˜๋ฏ€๋กœ, ๊ตฌ๋… ํฌ๋ ˆ๋”ง์œผ๋กœ ์ด๋ฏธ์ง€๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค (๋ณ„๋„ API ๊ณผ๊ธˆ ์—†์Œ).

## ์‚ฌ์ „ ์š”๊ตฌ์‚ฌํ•ญ

1. [OpenAI Codex CLI](https://github.com/openai/codex) ์„ค์น˜
2. ๋กœ๊ทธ์ธ: `codex login` (ChatGPT/OAuth ๊ถŒ์žฅ)
3. ํ™•์ธ: `codex login status` -> "Logged in" ํ‘œ์‹œ

## ์„ค์น˜

```bash
git clone https://github.com/jkf87/openclaw-codex-image-gen.git
cp -r openclaw-codex-image-gen ~/.openclaw/workspace-<๋ด‡์ด๋ฆ„>/local-plugins/codex-image-gen
```

๋˜๋Š” ์ง์ ‘ ๋‹ค์šด๋กœ๋“œ:

```bash
mkdir -p ~/.openclaw/workspace-<๋ด‡์ด๋ฆ„>/local-plugins/codex-image-gen
cd ~/.openclaw/workspace-<๋ด‡์ด๋ฆ„>/local-plugins/codex-image-gen
curl -sLO https://raw.githubusercontent.com/jkf87/openclaw-codex-image-gen/main/index.ts
curl -sLO https://raw.githubusercontent.com/jkf87/openclaw-codex-image-gen/main/openclaw.plugin.json
curl -sLO https://raw.githubusercontent.com/jkf87/openclaw-codex-image-gen/main/package.json
```

## OpenClaw์—์„œ ์‚ฌ์šฉ๋ฒ•

์„ค์น˜ํ•˜๋ฉด `codex_image_generate` ๋„๊ตฌ๊ฐ€ ์ž๋™์œผ๋กœ ๋“ฑ๋ก๋ฉ๋‹ˆ๋‹ค.

### ์ž์—ฐ์–ด๋กœ ์š”์ฒญ (ํ•œ๊ตญ์–ด ์ž๋™ ๋ผ์šฐํŒ…)

๊ทธ๋ƒฅ ์ž์—ฐ์Šค๋Ÿฝ๊ฒŒ ๋งํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค:

```
"๊ณ ์–‘์ด ์ผ๋Ÿฌ์ŠคํŠธ ๊ทธ๋ ค์ค˜"           --> ์ž๋™ ๊ฐ์ง€ & ์ด๋ฏธ์ง€ ์ƒ์„ฑ
"๋กœ๊ณ  ์ด๋ฏธ์ง€ ๋งŒ๋“ค์–ด์ค˜"             --> ์ž๋™ ๊ฐ์ง€ & ์ด๋ฏธ์ง€ ์ƒ์„ฑ
"codex๋กœ ๋ฐฐ๊ฒฝ ์‚ฌ์ง„ ์ƒ์„ฑํ•ด์ค˜"       --> ์ž๋™ ๊ฐ์ง€ & ์ด๋ฏธ์ง€ ์ƒ์„ฑ
```

`pre_llm_call` ํ›…์ด ํ‚ค์›Œ๋“œ ์กฐํ•ฉ์„ ๊ฐ์ง€ํ•˜์—ฌ `codex_image_generate` ๋„๊ตฌ๋กœ ์ž๋™ ๋ผ์šฐํŒ…ํ•ฉ๋‹ˆ๋‹ค.

### ๋„๊ตฌ ํŒŒ๋ผ๋ฏธํ„ฐ

| ํŒŒ๋ผ๋ฏธํ„ฐ | ํ•„์ˆ˜ | ๊ธฐ๋ณธ๊ฐ’ | ์„ค๋ช… |
|---------|------|--------|------|
| `prompt` | O | -- | ์ด๋ฏธ์ง€ ์„ค๋ช… (ํ•œ๊ธ€/์˜์–ด ๋ชจ๋‘ ๊ฐ€๋Šฅ) |
| `aspect_ratio` | X | `square` | `landscape` (1536x1024) / `square` (1024x1024) / `portrait` (1024x1536) |
| `file_name` | X | ์ž๋™ | ์ถœ๋ ฅ ํŒŒ์ผ๋ช… (.png) |
| `output_dir` | X | ์ž„์‹œ | ์ €์žฅ ๋””๋ ‰ํ„ฐ๋ฆฌ |
| `background` | X | `auto` | `auto` / `transparent` / `opaque` |
| `quality` | X | `high` | `auto` / `low` / `medium` / `high` |
| `timeout_seconds` | X | `120` | ์ตœ๋Œ€ ๋Œ€๊ธฐ ์‹œ๊ฐ„ (์ดˆ) |

### ์‘๋‹ต ์˜ˆ์‹œ

```json
{
  "image_path": "/tmp/openclaw-codex-imagegen-abc123/summer_collection.png",
  "file_name": "summer_collection.png",
  "output_dir": "/tmp/openclaw-codex-imagegen-abc123",
  "size_bytes": 2011115,
  "assistant_hint": "Image generated at: /tmp/..."
}
```

### OpenClaw ์—†์ด ๋‹จ๋… ํ…Œ์ŠคํŠธ

```bash
node -e "
const { spawn } = require('child_process');
const fs = require('fs');
const payload = JSON.stringify({
  model: 'gpt-5.4',
  instructions: 'Use the image_generation tool to create the requested image.',
  input: [{ role: 'user', content: [{ type: 'input_text', text: '๊ท€์—ฌ์šด ๋กœ๋ด‡ ๊ณ ์–‘์ด๊ฐ€ ๊ตฌ๋ฆ„ ์œ„์— ์•‰์•„์žˆ๋Š” ๊ทธ๋ฆผ' }] }],
  tools: [{ type: 'image_generation', size: '1024x1024', quality: 'high', background: 'auto', action: 'generate' }],
  tool_choice: { type: 'image_generation' },
  store: false, stream: true,
});
const proc = spawn('codex', ['responses'], { stdio: ['pipe', 'pipe', 'pipe'] });
let out = '';
proc.stdout.on('data', d => out += d);
proc.on('close', () => {
  for (const line of out.split('\n')) {
    try {
      const ev = JSON.parse(line);
      if (ev.type === 'response.output_item.done' && ev.item?.type === 'image_generation_call') {
        fs.writeFileSync('output.png', Buffer.from(ev.item.result, 'base64'));
        console.log('Saved output.png');
      }
    } catch {}
  }
});
proc.stdin.write(payload);
proc.stdin.end();
"
```

## ์„ค์ •

| ํ‚ค | ํƒ€์ž… | ๊ธฐ๋ณธ๊ฐ’ | ์„ค๋ช… |
|----|------|--------|------|
| `outputDir` | string | `""` (์ž„์‹œ) | ๊ธฐ๋ณธ ์ถœ๋ ฅ ๋””๋ ‰ํ„ฐ๋ฆฌ |
| `timeoutSeconds` | number | `120` | codex responses ์ตœ๋Œ€ ๋Œ€๊ธฐ ์‹œ๊ฐ„ |
| `tempDirMaxAgeHours` | number | `24` | ์ž„์‹œ ๋””๋ ‰ํ„ฐ๋ฆฌ ์ž๋™ ์ •๋ฆฌ ๊ธฐ์ค€ (์‹œ๊ฐ„) |
| `cleanupIntervalMinutes` | number | `60` | ์ •๋ฆฌ ์ฒดํฌ ์ฃผ๊ธฐ (๋ถ„) |

## ํ•œ๊ตญ์–ด ํŠธ๋ฆฌ๊ฑฐ ๋ผ์šฐํŒ…

`pre_llm_call` ํ›…์ด ์•„๋ž˜ ํ‚ค์›Œ๋“œ ์กฐํ•ฉ์„ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค:

| ๋ถ„๋ฅ˜ | ํ‚ค์›Œ๋“œ |
|------|--------|
| ์—”์ง„ | codex, ์ฝ”๋ฑ์Šค, gpt, openai |
| ๋ช…์‚ฌ | ์ด๋ฏธ์ง€, ๊ทธ๋ฆผ, ์‚ฌ์ง„, ์•„์ด์ฝ˜, ์ผ๋Ÿฌ์ŠคํŠธ, ๋ฐฐ๊ฒฝ, ๋กœ๊ณ , image, picture, icon |
| ๋™์‚ฌ | ์ƒ์„ฑ, ๋งŒ๋“ค, ๊ทธ๋ ค, ๊ทธ๋ฆฌ, ์ œ์ž‘, generate, create, draw, make |

**๊ฐ์ง€ ์กฐ๊ฑด**: (์—”์ง„ + ๋ช…์‚ฌ) ๋˜๋Š” (๋ช…์‚ฌ + ๋™์‚ฌ) ์กฐํ•ฉ -> ์ž๋™ ๋ผ์šฐํŒ…

## ๋ผ์ด์„ ์Šค

MIT
tools

Comments

Sign in to leave a comment

Loading comments...