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.
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/PKG-INFO +14 -3
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/README.md +12 -1
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/agents.py +18 -18
- agentic_fabriq_sdk-0.1.16/af_cli/commands/applications.py +323 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/auth.py +0 -6
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/config.py +1 -1
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/mcp_servers.py +5 -5
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/secrets.py +6 -6
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/tools.py +163 -89
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/main.py +2 -7
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/__init__.py +15 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/auth/__init__.py +16 -0
- agentic_fabriq_sdk-0.1.16/af_sdk/auth/application.py +264 -0
- agentic_fabriq_sdk-0.1.16/af_sdk/auth/applications.py +264 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/fabriq_client.py +54 -1
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/pyproject.toml +2 -2
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/commands/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/client.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/config.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/oauth.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/output.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_cli/core/token_storage.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/auth/dpop.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/auth/oauth.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/auth/token_cache.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/connectors/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/connectors/base.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/connectors/registry.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/dx/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/dx/decorators.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/dx/runtime.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/events.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/exceptions.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/models/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/models/audit.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/models/types.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/py.typed +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/transport/__init__.py +0 -0
- {agentic_fabriq_sdk-0.1.14 → agentic_fabriq_sdk-0.1.16}/af_sdk/transport/http.py +0 -0
- {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.
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
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",
|
|
18
|
-
page_size: int = typer.Option(20, "--page-size",
|
|
19
|
-
search: Optional[str] = typer.Option(None, "--search",
|
|
20
|
-
format: str = typer.Option("table", "--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",
|
|
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",
|
|
106
|
-
description: Optional[str] = typer.Option(None, "--description",
|
|
107
|
-
version: str = typer.Option("1.0.0", "--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",
|
|
110
|
-
auth_method: str = typer.Option("OAUTH2", "--auth-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",
|
|
139
|
-
description: Optional[str] = typer.Option(None, "--description",
|
|
140
|
-
version: Optional[str] = typer.Option(None, "--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",
|
|
143
|
-
auth_method: Optional[str] = typer.Option(None, "--auth-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",
|
|
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",
|
|
204
|
-
format: str = typer.Option("table", "--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",
|
|
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",
|
|
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",
|
|
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",
|
|
62
|
-
base_url: str = typer.Option(..., "--base-url",
|
|
63
|
-
auth_type: str = typer.Option("API_KEY", "--auth-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",
|
|
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",
|
|
46
|
-
description: str = typer.Option("", "--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",
|
|
70
|
-
description: str = typer.Option("", "--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",
|
|
93
|
+
force: bool = typer.Option(False, "--force", help="Force deletion"),
|
|
94
94
|
):
|
|
95
95
|
"""Delete a secret."""
|
|
96
96
|
try:
|