This is an automated email from the ASF dual-hosted git repository.

bugraoz pushed a commit to branch v3-2-test
in repository https://gitbox.apache.org/repos/asf/airflow.git


The following commit(s) were added to refs/heads/v3-2-test by this push:
     new f9559f9c2b0 [v3-2-test] Added plugins command to airflowctl (#64935) 
(#65001)
f9559f9c2b0 is described below

commit f9559f9c2b09fa2e60a7445e18c94ce8aa1c5128
Author: github-actions[bot] 
<41898282+github-actions[bot]@users.noreply.github.com>
AuthorDate: Sat Apr 11 22:01:22 2026 +0200

    [v3-2-test] Added plugins command to airflowctl (#64935) (#65001)
    
    * Added plugins command into airflowctl
    
    * Add missing docstring to test
    (cherry picked from commit bfa22c05a822e5b7931dda233a1f86309aabc8f1)
    
    Co-authored-by: Justin Pakzad 
<[email protected]>
---
 .../airflowctl_tests/test_airflowctl_commands.py   |   3 +
 airflow-ctl/docs/images/command_hashes.txt         |   3 +-
 airflow-ctl/docs/images/output_main.svg            | 130 +++++++++++----------
 airflow-ctl/docs/images/output_plugins.svg         | 105 +++++++++++++++++
 airflow-ctl/src/airflowctl/api/client.py           |   7 ++
 airflow-ctl/src/airflowctl/api/operations.py       |  18 +++
 .../tests/airflow_ctl/api/test_operations.py       |  54 +++++++++
 .../in_container/run_capture_airflowctl_help.py    |   1 +
 8 files changed, 257 insertions(+), 64 deletions(-)

diff --git 
a/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py 
b/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
index 4aea60dca68..5215c2946cb 100644
--- a/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
+++ b/airflow-ctl-tests/tests/airflowctl_tests/test_airflowctl_commands.py
@@ -124,6 +124,9 @@ TEST_COMMANDS = [
     "variables delete --variable-key=test_import_var_with_desc",
     # Version command
     "version --remote",
+    # Plugins command
+    "plugins list",
+    "plugins list-import-errors",
 ]
 
 DATE_PARAM_1 = date_param()
diff --git a/airflow-ctl/docs/images/command_hashes.txt 
b/airflow-ctl/docs/images/command_hashes.txt
index b0089d41d1f..3e03b177158 100644
--- a/airflow-ctl/docs/images/command_hashes.txt
+++ b/airflow-ctl/docs/images/command_hashes.txt
@@ -1,4 +1,4 @@
-main:65249416abad6ad24c276fb44326ae15
+main:27a22c00dcf32e7a1a4f06672dc8e3c8
 assets:b3ae2b933e54528bf486ff28e887804d
 auth:d79e9c7d00c432bdbcbc2a86e2e32053
 backfill:bbce9859a2d1ce054ad22db92dea8c05
@@ -11,4 +11,5 @@ pools:03fc7d948cbecf16ff8d640eb8f0ce43
 providers:1c0afb2dff31d93ab2934b032a2250ab
 variables:0354f8f4b0dde1c3771ed1568692c6ae
 version:31f4efdf8de0dbaaa4fac71ff7efecc3
+plugins:118ea0beda9b3935eedc67b0511f7e4a
 auth login:9fe2bb1dd5c602beea2eefb33a2b20a8
diff --git a/airflow-ctl/docs/images/output_main.svg 
b/airflow-ctl/docs/images/output_main.svg
index 8e4ef71bdb0..f586877bce8 100644
--- a/airflow-ctl/docs/images/output_main.svg
+++ b/airflow-ctl/docs/images/output_main.svg
@@ -1,4 +1,4 @@
-<svg class="rich-terminal" viewBox="0 0 933 684.4" 
xmlns="http://www.w3.org/2000/svg";>
+<svg class="rich-terminal" viewBox="0 0 933 708.8" 
xmlns="http://www.w3.org/2000/svg";>
     <!-- Generated with Rich https://www.textualize.io -->
     <style>
 
@@ -19,142 +19,146 @@
         font-weight: 700;
     }
 
-    .terminal-4108169915-matrix {
+    .terminal-2670787131-matrix {
         font-family: Fira Code, monospace;
         font-size: 20px;
         line-height: 24.4px;
         font-variant-east-asian: full-width;
     }
 
-    .terminal-4108169915-title {
+    .terminal-2670787131-title {
         font-size: 18px;
         font-weight: bold;
         font-family: arial;
     }
 
-    .terminal-4108169915-r1 { fill: #ff8700 }
-.terminal-4108169915-r2 { fill: #c5c8c6 }
-.terminal-4108169915-r3 { fill: #808080 }
-.terminal-4108169915-r4 { fill: #68a0b3 }
+    .terminal-2670787131-r1 { fill: #ff8700 }
+.terminal-2670787131-r2 { fill: #c5c8c6 }
+.terminal-2670787131-r3 { fill: #808080 }
+.terminal-2670787131-r4 { fill: #68a0b3 }
     </style>
 
     <defs>
-    <clipPath id="terminal-4108169915-clip-terminal">
-      <rect x="0" y="0" width="914.0" height="633.4" />
+    <clipPath id="terminal-2670787131-clip-terminal">
+      <rect x="0" y="0" width="914.0" height="657.8" />
     </clipPath>
-    <clipPath id="terminal-4108169915-line-0">
+    <clipPath id="terminal-2670787131-line-0">
     <rect x="0" y="1.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-1">
+<clipPath id="terminal-2670787131-line-1">
     <rect x="0" y="25.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-2">
+<clipPath id="terminal-2670787131-line-2">
     <rect x="0" y="50.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-3">
+<clipPath id="terminal-2670787131-line-3">
     <rect x="0" y="74.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-4">
+<clipPath id="terminal-2670787131-line-4">
     <rect x="0" y="99.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-5">
+<clipPath id="terminal-2670787131-line-5">
     <rect x="0" y="123.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-6">
+<clipPath id="terminal-2670787131-line-6">
     <rect x="0" y="147.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-7">
+<clipPath id="terminal-2670787131-line-7">
     <rect x="0" y="172.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-8">
+<clipPath id="terminal-2670787131-line-8">
     <rect x="0" y="196.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-9">
+<clipPath id="terminal-2670787131-line-9">
     <rect x="0" y="221.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-10">
+<clipPath id="terminal-2670787131-line-10">
     <rect x="0" y="245.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-11">
+<clipPath id="terminal-2670787131-line-11">
     <rect x="0" y="269.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-12">
+<clipPath id="terminal-2670787131-line-12">
     <rect x="0" y="294.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-13">
+<clipPath id="terminal-2670787131-line-13">
     <rect x="0" y="318.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-14">
+<clipPath id="terminal-2670787131-line-14">
     <rect x="0" y="343.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-15">
+<clipPath id="terminal-2670787131-line-15">
     <rect x="0" y="367.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-16">
+<clipPath id="terminal-2670787131-line-16">
     <rect x="0" y="391.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-17">
+<clipPath id="terminal-2670787131-line-17">
     <rect x="0" y="416.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-18">
+<clipPath id="terminal-2670787131-line-18">
     <rect x="0" y="440.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-19">
+<clipPath id="terminal-2670787131-line-19">
     <rect x="0" y="465.1" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-20">
+<clipPath id="terminal-2670787131-line-20">
     <rect x="0" y="489.5" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-21">
+<clipPath id="terminal-2670787131-line-21">
     <rect x="0" y="513.9" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-22">
+<clipPath id="terminal-2670787131-line-22">
     <rect x="0" y="538.3" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-23">
+<clipPath id="terminal-2670787131-line-23">
     <rect x="0" y="562.7" width="915" height="24.65"/>
             </clipPath>
-<clipPath id="terminal-4108169915-line-24">
+<clipPath id="terminal-2670787131-line-24">
     <rect x="0" y="587.1" width="915" height="24.65"/>
             </clipPath>
+<clipPath id="terminal-2670787131-line-25">
+    <rect x="0" y="611.5" width="915" height="24.65"/>
+            </clipPath>
     </defs>
 
-    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="931" height="682.4" rx="8"/>
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="931" height="706.8" rx="8"/>
             <g transform="translate(26,22)">
             <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
             <circle cx="22" cy="0" r="7" fill="#febc2e"/>
             <circle cx="44" cy="0" r="7" fill="#28c840"/>
             </g>
         
-    <g transform="translate(9, 41)" 
clip-path="url(#terminal-4108169915-clip-terminal)">
+    <g transform="translate(9, 41)" 
clip-path="url(#terminal-2670787131-clip-terminal)">
     
-    <g class="terminal-4108169915-matrix">
-    <text class="terminal-4108169915-r1" x="0" y="20" textLength="73.2" 
clip-path="url(#terminal-4108169915-line-0)">Usage:</text><text 
class="terminal-4108169915-r3" x="85.4" y="20" textLength="122" 
clip-path="url(#terminal-4108169915-line-0)">airflowctl</text><text 
class="terminal-4108169915-r2" x="207.4" y="20" textLength="24.4" 
clip-path="url(#terminal-4108169915-line-0)">&#160;[</text><text 
class="terminal-4108169915-r4" x="231.8" y="20" textLength="24.4" 
clip-path="url(#terminal-41 [...]
-</text><text class="terminal-4108169915-r2" x="915" y="44.4" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-1)">
-</text><text class="terminal-4108169915-r1" x="0" y="68.8" textLength="256.2" 
clip-path="url(#terminal-4108169915-line-2)">Positional&#160;Arguments:</text><text
 class="terminal-4108169915-r2" x="915" y="68.8" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-2)">
-</text><text class="terminal-4108169915-r4" x="24.4" y="93.2" 
textLength="195.2" 
clip-path="url(#terminal-4108169915-line-3)">GROUP_OR_COMMAND</text><text 
class="terminal-4108169915-r2" x="915" y="93.2" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-3)">
-</text><text class="terminal-4108169915-r2" x="915" y="117.6" 
textLength="12.2" clip-path="url(#terminal-4108169915-line-4)">
-</text><text class="terminal-4108169915-r4" x="0" y="142" textLength="122" 
clip-path="url(#terminal-4108169915-line-5)">&#160;&#160;&#160;&#160;Groups</text><text
 class="terminal-4108169915-r2" x="915" y="142" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-5)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="166.4" 
textLength="73.2" 
clip-path="url(#terminal-4108169915-line-6)">assets</text><text 
class="terminal-4108169915-r2" x="244" y="166.4" textLength="305" 
clip-path="url(#terminal-4108169915-line-6)">Perform&#160;Assets&#160;operations</text><text
 class="terminal-4108169915-r2" x="915" y="166.4" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-6)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="190.8" 
textLength="48.8" clip-path="url(#terminal-4108169915-line-7)">auth</text><text 
class="terminal-4108169915-r2" x="244" y="190.8" textLength="646.6" 
clip-path="url(#terminal-4108169915-line-7)">Manage&#160;authentication&#160;for&#160;CLI.&#160;Either&#160;pass&#160;token&#160;from</text><text
 class="terminal-4108169915-r2" x="915" y="190.8" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-7)">
-</text><text class="terminal-4108169915-r2" x="244" y="215.2" 
textLength="622.2" 
clip-path="url(#terminal-4108169915-line-8)">environment&#160;variable/parameter&#160;or&#160;pass&#160;username&#160;and</text><text
 class="terminal-4108169915-r2" x="915" y="215.2" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-8)">
-</text><text class="terminal-4108169915-r2" x="244" y="239.6" 
textLength="109.8" 
clip-path="url(#terminal-4108169915-line-9)">password.</text><text 
class="terminal-4108169915-r2" x="915" y="239.6" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-9)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="264" textLength="97.6" 
clip-path="url(#terminal-4108169915-line-10)">backfill</text><text 
class="terminal-4108169915-r2" x="244" y="264" textLength="329.4" 
clip-path="url(#terminal-4108169915-line-10)">Perform&#160;Backfill&#160;operations</text><text
 class="terminal-4108169915-r2" x="915" y="264" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-10)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="288.4" 
textLength="73.2" 
clip-path="url(#terminal-4108169915-line-11)">config</text><text 
class="terminal-4108169915-r2" x="244" y="288.4" textLength="305" 
clip-path="url(#terminal-4108169915-line-11)">Perform&#160;Config&#160;operations</text><text
 class="terminal-4108169915-r2" x="915" y="288.4" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-11)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="312.8" 
textLength="134.2" 
clip-path="url(#terminal-4108169915-line-12)">connections</text><text 
class="terminal-4108169915-r2" x="244" y="312.8" textLength="366" 
clip-path="url(#terminal-4108169915-line-12)">Perform&#160;Connections&#160;operations</text><text
 class="terminal-4108169915-r2" x="915" y="312.8" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-12)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="337.2" 
textLength="73.2" 
clip-path="url(#terminal-4108169915-line-13)">dagrun</text><text 
class="terminal-4108169915-r2" x="244" y="337.2" textLength="305" 
clip-path="url(#terminal-4108169915-line-13)">Perform&#160;DagRun&#160;operations</text><text
 class="terminal-4108169915-r2" x="915" y="337.2" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-13)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="361.6" 
textLength="48.8" 
clip-path="url(#terminal-4108169915-line-14)">dags</text><text 
class="terminal-4108169915-r2" x="244" y="361.6" textLength="280.6" 
clip-path="url(#terminal-4108169915-line-14)">Perform&#160;Dags&#160;operations</text><text
 class="terminal-4108169915-r2" x="915" y="361.6" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-14)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="386" textLength="48.8" 
clip-path="url(#terminal-4108169915-line-15)">jobs</text><text 
class="terminal-4108169915-r2" x="244" y="386" textLength="280.6" 
clip-path="url(#terminal-4108169915-line-15)">Perform&#160;Jobs&#160;operations</text><text
 class="terminal-4108169915-r2" x="915" y="386" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-15)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="410.4" textLength="61" 
clip-path="url(#terminal-4108169915-line-16)">pools</text><text 
class="terminal-4108169915-r2" x="244" y="410.4" textLength="292.8" 
clip-path="url(#terminal-4108169915-line-16)">Perform&#160;Pools&#160;operations</text><text
 class="terminal-4108169915-r2" x="915" y="410.4" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-16)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="434.8" 
textLength="109.8" 
clip-path="url(#terminal-4108169915-line-17)">providers</text><text 
class="terminal-4108169915-r2" x="244" y="434.8" textLength="341.6" 
clip-path="url(#terminal-4108169915-line-17)">Perform&#160;Providers&#160;operations</text><text
 class="terminal-4108169915-r2" x="915" y="434.8" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-17)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="459.2" 
textLength="109.8" 
clip-path="url(#terminal-4108169915-line-18)">variables</text><text 
class="terminal-4108169915-r2" x="244" y="459.2" textLength="341.6" 
clip-path="url(#terminal-4108169915-line-18)">Perform&#160;Variables&#160;operations</text><text
 class="terminal-4108169915-r2" x="915" y="459.2" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-18)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="483.6" 
textLength="48.8" 
clip-path="url(#terminal-4108169915-line-19)">xcom</text><text 
class="terminal-4108169915-r2" x="244" y="483.6" textLength="280.6" 
clip-path="url(#terminal-4108169915-line-19)">Perform&#160;XCom&#160;operations</text><text
 class="terminal-4108169915-r2" x="915" y="483.6" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-19)">
-</text><text class="terminal-4108169915-r2" x="915" y="508" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-20)">
-</text><text class="terminal-4108169915-r4" x="0" y="532.4" textLength="158.6" 
clip-path="url(#terminal-4108169915-line-21)">&#160;&#160;&#160;&#160;Commands:</text><text
 class="terminal-4108169915-r2" x="915" y="532.4" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-21)">
-</text><text class="terminal-4108169915-r4" x="73.2" y="556.8" 
textLength="85.4" 
clip-path="url(#terminal-4108169915-line-22)">version</text><text 
class="terminal-4108169915-r2" x="244" y="556.8" textLength="292.8" 
clip-path="url(#terminal-4108169915-line-22)">Show&#160;version&#160;information</text><text
 class="terminal-4108169915-r2" x="915" y="556.8" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-22)">
-</text><text class="terminal-4108169915-r2" x="915" y="581.2" 
textLength="12.2" clip-path="url(#terminal-4108169915-line-23)">
-</text><text class="terminal-4108169915-r1" x="0" y="605.6" textLength="97.6" 
clip-path="url(#terminal-4108169915-line-24)">Options:</text><text 
class="terminal-4108169915-r2" x="915" y="605.6" textLength="12.2" 
clip-path="url(#terminal-4108169915-line-24)">
-</text><text class="terminal-4108169915-r4" x="24.4" y="630" textLength="24.4" 
clip-path="url(#terminal-4108169915-line-25)">-h</text><text 
class="terminal-4108169915-r2" x="48.8" y="630" textLength="24.4" 
clip-path="url(#terminal-4108169915-line-25)">,&#160;</text><text 
class="terminal-4108169915-r4" x="73.2" y="630" textLength="73.2" 
clip-path="url(#terminal-4108169915-line-25)">--help</text><text 
class="terminal-4108169915-r2" x="244" y="630" textLength="378.2" 
clip-path="url(#termina [...]
+    <g class="terminal-2670787131-matrix">
+    <text class="terminal-2670787131-r1" x="0" y="20" textLength="73.2" 
clip-path="url(#terminal-2670787131-line-0)">Usage:</text><text 
class="terminal-2670787131-r3" x="85.4" y="20" textLength="122" 
clip-path="url(#terminal-2670787131-line-0)">airflowctl</text><text 
class="terminal-2670787131-r2" x="207.4" y="20" textLength="24.4" 
clip-path="url(#terminal-2670787131-line-0)">&#160;[</text><text 
class="terminal-2670787131-r4" x="231.8" y="20" textLength="24.4" 
clip-path="url(#terminal-26 [...]
+</text><text class="terminal-2670787131-r2" x="915" y="44.4" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-1)">
+</text><text class="terminal-2670787131-r1" x="0" y="68.8" textLength="256.2" 
clip-path="url(#terminal-2670787131-line-2)">Positional&#160;Arguments:</text><text
 class="terminal-2670787131-r2" x="915" y="68.8" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-2)">
+</text><text class="terminal-2670787131-r4" x="24.4" y="93.2" 
textLength="195.2" 
clip-path="url(#terminal-2670787131-line-3)">GROUP_OR_COMMAND</text><text 
class="terminal-2670787131-r2" x="915" y="93.2" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-3)">
+</text><text class="terminal-2670787131-r2" x="915" y="117.6" 
textLength="12.2" clip-path="url(#terminal-2670787131-line-4)">
+</text><text class="terminal-2670787131-r4" x="0" y="142" textLength="122" 
clip-path="url(#terminal-2670787131-line-5)">&#160;&#160;&#160;&#160;Groups</text><text
 class="terminal-2670787131-r2" x="915" y="142" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-5)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="166.4" 
textLength="73.2" 
clip-path="url(#terminal-2670787131-line-6)">assets</text><text 
class="terminal-2670787131-r2" x="244" y="166.4" textLength="305" 
clip-path="url(#terminal-2670787131-line-6)">Perform&#160;Assets&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="166.4" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-6)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="190.8" 
textLength="48.8" clip-path="url(#terminal-2670787131-line-7)">auth</text><text 
class="terminal-2670787131-r2" x="244" y="190.8" textLength="646.6" 
clip-path="url(#terminal-2670787131-line-7)">Manage&#160;authentication&#160;for&#160;CLI.&#160;Either&#160;pass&#160;token&#160;from</text><text
 class="terminal-2670787131-r2" x="915" y="190.8" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-7)">
+</text><text class="terminal-2670787131-r2" x="244" y="215.2" 
textLength="622.2" 
clip-path="url(#terminal-2670787131-line-8)">environment&#160;variable/parameter&#160;or&#160;pass&#160;username&#160;and</text><text
 class="terminal-2670787131-r2" x="915" y="215.2" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-8)">
+</text><text class="terminal-2670787131-r2" x="244" y="239.6" 
textLength="109.8" 
clip-path="url(#terminal-2670787131-line-9)">password.</text><text 
class="terminal-2670787131-r2" x="915" y="239.6" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-9)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="264" textLength="97.6" 
clip-path="url(#terminal-2670787131-line-10)">backfill</text><text 
class="terminal-2670787131-r2" x="244" y="264" textLength="329.4" 
clip-path="url(#terminal-2670787131-line-10)">Perform&#160;Backfill&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="264" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-10)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="288.4" 
textLength="73.2" 
clip-path="url(#terminal-2670787131-line-11)">config</text><text 
class="terminal-2670787131-r2" x="244" y="288.4" textLength="305" 
clip-path="url(#terminal-2670787131-line-11)">Perform&#160;Config&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="288.4" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-11)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="312.8" 
textLength="134.2" 
clip-path="url(#terminal-2670787131-line-12)">connections</text><text 
class="terminal-2670787131-r2" x="244" y="312.8" textLength="366" 
clip-path="url(#terminal-2670787131-line-12)">Perform&#160;Connections&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="312.8" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-12)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="337.2" 
textLength="73.2" 
clip-path="url(#terminal-2670787131-line-13)">dagrun</text><text 
class="terminal-2670787131-r2" x="244" y="337.2" textLength="305" 
clip-path="url(#terminal-2670787131-line-13)">Perform&#160;DagRun&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="337.2" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-13)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="361.6" 
textLength="48.8" 
clip-path="url(#terminal-2670787131-line-14)">dags</text><text 
class="terminal-2670787131-r2" x="244" y="361.6" textLength="280.6" 
clip-path="url(#terminal-2670787131-line-14)">Perform&#160;Dags&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="361.6" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-14)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="386" textLength="48.8" 
clip-path="url(#terminal-2670787131-line-15)">jobs</text><text 
class="terminal-2670787131-r2" x="244" y="386" textLength="280.6" 
clip-path="url(#terminal-2670787131-line-15)">Perform&#160;Jobs&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="386" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-15)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="410.4" 
textLength="85.4" 
clip-path="url(#terminal-2670787131-line-16)">plugins</text><text 
class="terminal-2670787131-r2" x="244" y="410.4" textLength="317.2" 
clip-path="url(#terminal-2670787131-line-16)">Perform&#160;Plugins&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="410.4" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-16)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="434.8" textLength="61" 
clip-path="url(#terminal-2670787131-line-17)">pools</text><text 
class="terminal-2670787131-r2" x="244" y="434.8" textLength="292.8" 
clip-path="url(#terminal-2670787131-line-17)">Perform&#160;Pools&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="434.8" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-17)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="459.2" 
textLength="109.8" 
clip-path="url(#terminal-2670787131-line-18)">providers</text><text 
class="terminal-2670787131-r2" x="244" y="459.2" textLength="341.6" 
clip-path="url(#terminal-2670787131-line-18)">Perform&#160;Providers&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="459.2" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-18)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="483.6" 
textLength="109.8" 
clip-path="url(#terminal-2670787131-line-19)">variables</text><text 
class="terminal-2670787131-r2" x="244" y="483.6" textLength="341.6" 
clip-path="url(#terminal-2670787131-line-19)">Perform&#160;Variables&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="483.6" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-19)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="508" textLength="48.8" 
clip-path="url(#terminal-2670787131-line-20)">xcom</text><text 
class="terminal-2670787131-r2" x="244" y="508" textLength="280.6" 
clip-path="url(#terminal-2670787131-line-20)">Perform&#160;XCom&#160;operations</text><text
 class="terminal-2670787131-r2" x="915" y="508" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-20)">
+</text><text class="terminal-2670787131-r2" x="915" y="532.4" 
textLength="12.2" clip-path="url(#terminal-2670787131-line-21)">
+</text><text class="terminal-2670787131-r4" x="0" y="556.8" textLength="158.6" 
clip-path="url(#terminal-2670787131-line-22)">&#160;&#160;&#160;&#160;Commands:</text><text
 class="terminal-2670787131-r2" x="915" y="556.8" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-22)">
+</text><text class="terminal-2670787131-r4" x="73.2" y="581.2" 
textLength="85.4" 
clip-path="url(#terminal-2670787131-line-23)">version</text><text 
class="terminal-2670787131-r2" x="244" y="581.2" textLength="292.8" 
clip-path="url(#terminal-2670787131-line-23)">Show&#160;version&#160;information</text><text
 class="terminal-2670787131-r2" x="915" y="581.2" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-23)">
+</text><text class="terminal-2670787131-r2" x="915" y="605.6" 
textLength="12.2" clip-path="url(#terminal-2670787131-line-24)">
+</text><text class="terminal-2670787131-r1" x="0" y="630" textLength="97.6" 
clip-path="url(#terminal-2670787131-line-25)">Options:</text><text 
class="terminal-2670787131-r2" x="915" y="630" textLength="12.2" 
clip-path="url(#terminal-2670787131-line-25)">
+</text><text class="terminal-2670787131-r4" x="24.4" y="654.4" 
textLength="24.4" clip-path="url(#terminal-2670787131-line-26)">-h</text><text 
class="terminal-2670787131-r2" x="48.8" y="654.4" textLength="24.4" 
clip-path="url(#terminal-2670787131-line-26)">,&#160;</text><text 
class="terminal-2670787131-r4" x="73.2" y="654.4" textLength="73.2" 
clip-path="url(#terminal-2670787131-line-26)">--help</text><text 
class="terminal-2670787131-r2" x="244" y="654.4" textLength="378.2" 
clip-path="url( [...]
 </text>
     </g>
     </g>
diff --git a/airflow-ctl/docs/images/output_plugins.svg 
b/airflow-ctl/docs/images/output_plugins.svg
new file mode 100644
index 00000000000..21dfd097508
--- /dev/null
+++ b/airflow-ctl/docs/images/output_plugins.svg
@@ -0,0 +1,105 @@
+<svg class="rich-terminal" viewBox="0 0 933 342.79999999999995" 
xmlns="http://www.w3.org/2000/svg";>
+    <!-- Generated with Rich https://www.textualize.io -->
+    <style>
+
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Regular"),
+                
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Regular.woff2";)
 format("woff2"),
+                
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Regular.woff";)
 format("woff");
+        font-style: normal;
+        font-weight: 400;
+    }
+    @font-face {
+        font-family: "Fira Code";
+        src: local("FiraCode-Bold"),
+                
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff2/FiraCode-Bold.woff2";)
 format("woff2"),
+                
url("https://cdnjs.cloudflare.com/ajax/libs/firacode/6.2.0/woff/FiraCode-Bold.woff";)
 format("woff");
+        font-style: bold;
+        font-weight: 700;
+    }
+
+    .terminal-2458333931-matrix {
+        font-family: Fira Code, monospace;
+        font-size: 20px;
+        line-height: 24.4px;
+        font-variant-east-asian: full-width;
+    }
+
+    .terminal-2458333931-title {
+        font-size: 18px;
+        font-weight: bold;
+        font-family: arial;
+    }
+
+    .terminal-2458333931-r1 { fill: #ff8700 }
+.terminal-2458333931-r2 { fill: #c5c8c6 }
+.terminal-2458333931-r3 { fill: #808080 }
+.terminal-2458333931-r4 { fill: #68a0b3 }
+    </style>
+
+    <defs>
+    <clipPath id="terminal-2458333931-clip-terminal">
+      <rect x="0" y="0" width="914.0" height="291.79999999999995" />
+    </clipPath>
+    <clipPath id="terminal-2458333931-line-0">
+    <rect x="0" y="1.5" width="915" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2458333931-line-1">
+    <rect x="0" y="25.9" width="915" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2458333931-line-2">
+    <rect x="0" y="50.3" width="915" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2458333931-line-3">
+    <rect x="0" y="74.7" width="915" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2458333931-line-4">
+    <rect x="0" y="99.1" width="915" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2458333931-line-5">
+    <rect x="0" y="123.5" width="915" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2458333931-line-6">
+    <rect x="0" y="147.9" width="915" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2458333931-line-7">
+    <rect x="0" y="172.3" width="915" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2458333931-line-8">
+    <rect x="0" y="196.7" width="915" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2458333931-line-9">
+    <rect x="0" y="221.1" width="915" height="24.65"/>
+            </clipPath>
+<clipPath id="terminal-2458333931-line-10">
+    <rect x="0" y="245.5" width="915" height="24.65"/>
+            </clipPath>
+    </defs>
+
+    <rect fill="#292929" stroke="rgba(255,255,255,0.35)" stroke-width="1" 
x="1" y="1" width="931" height="340.8" rx="8"/>
+            <g transform="translate(26,22)">
+            <circle cx="0" cy="0" r="7" fill="#ff5f57"/>
+            <circle cx="22" cy="0" r="7" fill="#febc2e"/>
+            <circle cx="44" cy="0" r="7" fill="#28c840"/>
+            </g>
+        
+    <g transform="translate(9, 41)" 
clip-path="url(#terminal-2458333931-clip-terminal)">
+    
+    <g class="terminal-2458333931-matrix">
+    <text class="terminal-2458333931-r1" x="0" y="20" textLength="73.2" 
clip-path="url(#terminal-2458333931-line-0)">Usage:</text><text 
class="terminal-2458333931-r3" x="85.4" y="20" textLength="219.6" 
clip-path="url(#terminal-2458333931-line-0)">airflowctl&#160;plugins</text><text
 class="terminal-2458333931-r2" x="305" y="20" textLength="24.4" 
clip-path="url(#terminal-2458333931-line-0)">&#160;[</text><text 
class="terminal-2458333931-r4" x="329.4" y="20" textLength="24.4" 
clip-path="url [...]
+</text><text class="terminal-2458333931-r2" x="915" y="44.4" textLength="12.2" 
clip-path="url(#terminal-2458333931-line-1)">
+</text><text class="terminal-2458333931-r2" x="0" y="68.8" textLength="317.2" 
clip-path="url(#terminal-2458333931-line-2)">Perform&#160;Plugins&#160;operations</text><text
 class="terminal-2458333931-r2" x="915" y="68.8" textLength="12.2" 
clip-path="url(#terminal-2458333931-line-2)">
+</text><text class="terminal-2458333931-r2" x="915" y="93.2" textLength="12.2" 
clip-path="url(#terminal-2458333931-line-3)">
+</text><text class="terminal-2458333931-r1" x="0" y="117.6" textLength="256.2" 
clip-path="url(#terminal-2458333931-line-4)">Positional&#160;Arguments:</text><text
 class="terminal-2458333931-r2" x="915" y="117.6" textLength="12.2" 
clip-path="url(#terminal-2458333931-line-4)">
+</text><text class="terminal-2458333931-r4" x="24.4" y="142" textLength="85.4" 
clip-path="url(#terminal-2458333931-line-5)">COMMAND</text><text 
class="terminal-2458333931-r2" x="915" y="142" textLength="12.2" 
clip-path="url(#terminal-2458333931-line-5)">
+</text><text class="terminal-2458333931-r4" x="48.8" y="166.4" 
textLength="48.8" clip-path="url(#terminal-2458333931-line-6)">list</text><text 
class="terminal-2458333931-r2" x="268.4" y="166.4" textLength="268.4" 
clip-path="url(#terminal-2458333931-line-6)">Perform&#160;list&#160;operation</text><text
 class="terminal-2458333931-r2" x="915" y="166.4" textLength="12.2" 
clip-path="url(#terminal-2458333931-line-6)">
+</text><text class="terminal-2458333931-r4" x="48.8" y="190.8" 
textLength="219.6" 
clip-path="url(#terminal-2458333931-line-7)">list-import-errors</text><text 
class="terminal-2458333931-r2" x="915" y="190.8" textLength="12.2" 
clip-path="url(#terminal-2458333931-line-7)">
+</text><text class="terminal-2458333931-r2" x="268.4" y="215.2" 
textLength="439.2" 
clip-path="url(#terminal-2458333931-line-8)">Perform&#160;list_import_errors&#160;operation</text><text
 class="terminal-2458333931-r2" x="915" y="215.2" textLength="12.2" 
clip-path="url(#terminal-2458333931-line-8)">
+</text><text class="terminal-2458333931-r2" x="915" y="239.6" 
textLength="12.2" clip-path="url(#terminal-2458333931-line-9)">
+</text><text class="terminal-2458333931-r1" x="0" y="264" textLength="97.6" 
clip-path="url(#terminal-2458333931-line-10)">Options:</text><text 
class="terminal-2458333931-r2" x="915" y="264" textLength="12.2" 
clip-path="url(#terminal-2458333931-line-10)">
+</text><text class="terminal-2458333931-r4" x="24.4" y="288.4" 
textLength="24.4" clip-path="url(#terminal-2458333931-line-11)">-h</text><text 
class="terminal-2458333931-r2" x="48.8" y="288.4" textLength="24.4" 
clip-path="url(#terminal-2458333931-line-11)">,&#160;</text><text 
class="terminal-2458333931-r4" x="73.2" y="288.4" textLength="73.2" 
clip-path="url(#terminal-2458333931-line-11)">--help</text><text 
class="terminal-2458333931-r2" x="268.4" y="288.4" textLength="378.2" 
clip-path="ur [...]
+</text>
+    </g>
+    </g>
+</svg>
diff --git a/airflow-ctl/src/airflowctl/api/client.py 
b/airflow-ctl/src/airflowctl/api/client.py
index 0ef5d7cb164..06e78a6a32a 100644
--- a/airflow-ctl/src/airflowctl/api/client.py
+++ b/airflow-ctl/src/airflowctl/api/client.py
@@ -52,6 +52,7 @@ from airflowctl.api.operations import (
     DagsOperations,
     JobsOperations,
     LoginOperations,
+    PluginsOperations,
     PoolsOperations,
     ProvidersOperations,
     ServerResponseError,
@@ -415,6 +416,12 @@ class Client(httpx.Client):
         """Operations related to XComs."""
         return XComOperations(self)
 
+    @lru_cache()  # type: ignore[prop-decorator]
+    @property
+    def plugins(self):
+        """Operations related to plugins."""
+        return PluginsOperations(self)
+
 
 # API Client Decorator for CLI Actions
 @contextlib.contextmanager
diff --git a/airflow-ctl/src/airflowctl/api/operations.py 
b/airflow-ctl/src/airflowctl/api/operations.py
index 64424eff9c5..8c7b4a1408d 100644
--- a/airflow-ctl/src/airflowctl/api/operations.py
+++ b/airflow-ctl/src/airflowctl/api/operations.py
@@ -59,6 +59,8 @@ from airflowctl.api.datamodels.generated import (
     ImportErrorCollectionResponse,
     ImportErrorResponse,
     JobCollectionResponse,
+    PluginCollectionResponse,
+    PluginImportErrorCollectionResponse,
     PoolBody,
     PoolCollectionResponse,
     PoolPatchBody,
@@ -889,3 +891,19 @@ class XComOperations(BaseOperations):
             return key
         except ServerResponseError as e:
             raise e
+
+
+class PluginsOperations(BaseOperations):
+    """Plugins operations."""
+
+    def list(self) -> PluginCollectionResponse | ServerResponseError:
+        """List all plugins from the API server."""
+        return super().execute_list(path="plugins", 
data_model=PluginCollectionResponse)
+
+    def list_import_errors(self) -> PluginImportErrorCollectionResponse | 
ServerResponseError:
+        """List plugin import errors from the API server."""
+        try:
+            self.response = self.client.get("plugins/importErrors")
+            return 
PluginImportErrorCollectionResponse.model_validate_json(self.response.content)
+        except ServerResponseError as e:
+            raise e
diff --git a/airflow-ctl/tests/airflow_ctl/api/test_operations.py 
b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
index f75a3c9678f..7d875054c3f 100644
--- a/airflow-ctl/tests/airflow_ctl/api/test_operations.py
+++ b/airflow-ctl/tests/airflow_ctl/api/test_operations.py
@@ -79,6 +79,10 @@ from airflowctl.api.datamodels.generated import (
     ImportErrorResponse,
     JobCollectionResponse,
     JobResponse,
+    PluginCollectionResponse,
+    PluginImportErrorCollectionResponse,
+    PluginImportErrorResponse,
+    PluginResponse,
     PoolBody,
     PoolCollectionResponse,
     PoolResponse,
@@ -1703,3 +1707,53 @@ class TestXComOperations:
             map_index=self.map_index,
         )
         assert response == self.key
+
+
+class TestPluginsOperations:
+    plugin_response = PluginResponse(
+        name="test-plugin",
+        macros=[],
+        flask_blueprints=[],
+        fastapi_apps=[],
+        fastapi_root_middlewares=[],
+        external_views=[],
+        react_apps=[],
+        appbuilder_views=[],
+        appbuilder_menu_items=[],
+        global_operator_extra_links=[],
+        operator_extra_links=[],
+        source="test-source",
+        listeners=[],
+        timetables=[],
+    )
+    plugin_collection_response = 
PluginCollectionResponse(plugins=[plugin_response], total_entries=1)
+    plugin_import_error_response = PluginImportErrorResponse(
+        source="plugins/test_plugin.py", error="something went wrong"
+    )
+    plugin_import_error_collection_response = 
PluginImportErrorCollectionResponse(
+        import_errors=[plugin_import_error_response], total_entries=1
+    )
+
+    def test_list(self):
+        """Test listing plugins"""
+
+        def handle_request(request: httpx.Request) -> httpx.Response:
+            assert request.url.path == ("/api/v2/plugins")
+            return httpx.Response(200, 
json=json.loads(self.plugin_collection_response.model_dump_json()))
+
+        client = make_api_client(transport=httpx.MockTransport(handle_request))
+        response = client.plugins.list()
+        assert response == self.plugin_collection_response
+
+    def test_list_import_errors(self):
+        """Test listing plugin import errors"""
+
+        def handle_request(request: httpx.Request) -> httpx.Response:
+            assert request.url.path == "/api/v2/plugins/importErrors"
+            return httpx.Response(
+                200, 
json=json.loads(self.plugin_import_error_collection_response.model_dump_json())
+            )
+
+        client = make_api_client(transport=httpx.MockTransport(handle_request))
+        response = client.plugins.list_import_errors()
+        assert response == self.plugin_import_error_collection_response
diff --git a/scripts/in_container/run_capture_airflowctl_help.py 
b/scripts/in_container/run_capture_airflowctl_help.py
index 3fac46ebd56..9529dbe0439 100644
--- a/scripts/in_container/run_capture_airflowctl_help.py
+++ b/scripts/in_container/run_capture_airflowctl_help.py
@@ -47,6 +47,7 @@ COMMANDS = [
     "providers",
     "variables",
     "version",
+    "plugins",
 ]
 
 SUBCOMMANDS = [


Reply via email to