🐚
Self-Host Dify Console APIのワンファイル Dify CLIを作ってみた(bash + curl + jq)
これは何?
社内外で「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がインストール済み
セットアップ
-
.envを作成
CONSOLE_API_BASE=セルフホストDifyのURL
EMAIL=あなたのメールアドレス
PASSWORD='あなたのパスワード'
# Optional
OUT_DIR=./dsl
- 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>`
- ログイン(トークンは
.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
furufuru様
素晴らしい記事&ツールをありがとうございます!
少しだけ気になる点があり、僭越ながらコメントさせていただきました。
Dify v1.9.2から
/console/api/loginでのログイン時にレスポンスボディにトークンが含まれないようになっているので、Dify側のバージョンを上げると
dify loginが失敗してしまうようです。v1.9.2からはトークンがCookieに埋め込まれる形になるので、
login時に付与されるCookieを参照することで対応可能です。
ご参考:
突然の長文、失礼いたしました🙇♂️