Skip to content

Instantly share code, notes, and snippets.

@masakielastic
Last active April 21, 2026 15:39
Show Gist options
  • Select an option

  • Save masakielastic/86603f0e4746b57eab2f672b4be5acab to your computer and use it in GitHub Desktop.

Select an option

Save masakielastic/86603f0e4746b57eab2f672b4be5acab to your computer and use it in GitHub Desktop.
Grok Imagine API を呼び出して画像を生成するコマンドツール

grokimg

grokimg は、pass で管理している xAI API キーを使って Grok Imagine API を呼び出し、生成画像とプロンプト記録を コマンド実行ディレクトリに保存するための簡易 Bash コマンドです。

実験用途を前提にした最小構成で、次のことだけに絞っています。

  • pass から api/xai を読み出す
  • Grok Imagine API を叩く
  • 生成画像をカレントディレクトリに保存する
  • 生成時の prompt を YAML frontmatter 付き Markdown として保存する

特徴

  • pass 前提なので API キーをスクリプトに直書きしない
  • Bash / curl / jq だけで動く
  • 画像と Markdown の 2 ファイルだけを保存する
  • Obsidian や Git 管理と相性がよい
  • まずは Grok Imagine 専用の簡易実験として使える

必要なもの

  • Bash
  • curl
  • jq
  • pass
  • gpg
  • gpg-agent

Debian / Ubuntu 系なら、だいたい次で入ります。

sudo apt update
sudo apt install -y curl jq pass gnupg

事前セットアップ

1. GPG 鍵を確認する

pass は GPG 鍵で内容を暗号化するので、まず秘密鍵があるか確認します。

gpg \--list-secret-keys \--keyid-format LONG

秘密鍵がなければ先に GPG 鍵を作成してください。


2. pass を初期化する

gpg --list-secret-keys で表示された鍵 ID を使って初期化します。

例:

pass init YOUR\_GPG\_KEY\_ID

たとえば sec rsa4096/ABCDEF1234567890 ... のように表示された場合は、ABCDEF1234567890 を使うことが多いです。


3. xAI API キーを pass に保存する

api/xai というパスに保存する前提です。

pass insert api/xai

入力時は 1 行目に API キー本体 を入れてください。

例:

xai-xxxxxxxxxxxxxxxxxxxxxxxx

この grokimg は次のように先頭行を読み出します。

pass show api/xai | head \-n 1

そのため、1 行目にキー本体を置くのがいちばん簡単です。


インストール

スクリプト本体を grokimg という名前で保存し、実行権限を付けます。

chmod +x grokimg

手元で試すだけなら、そのままカレントディレクトリで実行できます。

./grokimg "A collage of London landmarks in a stenciled street-art style"

よく使うなら ~/bin に置くと便利です。

mkdir \-p ~/bin  
cp grokimg ~/bin/grokimg  
chmod +x ~/bin/grokimg

~/binPATH に入っていれば、次のように呼べます。

grokimg "A collage of London landmarks in a stenciled street-art style"

使い方

基本

grokimg "A collage of London landmarks in a stenciled street-art style"

-p オプションを使う場合

grokimg \-p "A snow leopard on a Himalayan ridge at blue hour"

ヘルプ表示

grokimg \--help

保存されるファイル

コマンドを実行すると、実行したディレクトリに次の 2 ファイルが保存されます。

  • 画像ファイル
    例: grokimg-20260422-213015.jpg

  • Markdown ファイル
    例: grokimg-20260422-213015.md

画像の拡張子は API レスポンスの mime_type に応じて決まります。

  • image/jpeg.jpg

  • image/png.png

  • image/webp.webp


Markdown の内容

Markdown は YAML frontmatter 付きで保存されます。

例:

---
created_at: '2026-04-22T21:30:15+09:00'
model: 'grok-imagine-image'
mime_type: 'image/jpeg'
cost_in_usd_ticks: 200000000
tags:
  - grok-imagine
  - image-generation
---

![](./grokimg-20260422-213015.jpg)

A collage of London landmarks in a stenciled street-art style

この形式にしておくと、あとで

  • どの prompt を投げたか

  • どの画像に対応する記録か

  • 生成コストの目安

  • revised prompt が返ってきたか

を Markdown だけで把握できます。


想定している運用

このツールは、画像生成サービスを「作品保管庫」ではなく 実験ログ生成ツール として扱うための最小実装です。

そのため、次のような使い方を想定しています。

  • 画像を生成する

  • 生成画像と prompt 記録を同じ場所に置く

  • 必要なものだけ後で整理する

  • 不要なものは捨てる

JSON を大量に残すとノイズになりやすいため、この版では 画像 + Markdown のみ 保存します。


初回実行時の注意

pass は内部で GPG を使っているため、初回やキャッシュ切れ時には GPG のパスフレーズ入力 が求められることがあります。

これは gpg-agent のキャッシュが効いていないタイミングでは自然な挙動です。
毎回 API キー文字列を手入力するわけではなく、pass の復号時に必要になることがあります。


トラブルシューティング

pass: command not found

pass がインストールされていません。

sudo apt install \-y pass

jq: command not found

jq がインストールされていません。

sudo apt install \-y jq

Error: failed to read API key from pass (api/xai).

pass 側に api/xai が保存されていないか、先頭行が空の可能性があります。

確認:

pass show api/xai

GPG のパスフレーズ入力が出る

pass が秘密情報を復号するときに GPG を使っているためです。
異常ではありません。gpg-agent のキャッシュが効くと再入力回数は減ります。


画像 URL は取れたのに保存に失敗する

一時 URL の有効期限切れや通信エラーの可能性があります。
このツールは API 呼び出し直後にダウンロードする前提なので、保存処理を遅らせないほうが安全です。


今後の拡張候補

このスクリプトは簡易版なので、必要なら次のような拡張が考えられます。

  • --name で出力ファイル名の接頭辞を指定する

  • prompt の一部から title を自動生成する

  • 縦横比や seed などのパラメータ指定を追加する

  • 保存先ディレクトリを指定できるようにする

  • fal.ai など他プロバイダ対応に広げる

  • 単発スクリプトではなく本格的な CLI に育てる


ライセンス

必要に応じて追記してください。

例:

MIT


補足

このツールは Grok Imagine API の簡易実験用 です。
堅牢な再試行制御、複数画像管理、履歴検索、モデル切り替えなどはまだ入れていません。
まずは「pass を前提に安全寄りの最小構成で画像生成を回す」ことを目的にしています。

#!/usr/bin/env bash
set -euo pipefail
usage() {
cat <<'EOF'
Usage:
grokimg "prompt text"
grokimg -p "prompt text"
grokimg --help
Description:
Read XAI API key from pass(api/xai), call Grok Imagine API,
save the generated image into the current working directory,
and also save a minimal Markdown file with YAML frontmatter.
Requirements:
- bash
- curl
- jq
- pass
- gpg / gpg-agent
Examples:
grokimg "A collage of London landmarks in a stenciled street-art style"
grokimg -p "A snow leopard on a Himalayan ridge at blue hour"
EOF
}
PROMPT=""
while [[ $# -gt 0 ]]; do
case "$1" in
-p|--prompt)
[[ $# -ge 2 ]] || { echo "Error: missing value for $1" >&2; exit 1; }
PROMPT="$2"
shift 2
;;
-h|--help)
usage
exit 0
;;
--)
shift
break
;;
-*)
echo "Error: unknown option: $1" >&2
usage >&2
exit 1
;;
*)
if [[ -z "$PROMPT" ]]; then
PROMPT="$1"
else
PROMPT+=" $1"
fi
shift
;;
esac
done
if [[ -z "$PROMPT" ]]; then
echo "Error: prompt is required." >&2
usage >&2
exit 1
fi
for cmd in curl jq pass; do
command -v "$cmd" >/dev/null 2>&1 || {
echo "Error: required command not found: $cmd" >&2
exit 1
}
done
XAI_API_KEY="$(pass show api/xai | head -n 1)"
if [[ -z "$XAI_API_KEY" ]]; then
echo "Error: failed to read API key from pass (api/xai)." >&2
exit 1
fi
response="$(
curl -fsS -X POST "https://api.x.ai/v1/images/generations" \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${XAI_API_KEY}" \
-d "$(jq -n \
--arg model "grok-imagine-image" \
--arg prompt "$PROMPT" \
'{model: $model, prompt: $prompt}')"
)"
image_url="$(printf '%s' "$response" | jq -r '.data[0].url // empty')"
mime_type="$(printf '%s' "$response" | jq -r '.data[0].mime_type // empty')"
revised_prompt="$(printf '%s' "$response" | jq -r '.data[0].revised_prompt // empty')"
cost_ticks="$(printf '%s' "$response" | jq -r '.usage.cost_in_usd_ticks // 0')"
if [[ -z "$image_url" ]]; then
echo "Error: image URL not found in API response." >&2
echo "$response" | jq . >&2 || echo "$response" >&2
exit 1
fi
ext="jpg"
case "$mime_type" in
image/jpeg) ext="jpg" ;;
image/png) ext="png" ;;
image/webp) ext="webp" ;;
esac
timestamp="$(date +%Y%m%d-%H%M%S)"
base="${timestamp}"
image_file="${base}.${ext}"
md_file="${base}.md"
curl -fsSLo "$image_file" "$image_url"
created_at="$(date --iso-8601=seconds 2>/dev/null || date '+%Y-%m-%dT%H:%M:%S%z')"
yaml_escape() {
printf '%s' "$1" | sed "s/'/''/g"
}
mime_type_escaped="$(yaml_escape "$mime_type")"
revised_prompt_escaped="$(yaml_escape "$revised_prompt")"
{
cat <<EOF
---
created_at: '${created_at}'
model: 'grok-imagine-image'
mime_type: '${mime_type_escaped}'
cost_in_usd_ticks: ${cost_ticks}
tags:
- grok-imagine
- image-generation
EOF
if [[ -n "$revised_prompt" ]]; then
cat <<EOF
revised_prompt: '${revised_prompt_escaped}'
EOF
fi
cat <<EOF
---
![](./${image_file})
${PROMPT}
EOF
} > "$md_file"
echo "Saved image: $image_file"
echo "Saved notes: $md_file"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment