agentic-fabriq-sdk 0.1.14__tar.gz → 0.1.16__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of agentic-fabriq-sdk might be problematic. Click here for more details.

Files changed (42) hide show
  1. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/PKG-INFO +14 -3
  2. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/README.md +12 -1
  3. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/agents.py +18 -18
  4. agentic_fabriq_sdk-0.1.16/af_cli/commands/applications.py +323 -0
  5. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/auth.py +0 -6
  6. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/config.py +1 -1
  7. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/mcp_servers.py +5 -5
  8. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/secrets.py +6 -6
  9. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/tools.py +163 -89
  10. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/main.py +2 -7
  11. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/__init__.py +15 -0
  12. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/auth/__init__.py +16 -0
  13. agentic_fabriq_sdk-0.1.16/af_sdk/auth/application.py +264 -0
  14. agentic_fabriq_sdk-0.1.16/af_sdk/auth/applications.py +264 -0
  15. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/fabriq_client.py +54 -1
  16. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/pyproject.toml +2 -2
  17. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/__init__.py +0 -0
  18. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/__init__.py +0 -0
  19. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/__init__.py +0 -0
  20. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/client.py +0 -0
  21. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/config.py +0 -0
  22. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/oauth.py +0 -0
  23. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/output.py +0 -0
  24. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/token_storage.py +0 -0
  25. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/auth/dpop.py +0 -0
  26. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/auth/oauth.py +0 -0
  27. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/auth/token_cache.py +0 -0
  28. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/connectors/__init__.py +0 -0
  29. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/connectors/base.py +0 -0
  30. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/connectors/registry.py +0 -0
  31. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/dx/__init__.py +0 -0
  32. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/dx/decorators.py +0 -0
  33. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/dx/runtime.py +0 -0
  34. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/events.py +0 -0
  35. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/exceptions.py +0 -0
  36. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/models/__init__.py +0 -0
  37. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/models/audit.py +0 -0
  38. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/models/types.py +0 -0
  39. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/py.typed +0 -0
  40. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/transport/__init__.py +0 -0
  41. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/transport/http.py +0 -0
  42. {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/vault.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentic-fabriq-sdk
3
- Version: 0.1.14
3
+ Version: 0.1.16
4
4
  Summary: Agentic Fabriq SDK: high-level client, CLI tool, DX helpers, and auth for AI agents
5
5
  License: Apache-2.0
6
6
  Keywords: fabriq,agentic-fabriq,sdk,ai,agents,agentic,fabric,cli
@@ -27,7 +27,7 @@ Requires-Dist: opentelemetry-instrumentation-httpx (>=0.41b0)
27
27
  Requires-Dist: pydantic (>=2.4.0)
28
28
  Requires-Dist: rich (>=13.7.0)
29
29
  Requires-Dist: stevedore (>=5.1.0)
30
- Requires-Dist: typer[all] (>=0.9.0)
30
+ Requires-Dist: typer[all] (>=0.12.0)
31
31
  Requires-Dist: typing-extensions (>=4.0.0)
32
32
  Project-URL: Documentation, https://docs.agentic-fabric.org
33
33
  Project-URL: Homepage, https://github.com/agentic-fabric/agentic-fabric
@@ -84,8 +84,19 @@ BASE = "https://dashboard.agenticfabriq.com"
84
84
 
85
85
  async def main():
86
86
  async with FabriqClient(base_url=BASE, auth_token=TOKEN) as af:
87
+ # List tools and agents
88
+ tools = await af.list_tools()
87
89
  agents = await af.list_agents()
88
- print(agents)
90
+
91
+ # Invoke tools by name (easier!)
92
+ result = await af.invoke_tool("slack", method="get_channels")
93
+
94
+ # Or post a message
95
+ await af.invoke_tool(
96
+ "slack",
97
+ method="post_message",
98
+ parameters={"channel": "test", "text": "Hello from SDK!"}
99
+ )
89
100
  ```
90
101
 
91
102
  DX orchestration:
@@ -48,8 +48,19 @@ BASE = "https://dashboard.agenticfabriq.com"
48
48
 
49
49
  async def main():
50
50
  async with FabriqClient(base_url=BASE, auth_token=TOKEN) as af:
51
+ # List tools and agents
52
+ tools = await af.list_tools()
51
53
  agents = await af.list_agents()
52
- print(agents)
54
+
55
+ # Invoke tools by name (easier!)
56
+ result = await af.invoke_tool("slack", method="get_channels")
57
+
58
+ # Or post a message
59
+ await af.invoke_tool(
60
+ "slack",
61
+ method="post_message",
62
+ parameters={"channel": "test", "text": "Hello from SDK!"}
63
+ )
53
64
  ```
54
65
 
55
66
  DX orchestration:
@@ -14,10 +14,10 @@ app = typer.Typer(help="Agent management commands")
14
14
 
15
15
  @app.command()
16
16
  def list(
17
- page: int = typer.Option(1, "--page", "-p", help="Page number"),
18
- page_size: int = typer.Option(20, "--page-size", "-s", help="Page size"),
19
- search: Optional[str] = typer.Option(None, "--search", "-q", help="Search query"),
20
- format: str = typer.Option("table", "--format", "-f", help="Output format"),
17
+ page: int = typer.Option(1, "--page", help="Page number"),
18
+ page_size: int = typer.Option(20, "--page-size", help="Page size"),
19
+ search: Optional[str] = typer.Option(None, "--search", help="Search query"),
20
+ format: str = typer.Option("table", "--format", help="Output format"),
21
21
  ):
22
22
  """List agents."""
23
23
  try:
@@ -82,7 +82,7 @@ def list(
82
82
  @app.command()
83
83
  def get(
84
84
  agent_id: str = typer.Argument(..., help="Agent ID"),
85
- format: str = typer.Option("table", "--format", "-f", help="Output format"),
85
+ format: str = typer.Option("table", "--format", help="Output format"),
86
86
  ):
87
87
  """Get agent details."""
88
88
  try:
@@ -102,12 +102,12 @@ def get(
102
102
 
103
103
  @app.command()
104
104
  def create(
105
- name: str = typer.Option(..., "--name", "-n", help="Agent name"),
106
- description: Optional[str] = typer.Option(None, "--description", "-d", help="Agent description"),
107
- version: str = typer.Option("1.0.0", "--version", "-v", help="Agent version"),
105
+ name: str = typer.Option(..., "--name", help="Agent name"),
106
+ description: Optional[str] = typer.Option(None, "--description", help="Agent description"),
107
+ version: str = typer.Option("1.0.0", "--version", help="Agent version"),
108
108
  protocol: str = typer.Option("HTTP", "--protocol", help="Agent protocol"),
109
- endpoint_url: str = typer.Option(..., "--endpoint-url", "-u", help="Agent endpoint URL"),
110
- auth_method: str = typer.Option("OAUTH2", "--auth-method", "-a", help="Authentication method"),
109
+ endpoint_url: str = typer.Option(..., "--endpoint-url", help="Agent endpoint URL"),
110
+ auth_method: str = typer.Option("OAUTH2", "--auth-method", help="Authentication method"),
111
111
  ):
112
112
  """Create a new agent."""
113
113
  try:
@@ -135,12 +135,12 @@ def create(
135
135
  @app.command()
136
136
  def update(
137
137
  agent_id: str = typer.Argument(..., help="Agent ID"),
138
- name: Optional[str] = typer.Option(None, "--name", "-n", help="Agent name"),
139
- description: Optional[str] = typer.Option(None, "--description", "-d", help="Agent description"),
140
- version: Optional[str] = typer.Option(None, "--version", "-v", help="Agent version"),
138
+ name: Optional[str] = typer.Option(None, "--name", help="Agent name"),
139
+ description: Optional[str] = typer.Option(None, "--description", help="Agent description"),
140
+ version: Optional[str] = typer.Option(None, "--version", help="Agent version"),
141
141
  protocol: Optional[str] = typer.Option(None, "--protocol", help="Agent protocol"),
142
- endpoint_url: Optional[str] = typer.Option(None, "--endpoint-url", "-u", help="Agent endpoint URL"),
143
- auth_method: Optional[str] = typer.Option(None, "--auth-method", "-a", help="Authentication method"),
142
+ endpoint_url: Optional[str] = typer.Option(None, "--endpoint-url", help="Agent endpoint URL"),
143
+ auth_method: Optional[str] = typer.Option(None, "--auth-method", help="Authentication method"),
144
144
  ):
145
145
  """Update an agent."""
146
146
  try:
@@ -178,7 +178,7 @@ def update(
178
178
  @app.command()
179
179
  def delete(
180
180
  agent_id: str = typer.Argument(..., help="Agent ID"),
181
- force: bool = typer.Option(False, "--force", "-f", help="Force deletion without confirmation"),
181
+ force: bool = typer.Option(False, "--force", help="Force deletion without confirmation"),
182
182
  ):
183
183
  """Delete an agent."""
184
184
  try:
@@ -200,8 +200,8 @@ def delete(
200
200
  @app.command()
201
201
  def invoke(
202
202
  agent_id: str = typer.Argument(..., help="Agent ID"),
203
- input_text: str = typer.Option(..., "--input", "-i", help="Input message for the agent"),
204
- format: str = typer.Option("table", "--format", "-f", help="Output format"),
203
+ input_text: str = typer.Option(..., "--input", help="Input message for the agent"),
204
+ format: str = typer.Option("table", "--format", help="Output format"),
205
205
  ):
206
206
  """Invoke an agent."""
207
207
  try:
@@ -0,0 +1,323 @@
1
+ """
2
+ CLI commands for managing registered applications.
3
+ """
4
+
5
+ import json
6
+ from pathlib import Path
7
+ from typing import Optional
8
+
9
+ import httpx
10
+ import typer
11
+ from rich.console import Console
12
+ from rich.table import Table
13
+
14
+ from af_cli.core.config import get_config
15
+ from af_cli.core.output import print_output
16
+
17
+ app = typer.Typer(help="Manage registered applications")
18
+ console = Console()
19
+
20
+
21
+ @app.command("create")
22
+ def create_application(
23
+ app_id: str = typer.Option(..., "--app-id", help="Application identifier (no spaces)"),
24
+ connections: str = typer.Option(..., "--connections", help="Tool connections (format: 'tool1:conn-id,tool2:conn-id')"),
25
+ scopes: Optional[str] = typer.Option(None, "--scopes", help="Scopes (format: 'scope1,scope2,scope3')"),
26
+ ):
27
+ """
28
+ Register a new application.
29
+
30
+ Example:
31
+ afctl applications create \\
32
+ --app-id my-slack-bot \\
33
+ --connections slack:my-slack-conn,github:my-github-conn \\
34
+ --scopes slack:read,slack:write,github:repo:read
35
+ """
36
+ config = get_config()
37
+
38
+ if not config.is_authenticated():
39
+ console.print("❌ Not authenticated. Run 'afctl auth login' first.", style="red")
40
+ raise typer.Exit(1)
41
+
42
+ # Parse connections
43
+ tool_connections = {}
44
+ if connections:
45
+ for conn_pair in connections.split(","):
46
+ try:
47
+ tool, conn_id = conn_pair.split(":")
48
+ tool_connections[conn_id] = [] # Will add scopes below
49
+ except ValueError:
50
+ console.print(f"❌ Invalid connection format: '{conn_pair}'. Use 'tool:conn-id'", style="red")
51
+ raise typer.Exit(1)
52
+
53
+ # Parse scopes and assign to connections
54
+ if scopes:
55
+ scope_list = [s.strip() for s in scopes.split(",")]
56
+ # For simplicity, assign all scopes to all connections
57
+ # In production, you might want per-connection scopes
58
+ for conn_id in tool_connections:
59
+ tool_connections[conn_id] = scope_list
60
+
61
+ # Make API request
62
+ try:
63
+ response = httpx.post(
64
+ f"{config.gateway_url}/api/v1/applications",
65
+ headers={"Authorization": f"Bearer {config.access_token}"},
66
+ json={
67
+ "app_id": app_id,
68
+ "tool_connections": tool_connections
69
+ },
70
+ timeout=30.0
71
+ )
72
+
73
+ if response.status_code != 201:
74
+ error_detail = response.text
75
+ try:
76
+ error_json = response.json()
77
+ error_detail = error_json.get("detail", response.text)
78
+ except:
79
+ pass
80
+
81
+ console.print(f"❌ Failed to register application: {error_detail}", style="red")
82
+ raise typer.Exit(1)
83
+
84
+ data = response.json()
85
+
86
+ # Save credentials locally
87
+ from af_sdk import save_application_config
88
+
89
+ app_config = {
90
+ "app_id": data["app_id"],
91
+ "secret_key": data["secret_key"],
92
+ "user_id": data["user_id"],
93
+ "tenant_id": data["tenant_id"],
94
+ "tool_connections": data["tool_connections"],
95
+ "created_at": data["created_at"],
96
+ "gateway_url": config.gateway_url
97
+ }
98
+
99
+ app_file = save_application_config(data["app_id"], app_config)
100
+
101
+ # Display success
102
+ console.print("\n✅ Application registered successfully!", style="green bold")
103
+ console.print(f"\n📋 App ID: {data['app_id']}", style="cyan")
104
+ console.print(f"🔑 Secret Key: {data['secret_key']}", style="yellow")
105
+ console.print(f"\n💾 Credentials saved to: {app_file}", style="green")
106
+ console.print("\n⚠️ Save the secret key securely! It won't be shown again.", style="yellow bold")
107
+ console.print("\n🚀 Your agent can now authenticate with:", style="cyan")
108
+ console.print(f" from af_sdk import get_application_client", style="white")
109
+ console.print(f" client = await get_application_client('{data['app_id']}')", style="white")
110
+
111
+ except httpx.HTTPError as e:
112
+ console.print(f"❌ Network error: {e}", style="red")
113
+ raise typer.Exit(1)
114
+
115
+
116
+ @app.command("list")
117
+ def list_applications(
118
+ format: str = typer.Option("table", "--format", help="Output format (table, json, yaml)"),
119
+ ):
120
+ """
121
+ List all registered applications.
122
+
123
+ Shows applications from both:
124
+ - Local config files (~/.af/applications/)
125
+ - Server (via API)
126
+ """
127
+ config = get_config()
128
+
129
+ # Load from local config first
130
+ from af_sdk import list_applications as list_local_apps
131
+
132
+ local_apps = list_local_apps()
133
+
134
+ if format == "table":
135
+ if not local_apps:
136
+ console.print("No applications registered locally.", style="yellow")
137
+ return
138
+
139
+ table = Table(title="Registered Applications")
140
+ table.add_column("App ID", style="cyan")
141
+ table.add_column("Created", style="green")
142
+ table.add_column("Tool Connections", style="magenta")
143
+ table.add_column("Config File", style="white")
144
+
145
+ for app in local_apps:
146
+ conn_count = len(app.get("tool_connections", {}))
147
+ conn_str = f"{conn_count} connection(s)"
148
+ config_file = f"~/.af/applications/{app['app_id']}.json"
149
+
150
+ table.add_row(
151
+ app["app_id"],
152
+ app.get("created_at", "N/A")[:10], # Just date
153
+ conn_str,
154
+ config_file
155
+ )
156
+
157
+ console.print(table)
158
+ console.print(f"\n📊 Total: {len(local_apps)} application(s)")
159
+ else:
160
+ print_output(
161
+ {"applications": local_apps, "total": len(local_apps)},
162
+ format_type=format,
163
+ title="Registered Applications"
164
+ )
165
+
166
+
167
+ @app.command("show")
168
+ def show_application(
169
+ app_id: str = typer.Argument(..., help="Application identifier"),
170
+ reveal_secret: bool = typer.Option(False, "--reveal-secret", help="Reveal the secret key"),
171
+ ):
172
+ """
173
+ Show details of a registered application.
174
+
175
+ Example:
176
+ afctl applications show my-slack-bot
177
+ afctl applications show my-slack-bot --reveal-secret
178
+ """
179
+ from af_sdk import load_application_config, ApplicationNotFoundError
180
+
181
+ try:
182
+ app_config = load_application_config(app_id)
183
+ except ApplicationNotFoundError as e:
184
+ console.print(f"❌ {e}", style="red")
185
+ raise typer.Exit(1)
186
+
187
+ console.print(f"\n📋 Application: {app_config['app_id']}", style="cyan bold")
188
+ console.print(f"👤 User ID: {app_config.get('user_id', 'N/A')}", style="white")
189
+ console.print(f"🏢 Tenant ID: {app_config.get('tenant_id', 'N/A')}", style="white")
190
+ console.print(f"📅 Created: {app_config.get('created_at', 'N/A')}", style="white")
191
+ console.print(f"🌐 Gateway: {app_config.get('gateway_url', 'N/A')}", style="white")
192
+
193
+ if reveal_secret:
194
+ console.print(f"\n🔑 Secret Key: {app_config['secret_key']}", style="yellow bold")
195
+ console.print("⚠️ Keep this secret secure!", style="yellow")
196
+ else:
197
+ console.print(f"\n🔑 Secret Key: ••••••••", style="white")
198
+ console.print(" Use --reveal-secret to show", style="dim")
199
+
200
+ console.print("\n🔌 Tool Connections:", style="cyan bold")
201
+ tool_conns = app_config.get("tool_connections", {})
202
+ if tool_conns:
203
+ for conn_id, scopes in tool_conns.items():
204
+ console.print(f" • {conn_id}", style="white")
205
+ if scopes:
206
+ console.print(f" Scopes: {', '.join(scopes)}", style="dim")
207
+ else:
208
+ console.print(" (none)", style="dim")
209
+
210
+ config_file = Path.home() / ".af" / "applications" / f"{app_id}.json"
211
+ console.print(f"\n💾 Config file: {config_file}", style="green")
212
+
213
+
214
+ @app.command("delete")
215
+ def delete_application(
216
+ app_id: str = typer.Argument(..., help="Application identifier"),
217
+ yes: bool = typer.Option(False, "--yes", help="Skip confirmation"),
218
+ ):
219
+ """
220
+ Delete a registered application.
221
+
222
+ This will:
223
+ - Delete the application registration on the server
224
+ - Remove local credentials
225
+ - Invalidate all active tokens
226
+
227
+ Example:
228
+ afctl applications delete my-slack-bot
229
+ afctl applications delete my-slack-bot --yes
230
+ """
231
+ config = get_config()
232
+
233
+ if not config.is_authenticated():
234
+ console.print("❌ Not authenticated. Run 'afctl auth login' first.", style="red")
235
+ raise typer.Exit(1)
236
+
237
+ # Confirm deletion
238
+ if not yes:
239
+ console.print(f"\n⚠️ This will:", style="yellow bold")
240
+ console.print(f" • Delete the application registration on the server", style="white")
241
+ console.print(f" • Remove local credentials from ~/.af/applications/{app_id}.json", style="white")
242
+ console.print(f" • Invalidate all active tokens for this application", style="white")
243
+
244
+ confirm = typer.confirm(f"\nAre you sure you want to delete '{app_id}'?", default=False)
245
+ if not confirm:
246
+ console.print("❌ Cancelled", style="yellow")
247
+ raise typer.Exit(0)
248
+
249
+ # Delete from server
250
+ try:
251
+ response = httpx.delete(
252
+ f"{config.gateway_url}/api/v1/applications/{app_id}",
253
+ headers={"Authorization": f"Bearer {config.access_token}"},
254
+ timeout=30.0
255
+ )
256
+
257
+ if response.status_code == 404:
258
+ console.print(f"⚠️ Application '{app_id}' not found on server", style="yellow")
259
+ elif response.status_code != 204:
260
+ error_detail = response.text
261
+ try:
262
+ error_json = response.json()
263
+ error_detail = error_json.get("detail", response.text)
264
+ except:
265
+ pass
266
+
267
+ console.print(f"❌ Failed to delete from server: {error_detail}", style="red")
268
+ raise typer.Exit(1)
269
+ else:
270
+ console.print(f"✅ Deleted from server", style="green")
271
+
272
+ except httpx.HTTPError as e:
273
+ console.print(f"❌ Network error: {e}", style="red")
274
+ raise typer.Exit(1)
275
+
276
+ # Delete local config
277
+ from af_sdk import delete_application_config
278
+
279
+ deleted = delete_application_config(app_id)
280
+ if deleted:
281
+ console.print(f"✅ Deleted local credentials", style="green")
282
+ else:
283
+ console.print(f"⚠️ Local credentials not found", style="yellow")
284
+
285
+ console.print(f"\n🎉 Application '{app_id}' deleted successfully", style="green bold")
286
+
287
+
288
+ @app.command("test")
289
+ def test_application(
290
+ app_id: str = typer.Argument(..., help="Application identifier"),
291
+ ):
292
+ """
293
+ Test application authentication.
294
+
295
+ Attempts to exchange credentials for a token to verify the application
296
+ is properly registered and can authenticate.
297
+
298
+ Example:
299
+ afctl applications test my-slack-bot
300
+ """
301
+ import asyncio
302
+ from af_sdk import get_application_client, ApplicationNotFoundError, AuthenticationError
303
+
304
+ async def _test():
305
+ try:
306
+ console.print(f"🔄 Testing authentication for '{app_id}'...", style="cyan")
307
+
308
+ client = await get_application_client(app_id)
309
+
310
+ console.print(f"✅ Authentication successful!", style="green bold")
311
+ console.print(f"\n📋 Application: {client._app_id}", style="cyan")
312
+ console.print(f"⏱️ Token expires in: {client._expires_in} seconds", style="white")
313
+ console.print(f"\n🎉 Your application can authenticate and make API calls!", style="green")
314
+
315
+ except ApplicationNotFoundError as e:
316
+ console.print(f"❌ {e}", style="red")
317
+ raise typer.Exit(1)
318
+ except AuthenticationError as e:
319
+ console.print(f"❌ {e}", style="red")
320
+ raise typer.Exit(1)
321
+
322
+ asyncio.run(_test())
323
+
@@ -51,18 +51,15 @@ def login(
51
51
  tenant_id: Optional[str] = typer.Option(
52
52
  None,
53
53
  "--tenant-id",
54
- "-t",
55
54
  help="Tenant ID (optional, can be extracted from JWT)"
56
55
  ),
57
56
  browser: bool = typer.Option(
58
57
  True,
59
- "--browser/--no-browser",
60
58
  help="Open browser for authentication"
61
59
  ),
62
60
  keycloak_url: Optional[str] = typer.Option(
63
61
  None,
64
62
  "--keycloak-url",
65
- "-k",
66
63
  help="Keycloak URL (default: https://auth.agenticfabriq.com or from config)"
67
64
  ),
68
65
  ):
@@ -142,7 +139,6 @@ def logout(
142
139
  keycloak_url: Optional[str] = typer.Option(
143
140
  None,
144
141
  "--keycloak-url",
145
- "-k",
146
142
  help="Keycloak URL (default: https://auth.agenticfabriq.com or from config)"
147
143
  ),
148
144
  ):
@@ -254,7 +250,6 @@ def refresh(
254
250
  keycloak_url: Optional[str] = typer.Option(
255
251
  None,
256
252
  "--keycloak-url",
257
- "-k",
258
253
  help="Keycloak URL (default: https://auth.agenticfabriq.com or from config)"
259
254
  ),
260
255
  ):
@@ -320,7 +315,6 @@ def token(
320
315
  show_full: bool = typer.Option(
321
316
  False,
322
317
  "--full",
323
- "-f",
324
318
  help="Show full token (warning: sensitive information)"
325
319
  ),
326
320
  ):
@@ -12,7 +12,7 @@ app = typer.Typer(help="Configuration commands")
12
12
 
13
13
  @app.command()
14
14
  def show(
15
- format: str = typer.Option("table", "--format", "-f", help="Output format"),
15
+ format: str = typer.Option("table", "--format", help="Output format"),
16
16
  ):
17
17
  """Show current configuration."""
18
18
  import os
@@ -12,7 +12,7 @@ app = typer.Typer(help="MCP server management commands")
12
12
 
13
13
  @app.command()
14
14
  def list(
15
- format: str = typer.Option("table", "--format", "-f", help="Output format"),
15
+ format: str = typer.Option("table", "--format", help="Output format"),
16
16
  ):
17
17
  """List MCP servers."""
18
18
  try:
@@ -38,7 +38,7 @@ def list(
38
38
  @app.command()
39
39
  def get(
40
40
  server_id: str = typer.Argument(..., help="MCP server ID"),
41
- format: str = typer.Option("table", "--format", "-f", help="Output format"),
41
+ format: str = typer.Option("table", "--format", help="Output format"),
42
42
  ):
43
43
  """Get MCP server details."""
44
44
  try:
@@ -58,9 +58,9 @@ def get(
58
58
 
59
59
  @app.command()
60
60
  def create(
61
- name: str = typer.Option(..., "--name", "-n", help="MCP server name"),
62
- base_url: str = typer.Option(..., "--base-url", "-u", help="Base URL"),
63
- auth_type: str = typer.Option("API_KEY", "--auth-type", "-a", help="Authentication type"),
61
+ name: str = typer.Option(..., "--name", help="MCP server name"),
62
+ base_url: str = typer.Option(..., "--base-url", help="Base URL"),
63
+ auth_type: str = typer.Option("API_KEY", "--auth-type", help="Authentication type"),
64
64
  ):
65
65
  """Create a new MCP server."""
66
66
  try:
@@ -13,7 +13,7 @@ app = typer.Typer(help="Secret management commands")
13
13
  @app.command()
14
14
  def get(
15
15
  path: str = typer.Argument(..., help="Secret path"),
16
- format: str = typer.Option("table", "--format", "-f", help="Output format"),
16
+ format: str = typer.Option("table", "--format", help="Output format"),
17
17
  ):
18
18
  """Get a secret."""
19
19
  try:
@@ -42,8 +42,8 @@ def get(
42
42
  @app.command()
43
43
  def create(
44
44
  path: str = typer.Argument(..., help="Secret path"),
45
- value: str = typer.Option(..., "--value", "-v", help="Secret value"),
46
- description: str = typer.Option("", "--description", "-d", help="Secret description"),
45
+ value: str = typer.Option(..., "--value", help="Secret value"),
46
+ description: str = typer.Option("", "--description", help="Secret description"),
47
47
  ):
48
48
  """Create a new secret."""
49
49
  try:
@@ -66,8 +66,8 @@ def create(
66
66
  @app.command()
67
67
  def update(
68
68
  path: str = typer.Argument(..., help="Secret path"),
69
- value: str = typer.Option(..., "--value", "-v", help="Secret value"),
70
- description: str = typer.Option("", "--description", "-d", help="Secret description"),
69
+ value: str = typer.Option(..., "--value", help="Secret value"),
70
+ description: str = typer.Option("", "--description", help="Secret description"),
71
71
  ):
72
72
  """Update a secret."""
73
73
  try:
@@ -90,7 +90,7 @@ def update(
90
90
  @app.command()
91
91
  def delete(
92
92
  path: str = typer.Argument(..., help="Secret path"),
93
- force: bool = typer.Option(False, "--force", "-f", help="Force deletion"),
93
+ force: bool = typer.Option(False, "--force", help="Force deletion"),
94
94
  ):
95
95
  """Delete a secret."""
96
96
  try: