OpenAI Image Generator widget screenshot

Script

OpenAI Image Generator

by Brett Terpstra

Generate a fresh image with the OpenAI Image API (GPT Image) and show it in a Terminal Widget via --image.

Each run is a paid API call. Prefer long intervals (hours or days), not every few minutes.

Requirements

1. Create an OpenAI API key

  1. Sign in at platform.openai.com.
  2. Open API keys → Create new secret key.
  3. Copy the key (sk-…). Store it only in your local config file.
  4. Confirm billing is enabled and that your org can use GPT Image (gpt-image-1-mini, gpt-image-1, gpt-image-2, etc.).

2. Create openai.env

mkdir -p ~/.config/terminal-widget
chmod 700 ~/.config/terminal-widget

Create ~/.config/terminal-widget/openai.env:

# Required
export OPENAI_API_KEY='sk-your_key_here'

# Optional defaults when the script is run with no / partial arguments
# export OPENAI_IMAGE_TARGET='openai-image'
# export OPENAI_IMAGE_PROMPT='neon rain on a quiet street'  # theme text, or omit for surprise default
# export OPENAI_IMAGE_SIZE='square'     # square | landscape | portrait | 1024x1024 | …
# export OPENAI_IMAGE_MODEL='gpt-image-1-mini'  # or gpt-image-1 / gpt-image-2
# export OPENAI_IMAGE_QUALITY='low'     # low | medium | high | auto
# export OPENAI_IMAGE_PROMPT_MODE='theme'  # theme (wrap) | raw (use prompt as-is)
# export OPENAI_IMAGE_PROMPT_PREFIX='A striking wallpaper-style image of'
# export OPENAI_IMAGE_DEFAULT_PROMPT='A surprising, beautiful wallpaper-style scene…'

# Optional: Shortcut name for tap-to-refresh (recommended; run-command is sandboxed)
# export OPENAI_TAP_SHORTCUT='OpenAI Image Refresh'

# Optional: path to the CLI if it is not on PATH
# export TERMINAL_WIDGET='/opt/homebrew/bin/terminal-widget'

Lock it down:

chmod 600 ~/.config/terminal-widget/openai.env

Alternate config path: set OPENAI_ENV=/path/to/your.env before running.

3. Add a widget target

Target ID Purpose

| openai-image | Default when no CLI args / env overrides are set | Use distinct targets per theme (e.g. openai-neon, openai-forest) if you run multiple LaunchAgents.

4. Install and run

Save the script as ~/bin/openai-image.sh (or ~/Scripts/…), then:

chmod +x ~/bin/openai-image.sh
~/bin/openai-image.sh
# theme + size:
~/bin/openai-image.sh openai-neon "neon rain on a quiet street" landscape
# surprise default prompt:
~/bin/openai-image.sh openai-image - square

Add a Desktop widget and choose that target. Generated PNGs are cached under ~/.cache/terminal-widget/openai-image-<target>.png.

5. Schedule updates

Enable the launchd section on this recipe (suggested: 6 hours or longer). The auto-generated agent runs the script with no arguments, using openai.env defaults.

Customize the LaunchAgent (target + prompt)

Edit ProgramArguments after creating the plist:

<key>ProgramArguments</key>
<array>
  <string>/bin/bash</string>
  <string>/Users/YOU/bin/openai-image.sh</string>
  <string>openai-neon</string>
  <string>neon rain on a quiet street</string>
  <string>landscape</string>
</array>
Argument Meaning
1 — target Terminal Widget target name
2 — prompt Theme text, or - for the default/surprise prompt
3 — size square, landscape, portrait, or WxH

Multiple widgets

Duplicate the plist (unique Label + logs), change the three args, create matching targets, bootstrap each job. Watch your OpenAI usage — N widgets × interval = N billable images.

Tap to refresh

App Sandbox blocks run-command from running Homebrew/~/Scripts. Use a Shortcut:

  1. Shortcuts → OpenAI Image Refresh → Run Shell Script with the same command you use manually.
  2. Set OPENAI_TAP_SHORTCUT='OpenAI Image Refresh' in openai.env.
  3. Re-run the script once so the widget stores run-shortcut.

Environment variables reference

Variable Required Default Description
OPENAI_API_KEY Yes — OpenAI secret key
OPENAI_IMAGE_TARGET No openai-image Widget target when arg 1 is omitted
OPENAI_IMAGE_PROMPT No surprise default Prompt/theme when arg 2 is - / omitted
OPENAI_IMAGE_SIZE No square Size preset or WxH
OPENAI_IMAGE_MODEL No gpt-image-1-mini Image model id
OPENAI_IMAGE_QUALITY No low low / medium / high / auto
OPENAI_IMAGE_PROMPT_MODE No theme theme wraps the subject; raw sends arg/env as the full prompt
OPENAI_TAP_SHORTCUT No — Shortcut name for tap refresh
OPENAI_ENV No ~/.config/terminal-widget/openai.env Config path
TERMINAL_WIDGET No terminal-widget CLI path
WIDGET_CACHE_DIR No ~/.cache/terminal-widget Where PNGs are written

Troubleshooting

Symptom Likely cause
“OPENAI_API_KEY not set” Missing/empty openai.env
Verification / model errors Complete org API verification; try gpt-image-1-mini
Billing / quota errors Add payment method or lower quality/size/frequency
Slow updates Image gen takes several seconds; quality low is faster
Tap does nothing with run-command Expected under sandbox — use OPENAI_TAP_SHORTCUT instead

Script (openai-image.sh)

#!/usr/bin/env bash
# OpenAI GPT Image → Terminal Widget (--image).
# Config: ~/.config/terminal-widget/openai.env (see recipe).
#
# Usage:
#   openai-image.sh [TARGET] [PROMPT|-] [SIZE]
#
# SIZE: square | landscape | portrait | or WxH (e.g. 1024x1024)
# PROMPT: subject/theme, or "-" to use OPENAI_IMAGE_PROMPT / a surprise default.
#
# Duplicate a LaunchAgent and change TARGET + PROMPT for multiple widgets.
# Prefer long intervals (hours/days) — each run is a paid API call.
#
# Tap refresh: set OPENAI_TAP_SHORTCUT in openai.env (run-command is sandboxed).
set -euo pipefail

export PATH="/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin${PATH:+:$PATH}"

OPENAI_ENV="${OPENAI_ENV:-$HOME/.config/terminal-widget/openai.env}"
if [[ -f "$OPENAI_ENV" ]]; then
	# shellcheck source=/dev/null
	source "$OPENAI_ENV"
fi

if [[ -z "${TERMINAL_WIDGET:-}" ]]; then
	if [[ -x /opt/homebrew/bin/terminal-widget ]]; then
		TW=/opt/homebrew/bin/terminal-widget
	elif [[ -x /usr/local/bin/terminal-widget ]]; then
		TW=/usr/local/bin/terminal-widget
	else
		TW=terminal-widget
	fi
else
	TW="$TERMINAL_WIDGET"
fi

TARGET="${1:-${OPENAI_IMAGE_TARGET:-openai-image}}"
PROMPT_ARG="${2:-${OPENAI_IMAGE_PROMPT:--}}"
SIZE_ARG="${3:-${OPENAI_IMAGE_SIZE:-square}}"
[[ "$PROMPT_ARG" == "-" ]] && PROMPT_ARG=""

MODEL="${OPENAI_IMAGE_MODEL:-gpt-image-1-mini}"
QUALITY="${OPENAI_IMAGE_QUALITY:-low}"
API_KEY="${OPENAI_API_KEY:-}"

case "$SIZE_ARG" in
	square | s) SIZE="1024x1024" ;;
	landscape | l) SIZE="1536x1024" ;;
	portrait | p) SIZE="1024x1536" ;;
	*) SIZE="$SIZE_ARG" ;;
esac

if [[ -n "$PROMPT_ARG" ]]; then
	if [[ "${OPENAI_IMAGE_PROMPT_MODE:-theme}" == "raw" ]]; then
		PROMPT="$PROMPT_ARG"
	else
		PREFIX="${OPENAI_IMAGE_PROMPT_PREFIX:-A striking wallpaper-style image of}"
		PROMPT="${PREFIX} ${PROMPT_ARG}, vivid but clean composition, no text or watermarks, suitable as a desktop widget background."
	fi
else
	PROMPT="${OPENAI_IMAGE_DEFAULT_PROMPT:-A surprising, beautiful wallpaper-style scene with vivid color and strong composition, no text or watermarks, suitable as a desktop widget background. Subject chosen creatively.}"
fi

widget_error() {
	local msg="$1"
	echo "OpenAI Image: $msg" >&2
	if command -v "$TW" >/dev/null 2>&1; then
		"$TW" --target "$TARGET" \
			--icon exclamationmark.triangle.fill \
			--background "#422006" \
			--foreground "#fde68a" \
			--text "OpenAI: $msg" || true
	fi
}

if [[ -z "$API_KEY" ]]; then
	widget_error "OPENAI_API_KEY not set"
	exit 1
fi

for cmd in jq curl base64; do
	if ! command -v "$cmd" >/dev/null 2>&1; then
		widget_error "$cmd required"
		exit 1
	fi
done

if ! command -v "$TW" >/dev/null 2>&1; then
	widget_error "terminal-widget not found (set TERMINAL_WIDGET)"
	exit 1
fi

CACHE_DIR="${WIDGET_CACHE_DIR:-$HOME/.cache/terminal-widget}"
mkdir -p "$CACHE_DIR"
OUT_PNG="${CACHE_DIR}/openai-image-${TARGET}.png"
BODY=$(jq -n \
	--arg model "$MODEL" \
	--arg prompt "$PROMPT" \
	--arg size "$SIZE" \
	--arg quality "$QUALITY" \
	'{model:$model, prompt:$prompt, size:$size, quality:$quality}')

HTTP_CODE=0
RESP_FILE=$(mktemp)
trap 'rm -f "$RESP_FILE"' EXIT

HTTP_CODE=$(curl -sS -o "$RESP_FILE" -w '%{http_code}' \
	-X POST "https://api.openai.com/v1/images/generations" \
	-H "Authorization: Bearer ${API_KEY}" \
	-H "Content-Type: application/json" \
	-d "$BODY") || true

if [[ "$HTTP_CODE" != "200" ]]; then
	ERR=$(jq -r '.error.message // .error.code // "API request failed"' "$RESP_FILE" 2>/dev/null || echo "HTTP $HTTP_CODE")
	widget_error "$ERR"
	exit 1
fi

B64=$(jq -r '.data[0].b64_json // empty' "$RESP_FILE")
if [[ -z "$B64" || "$B64" == "null" ]]; then
	widget_error "no image in response"
	exit 1
fi

if ! printf '%s' "$B64" | base64 --decode >"$OUT_PNG"; then
	widget_error "failed to decode image"
	exit 1
fi

ACTION_ARGS=()
if [[ -n "${OPENAI_TAP_SHORTCUT:-}" ]]; then
	ACTION_ARGS=(--action-kind run-shortcut --action-value "$OPENAI_TAP_SHORTCUT")
fi

"$TW" \
	--target "$TARGET" \
	--image "$OUT_PNG" \
	--padding fill \
	"${ACTION_ARGS[@]}"

Download script

Running in the background with launchd

Suggested interval: 1 day (StartInterval = 86400 seconds).

Save the script from this recipe to ~/bin/openai-image-generator.sh, then create a Launch Agent plist:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.terminalwidget.openai-image-generator</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/bash</string>
    <string>~/bin/openai-image-generator.sh</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>StartInterval</key>
  <integer>86400</integer>
  <key>StandardOutPath</key>
  <string>/tmp/com.terminalwidget.openai-image-generator.log</string>
  <key>StandardErrorPath</key>
  <string>/tmp/com.terminalwidget.openai-image-generator.err</string>
</dict>
</plist>

From Terminal:

mkdir -p ~/bin ~/Library/LaunchAgents
# Save your script to ~/bin/openai-image-generator.sh
cat > ~/Library/LaunchAgents/com.terminalwidget.openai-image-generator.plist <<'EOF'
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
  <key>Label</key>
  <string>com.terminalwidget.openai-image-generator</string>
  <key>ProgramArguments</key>
  <array>
    <string>/bin/bash</string>
    <string>~/bin/openai-image-generator.sh</string>
  </array>
  <key>RunAtLoad</key>
  <true/>
  <key>StartInterval</key>
  <integer>86400</integer>
  <key>StandardOutPath</key>
  <string>/tmp/com.terminalwidget.openai-image-generator.log</string>
  <key>StandardErrorPath</key>
  <string>/tmp/com.terminalwidget.openai-image-generator.err</string>
</dict>
</plist>
EOF
launchctl bootstrap "gui/$(id -u)" ~/Library/LaunchAgents/com.terminalwidget.openai-image-generator.plist

For a GUI editor and troubleshooting, see LaunchControl from soma-zone.

← All recipes