🐚

Self-Host Dify Console APIのワンファイル Dify CLIを作ってみた(bash + curl + jq)

に公開1

これは何?

社内外で「DifyのConsole APIは公開情報が少なく、何がどこまで操作できるか掴みにくい」「手元からサクッとエクスポートや公開、ログ取得を回したい」という声があり、bashのワンファイルで動く簡易CLIを作ってみました。

インストール不要(bash + curl + jq 前提)で、1ファイルをPATHに置くだけ。以下が実行できます。

  • 認証(ログイン/ログアウト)とメタ情報取得
  • アプリ一覧/詳細の取得
  • DSLのエクスポート/インポート
  • Workflowアプリの公開・公開情報取得
  • Workflow実行ログ/チャット会話ログの取得
  • Workflowツール(provider_type=workflow)の取得/作成/更新
  • 親子関係解析(Workflowツール → 元Workflow)と依存の可視化用JSON生成

背景として、Console APIは一般公開ドキュメントが少なく、挙動が見えづらい場面があるため、手元で“何が起きているか”を確認できる道具があると便利、という意図で作っています。

リポジトリ: furu-kaggle/dify_cli_sample(使い方はREADMEを参照)。

前提

  • macOS/Linux の bash
  • curl, jq がインストール済み

セットアップ

  1. .env を作成
CONSOLE_API_BASE=セルフホストDifyのURL
EMAIL=あなたのメールアドレス
PASSWORD='あなたのパスワード'

# Optional
OUT_DIR=./dsl
  1. PATH にインストール(推奨)
mkdir -p "$HOME/bin"
ln -s "${PWD}/dify-cli.sh" "$HOME/bin/dify"
echo 'export PATH="$HOME/bin:$PATH"' >> "$HOME/.zshrc"
source "$HOME/.zshrc"
# 以後はどこからでも `dify <subcommand>`
  1. ログイン(トークンは .dify_token に保存)
dify login

よく使うコマンド

  • 認証・メタ
dify me
dify apps [page] [limit]
dify app <APP_ID>
  • DSL 出力/投入
# export(基本は include_secret=false 推奨)
dify export <APP_ID> [include_secret=false]

# import(新規アプリ作成; YAMLから)
dify import ./dsl/sample.yml
  • 公開
dify publish <WORKFLOW_APP_ID> [marked_name] [marked_comment]
dify publish-info <WORKFLOW_APP_ID>
  • ログ取得
# Workflow実行ログ(期間: created_at__after / created_at__before)
dify logs-workflow <APP_ID> '2025-06-04T11:00:00-04:00' '2025-06-12T10:59:59-04:00' 1 10

# チャット会話ログ(JSTの日時文字列も可; sort_by デフォルト -created_at)
dify logs-chat <APP_ID> '2025-06-19 00:00' '2025-06-26 23:59' 1 10 -created_at
  • ツール管理(workflow ツールプロバイダ)
dify tool-get <WORKFLOW_APP_ID>
dify tool-create ./payloads/tool-create.json
dify tool-update ./payloads/tool-update.json

親子関係デバッグ(Workflowツール → 元Workflow)

Workflow をツール化したノード(type=tool かつ provider_type="workflow")から、元の Workflow アプリの DSL を収集します。

  • 親アプリに使われている Workflow ツールノードを列挙
dify tool-refs-in-app <PARENT_APP_ID>
# 例: dify tool-refs-in-app b1f6b331-11c5-4ce9-b0e7-5331eb13d192
  • ワークスペース全体をスキャンして、Workflow ツールを含む親アプリ候補を列挙
dify find-apps-with-workflow-tools
  • 親アプリにぶら下がる「ツールの中身(元ワークフロー DSL)」を一括保存
dify export-tools-of-app <PARENT_APP_ID> [include_secret=false]
# 例: dify export-tools-of-app b1f6b331-11c5-4ce9-b0e7-5331eb13d192 false
# 出力先: ./dsl/tools/<ツール名slug>.yml(日本語等で slug 化できなければ tool_id フォールバック)

仕組み:

  • 親アプリのドラフトワークフロー GET /apps/{id}/workflows/draft から provider_type="workflow" ノードを抽出。
  • mode=workflow アプリに対し GET /workspaces/current/tool-provider/workflow/get?workflow_app_id=... を照会し、workflow_tool_id → workflow_app_id の辞書を構築。
  • 辞書を使って /apps/{workflow_app_id}/export?include_secret=false を叩き、DSL を保存。

include_secret について

  • 既定は include_secret=false(レポジトリ持ち込み事故を防止)。
  • true を渡すと API が許す範囲で含めますが、環境・権限によりマスク/返却無しのことがあります。
dify export <APP_ID> true
dify export-tools-of-app <PARENT_APP_ID> true

ベストプラクティス / トラブルシューティング

  • ヘッダは慣例どおり Authorization: Bearer <token> を使用。
  • エラー本文を見やすくするには curl -fS --fail-with-body を活用。
  • URL エンコードは CLI 内で jq -rn '$v|@uri' を利用(手動の場合も %3A 等を忘れない)。
  • export レスポンスは環境により {data:"<yaml>"} または {dsl:"<yaml>"}。CLI は両対応。
  • タイムスタンプはオフセット付き(例: 2025-06-04T11:00:00-04:00)が安全。
  • トークンが失効したら dify login を再実行(必要なら自動更新に拡張可)。
  • jq の古いバージョンでも動くよう、インデックスのマージは --argfile 非依存で実装済み。

例: 一連の操作

# 1) ログイン
dify login

# 2) アプリ一覧
dify apps

# 3) DSLを出力(保存先: ./dsl/apps/<slug>.yml)
dify export <APP_ID> false

# 4) DSLをインポート(新規作成)
dify import ./dsl/sample.yml

# 5) 公開
dify publish <WORKFLOW_APP_ID> "nightly" "auto publish"
dify publish-info <WORKFLOW_APP_ID>

# 6) Workflowログ(期間絞り)
dify logs-workflow <APP_ID> '2025-06-04T11:00:00-04:00' '2025-06-12T10:59:59-04:00' 1 10

# 7) 親子関係(ツール→元Workflow)を解決してツールの中身を保存
dify export-tools-of-app <PARENT_APP_ID> false

注意事項

  • 機密情報(APIキー等)の取り扱いには十分ご注意ください。include_secret=true の使用は推奨しません。
  • 出力ファイルは ./dsl/apps/*.yml に統一。必要に応じて .gitignore へ追加してください。
  • 依存関係は別途 index(例: ./dsl/deps.json)で管理(生成コマンドで出力)。

実コマンドと結果

使い方はリポジトリのREADME(furu-kaggle/dify_cli_sample)を参照しつつ、以下はローカル環境での実行例と生の出力です(抜粋)。

login

dify login
>> Logging in to http://localhost/console/api ...
OK. token saved to /Users/kokoorikunihiko/Desktop/dify_cli_sample/.dify_token

me

dify me | jq
{
  "api_base": "http://localhost/console/api",
  "auth_ok": true,
  "source": "/account/profile",
  "profile": {
    "id": "b23a8da7-7f42-42a2-8064-7416270b1b1a",
    "name": "**********@gmail.com",
    "avatar": null,
    "avatar_url": null,
    "email": "**********@gmail.com",
    "is_password_set": true,
    "interface_language": "en-US",
    "interface_theme": "light",
    "timezone": "America/New_York",
    "last_login_at": 1756121974,
    "last_login_ip": "192.168.65.1",
    "created_at": 1755505179
  },
  "token_payload": {
    "user_id": "b23a8da7-7f42-42a2-8064-7416270b1b1a",
    "exp": 1756125574,
    "iss": "SELF_HOSTED",
    "sub": "Console API Passport"
  }
}

apps

dify apps 1 20 | jq
{
  "total": 2,
  "data": [
    {
      "id": "fead97e2-9430-4165-aa0a-7705fe62629f",
      "name": "technical_analysis_report",
      "mode": "workflow"
    },
    {
      "id": "27276e3a-523a-4900-88a0-04008ce47311",
      "name": "StockPredictionNextDayRange",
      "mode": "advanced-chat"
    }
  ]
}

app

dify app fead97e2-9430-4165-aa0a-7705fe62629f | jq '{id,name,mode}'
{
  "id": "fead97e2-9430-4165-aa0a-7705fe62629f",
  "name": "technical_analysis_report",
  "mode": "workflow"
}

export

dify export fead97e2-9430-4165-aa0a-7705fe62629f false
>> Exporting DSL for technical_analysis_report (fead97e2-9430-4165-aa0a-7705fe62629f) include_secret=false
saved: ./dsl/apps/technical-analysis-report.yml

publish

dify publish fead97e2-9430-4165-aa0a-7705fe62629f "nightly" "auto publish from CLI"
{"result": "success", "created_at": 1756122079}

publish-info

dify publish-info fead97e2-9430-4165-aa0a-7705fe62629f
{"id": "d87139b4-79fc-4d80-92dc-8af0ff7c16c4", "graph": { ... 省略 ... }, "version": "2025-08-25 11:41:19.733173", "marked_name": "nightly", "marked_comment": "auto publish from CLI", "created_at": 1756122079, "tool_published": true}

logs-workflow

dify logs-workflow fead97e2-9430-4165-aa0a-7705fe62629f '2025-06-04T11:00:00-04:00' '2025-06-12T10:59:59-04:00' 1 5 | jq '.data | (if type=="array" then .[0:5] else . end)'
[]

logs-chat

CHAT_APP_ID=27276e3a-523a-4900-88a0-04008ce47311
dify logs-chat "$CHAT_APP_ID" '2025-06-19 00:00' '2025-06-26 23:59' 1 5 -created_at | jq '.data | (if type=="array" then .[0:5] else . end)'
[]

tool-get

dify tool-get fead97e2-9430-4165-aa0a-7705fe62629f | jq '{workflow_tool_id, workflow_app_id, name}'
{
  "workflow_tool_id": "9cdf90c3-10ff-48df-8130-f9ede6f99745",
  "workflow_app_id": "fead97e2-9430-4165-aa0a-7705fe62629f",
  "name": "TecnicalAnalysisAgent"
}

tool-refs-in-app

dify tool-refs-in-app fead97e2-9430-4165-aa0a-7705fe62629f
(出力なし)  # この親アプリ内には workflow ツールノードがありませんでした

export-tools-of-app

dify export-tools-of-app fead97e2-9430-4165-aa0a-7705fe62629f false
>> Collect tool nodes from parent app...
No workflow tools found in parent app.

find-apps-with-workflow-tools

dify find-apps-with-workflow-tools | jq -s '.'
[]

export-all-workflows

dify export-all-workflows false
>> Export all workflow apps (include_secret=false)
>> Exporting technical_analysis_report (fead97e2-9430-4165-aa0a-7705fe62629f)
saved: ./dsl/apps/technical-analysis-report-fead97e2-9430-4165-aa0a-7705fe62629f.yml

deps-generate

dify deps-generate ./dsl/deps.json
cat ./dsl/deps.json | jq '. | {edges:(.edges|length), orphans:(.orphans|length), apps:(.apps|length)}'
{
  "edges": 1,
  "orphans": 0,
  "apps": 2
}

おわりに

セルフホストのDify Console APIを手元から安全に“まず触って把握する”ための最短経路として、ワンファイルCLIを用意しました。これを作ることで実際のConsole APIの理解が深まったので皆さんも是非作ってみてください。Difyの裏側の勉強になります。

  • リポジトリ: furu-kaggle/dify_cli_sample
  • トークン/DSLの保存先は .gitignore 登録を推奨
  • API仕様は環境/バージョンで差異があり得ますサンプルとしてご利用ください(本CLIは冪等・後方互換を目指しつつ、二系統のレスポンスに対応)

Discussion

uzimihsruzimihsr

furufuru様
素晴らしい記事&ツールをありがとうございます!

少しだけ気になる点があり、僭越ながらコメントさせていただきました。
Dify v1.9.2から/console/api/loginでのログイン時にレスポンスボディにトークンが含まれないようになっているので、
Dify側のバージョンを上げるとdify loginが失敗してしまうようです。

$ dify login
>> Logging in to http://localhost/console/api ...
Error: failed to obtain access_token

v1.9.2からはトークンがCookieに埋め込まれる形になるので、
login時に付与されるCookieを参照することで対応可能です。

# ログイン
email='your-email@example.com'
password='your-password'
base_url='http://localhost'
curl -c cookie -X POST "${base_url}/console/api/login" -H 'Content-Type: application/json' -d "{\"email\":\"${email}\",\"password\":\"${password}\"}"
csrf_token=$(grep csrf_token cookie | cut -f7)

# API呼び出し(dify me相当)
curl -sSf -X GET -b cookie -H "X-CSRF-Token: ${csrf_token}" "${base_url}/console/api/account/profile"

https://blog.uzimihsr.com/post/2025-10-25-dify-console-api-curl/

ご参考:

突然の長文、失礼いたしました🙇‍♂️