agentic-fabriq-sdk 0.1.24__py3-none-any.whl → 0.1.25__py3-none-any.whl
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.
- af_cli/__init__.py +11 -2
- af_cli/commands/applications.py +40 -5
- af_cli/commands/auth.py +8 -7
- af_cli/commands/config.py +37 -11
- af_cli/commands/tools.py +47 -16
- af_cli/core/client.py +6 -1
- af_cli/core/config.py +2 -1
- af_cli/main.py +6 -10
- af_sdk/connectors/__init__.py +0 -2
- af_sdk/connectors/base.py +1 -42
- af_sdk/dx/__init__.py +1 -2
- af_sdk/dx/runtime.py +0 -22
- af_sdk/fabriq_client.py +0 -38
- af_sdk/models/__init__.py +0 -4
- af_sdk/models/types.py +0 -22
- {agentic_fabriq_sdk-0.1.24.dist-info → agentic_fabriq_sdk-0.1.25.dist-info}/METADATA +1 -1
- {agentic_fabriq_sdk-0.1.24.dist-info → agentic_fabriq_sdk-0.1.25.dist-info}/RECORD +19 -21
- af_cli/commands/mcp_servers.py +0 -83
- af_cli/commands/secrets.py +0 -109
- {agentic_fabriq_sdk-0.1.24.dist-info → agentic_fabriq_sdk-0.1.25.dist-info}/WHEEL +0 -0
- {agentic_fabriq_sdk-0.1.24.dist-info → agentic_fabriq_sdk-0.1.25.dist-info}/entry_points.txt +0 -0
af_cli/__init__.py
CHANGED
|
@@ -2,7 +2,16 @@
|
|
|
2
2
|
Agentic Fabric CLI Tool
|
|
3
3
|
|
|
4
4
|
A command-line interface for managing Agentic Fabric resources including
|
|
5
|
-
agents, tools,
|
|
5
|
+
agents, tools, and administrative operations.
|
|
6
6
|
"""
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
try:
|
|
9
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
10
|
+
try:
|
|
11
|
+
__version__ = version("agentic-fabriq-sdk")
|
|
12
|
+
except PackageNotFoundError:
|
|
13
|
+
# Fallback if package is not installed
|
|
14
|
+
__version__ = "0.0.0-dev"
|
|
15
|
+
except ImportError:
|
|
16
|
+
# Fallback for Python < 3.8 (though we require 3.11+)
|
|
17
|
+
__version__ = "0.0.0-dev"
|
af_cli/commands/applications.py
CHANGED
|
@@ -190,24 +190,59 @@ def connect_application(
|
|
|
190
190
|
@app.command("list")
|
|
191
191
|
def list_applications(
|
|
192
192
|
format: str = typer.Option("table", "--format", help="Output format (table, json, yaml)"),
|
|
193
|
+
sync: bool = typer.Option(True, "--sync/--no-sync", help="Sync with server and remove orphaned local files"),
|
|
193
194
|
):
|
|
194
195
|
"""
|
|
195
196
|
List all registered applications.
|
|
196
197
|
|
|
197
|
-
Shows applications from
|
|
198
|
-
|
|
199
|
-
|
|
198
|
+
Shows applications from local config files (~/.af/applications/) and optionally
|
|
199
|
+
syncs with the server to remove any local files for applications that have been
|
|
200
|
+
deleted from the server (e.g., via the UI).
|
|
200
201
|
"""
|
|
201
202
|
config = get_config()
|
|
202
203
|
|
|
203
204
|
# Load from local config first
|
|
204
|
-
from af_sdk import list_applications as list_local_apps
|
|
205
|
+
from af_sdk import list_applications as list_local_apps, delete_application_config
|
|
205
206
|
|
|
206
207
|
local_apps = list_local_apps()
|
|
207
208
|
|
|
209
|
+
# If sync is enabled and user is authenticated, check server and clean up orphans
|
|
210
|
+
if sync and config.is_authenticated():
|
|
211
|
+
try:
|
|
212
|
+
response = httpx.get(
|
|
213
|
+
f"{config.gateway_url}/api/v1/applications",
|
|
214
|
+
headers={"Authorization": f"Bearer {config.access_token}"},
|
|
215
|
+
timeout=30.0
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
if response.status_code == 200:
|
|
219
|
+
data = response.json()
|
|
220
|
+
server_apps = data.get("applications", [])
|
|
221
|
+
server_app_ids = {app["app_id"] for app in server_apps}
|
|
222
|
+
|
|
223
|
+
# Find and remove orphaned local files
|
|
224
|
+
orphaned = []
|
|
225
|
+
for local_app in local_apps[:]: # Copy list to modify during iteration
|
|
226
|
+
if local_app["app_id"] not in server_app_ids:
|
|
227
|
+
orphaned.append(local_app["app_id"])
|
|
228
|
+
# Delete orphaned local config
|
|
229
|
+
delete_application_config(local_app["app_id"])
|
|
230
|
+
# Remove from local_apps list
|
|
231
|
+
local_apps.remove(local_app)
|
|
232
|
+
|
|
233
|
+
if orphaned:
|
|
234
|
+
console.print(f"🧹 Cleaned up {len(orphaned)} orphaned local file(s): {', '.join(orphaned)}", style="yellow")
|
|
235
|
+
|
|
236
|
+
except httpx.HTTPError as e:
|
|
237
|
+
# If server check fails, just show local apps with a warning
|
|
238
|
+
console.print(f"⚠️ Could not sync with server: {e}", style="yellow")
|
|
239
|
+
except Exception as e:
|
|
240
|
+
# Silently continue if sync fails
|
|
241
|
+
pass
|
|
242
|
+
|
|
208
243
|
if format == "table":
|
|
209
244
|
if not local_apps:
|
|
210
|
-
console.print("No applications registered
|
|
245
|
+
console.print("No applications registered.", style="yellow")
|
|
211
246
|
return
|
|
212
247
|
|
|
213
248
|
table = Table(title="Registered Applications")
|
af_cli/commands/auth.py
CHANGED
|
@@ -53,15 +53,16 @@ def login(
|
|
|
53
53
|
"--tenant-id",
|
|
54
54
|
help="Tenant ID (optional, can be extracted from JWT)"
|
|
55
55
|
),
|
|
56
|
-
browser: bool = typer.Option(
|
|
57
|
-
True,
|
|
58
|
-
help="Open browser for authentication"
|
|
59
|
-
),
|
|
60
56
|
keycloak_url: Optional[str] = typer.Option(
|
|
61
57
|
None,
|
|
62
58
|
"--keycloak-url",
|
|
63
59
|
help="Keycloak URL (default: https://auth.agenticfabriq.com or from config)"
|
|
64
60
|
),
|
|
61
|
+
yes: bool = typer.Option(
|
|
62
|
+
False,
|
|
63
|
+
"--yes",
|
|
64
|
+
help="Skip confirmation when already authenticated"
|
|
65
|
+
),
|
|
65
66
|
):
|
|
66
67
|
"""
|
|
67
68
|
Login to Agentic Fabric using OAuth2/PKCE flow.
|
|
@@ -80,7 +81,7 @@ def login(
|
|
|
80
81
|
info(f"Already authenticated as: {user_display}")
|
|
81
82
|
info(f"Tenant: {existing_token.tenant_id or 'Unknown'}")
|
|
82
83
|
|
|
83
|
-
if not typer.confirm("Do you want to login again?"):
|
|
84
|
+
if not yes and not typer.confirm("Do you want to login again?"):
|
|
84
85
|
return
|
|
85
86
|
|
|
86
87
|
try:
|
|
@@ -89,8 +90,8 @@ def login(
|
|
|
89
90
|
|
|
90
91
|
# Perform login
|
|
91
92
|
console.print()
|
|
92
|
-
#
|
|
93
|
-
tokens = oauth_client.login(open_browser=
|
|
93
|
+
# Always open browser for authentication
|
|
94
|
+
tokens = oauth_client.login(open_browser=True, timeout=300, use_hosted_callback=False)
|
|
94
95
|
|
|
95
96
|
# Extract and save token data
|
|
96
97
|
token_data = token_storage.extract_token_info(tokens)
|
af_cli/commands/config.py
CHANGED
|
@@ -12,12 +12,14 @@ app = typer.Typer(help="Configuration commands")
|
|
|
12
12
|
|
|
13
13
|
@app.command()
|
|
14
14
|
def show(
|
|
15
|
-
format: str = typer.Option(
|
|
15
|
+
format: str = typer.Option(None, "--format", help="Output format (overrides configured default)"),
|
|
16
16
|
):
|
|
17
17
|
"""Show current configuration."""
|
|
18
|
-
import os
|
|
19
18
|
config = get_config()
|
|
20
19
|
|
|
20
|
+
# Use provided format, or fall back to configured output_format
|
|
21
|
+
display_format = format if format else config.output_format
|
|
22
|
+
|
|
21
23
|
config_data = {
|
|
22
24
|
"gateway_url": config.gateway_url,
|
|
23
25
|
"keycloak_url": config.keycloak_url,
|
|
@@ -25,13 +27,12 @@ def show(
|
|
|
25
27
|
"authenticated": "Yes" if config.is_authenticated() else "No",
|
|
26
28
|
"config_file": config.config_file,
|
|
27
29
|
"output_format": config.output_format,
|
|
28
|
-
"
|
|
29
|
-
"env_keycloak_url": os.getenv("AF_KEYCLOAK_URL", "Not set"),
|
|
30
|
+
"page_size": config.page_size,
|
|
30
31
|
}
|
|
31
32
|
|
|
32
33
|
print_output(
|
|
33
34
|
config_data,
|
|
34
|
-
format_type=
|
|
35
|
+
format_type=display_format,
|
|
35
36
|
title="Configuration"
|
|
36
37
|
)
|
|
37
38
|
|
|
@@ -44,15 +45,20 @@ def set(
|
|
|
44
45
|
"""Set configuration value."""
|
|
45
46
|
config = get_config()
|
|
46
47
|
|
|
48
|
+
# Note: tenant_id is not settable - it comes from authentication
|
|
47
49
|
valid_keys = {
|
|
48
50
|
"gateway_url": "gateway_url",
|
|
49
|
-
"
|
|
51
|
+
"keycloak_url": "keycloak_url",
|
|
50
52
|
"output_format": "output_format",
|
|
53
|
+
"page_size": "page_size",
|
|
51
54
|
}
|
|
52
55
|
|
|
53
56
|
if key not in valid_keys:
|
|
54
57
|
error(f"Invalid configuration key: {key}")
|
|
55
58
|
error(f"Valid keys: {', '.join(valid_keys.keys())}")
|
|
59
|
+
if key == "tenant_id":
|
|
60
|
+
error("Note: tenant_id cannot be set manually. It comes from authentication.")
|
|
61
|
+
error("Run 'afctl auth login' to authenticate with a specific tenant.")
|
|
56
62
|
raise typer.Exit(1)
|
|
57
63
|
|
|
58
64
|
# Set the value
|
|
@@ -69,27 +75,46 @@ def get(
|
|
|
69
75
|
"""Get configuration value."""
|
|
70
76
|
config = get_config()
|
|
71
77
|
|
|
78
|
+
# All readable config keys
|
|
72
79
|
valid_keys = {
|
|
73
80
|
"gateway_url": "gateway_url",
|
|
81
|
+
"keycloak_url": "keycloak_url",
|
|
82
|
+
"keycloak_realm": "keycloak_realm",
|
|
83
|
+
"keycloak_client_id": "keycloak_client_id",
|
|
74
84
|
"tenant_id": "tenant_id",
|
|
75
85
|
"output_format": "output_format",
|
|
86
|
+
"page_size": "page_size",
|
|
87
|
+
"config_file": "config_file",
|
|
88
|
+
"verbose": "verbose",
|
|
89
|
+
"authenticated": "is_authenticated", # Special: calls method
|
|
76
90
|
}
|
|
77
91
|
|
|
78
92
|
if key not in valid_keys:
|
|
79
93
|
error(f"Invalid configuration key: {key}")
|
|
80
|
-
error(f"Valid keys: {', '.join(valid_keys.keys())}")
|
|
94
|
+
error(f"Valid keys: {', '.join(sorted(valid_keys.keys()))}")
|
|
81
95
|
raise typer.Exit(1)
|
|
82
96
|
|
|
83
|
-
|
|
84
|
-
|
|
97
|
+
# Handle special keys that are methods
|
|
98
|
+
if key == "authenticated":
|
|
99
|
+
value = "Yes" if config.is_authenticated() else "No"
|
|
100
|
+
else:
|
|
101
|
+
value = getattr(config, valid_keys[key])
|
|
102
|
+
|
|
103
|
+
# Display value
|
|
104
|
+
if value is None:
|
|
105
|
+
info(f"{key}: (not set)")
|
|
106
|
+
else:
|
|
107
|
+
info(f"{key}: {value}")
|
|
85
108
|
|
|
86
109
|
|
|
87
110
|
@app.command()
|
|
88
|
-
def reset(
|
|
111
|
+
def reset(
|
|
112
|
+
yes: bool = typer.Option(False, "--yes", help="Skip confirmation"),
|
|
113
|
+
):
|
|
89
114
|
"""Reset configuration to defaults."""
|
|
90
115
|
config = get_config()
|
|
91
116
|
|
|
92
|
-
if not typer.confirm("Are you sure you want to reset configuration to defaults?"):
|
|
117
|
+
if not yes and not typer.confirm("Are you sure you want to reset configuration to defaults?"):
|
|
93
118
|
info("Reset cancelled")
|
|
94
119
|
return
|
|
95
120
|
|
|
@@ -100,6 +125,7 @@ def reset():
|
|
|
100
125
|
config.gateway_url = "https://dashboard.agenticfabriq.com"
|
|
101
126
|
config.tenant_id = None
|
|
102
127
|
config.output_format = "table"
|
|
128
|
+
config.page_size = 20
|
|
103
129
|
|
|
104
130
|
config.save()
|
|
105
131
|
|
af_cli/commands/tools.py
CHANGED
|
@@ -15,7 +15,7 @@ app = typer.Typer(help="Tool management commands")
|
|
|
15
15
|
def list(
|
|
16
16
|
format: str = typer.Option("table", "--format", help="Output format"),
|
|
17
17
|
page: int = typer.Option(1, "--page", help="Page number (starts from 1)", min=1),
|
|
18
|
-
page_size: int = typer.Option(
|
|
18
|
+
page_size: int = typer.Option(None, "--page-size", help="Number of items per page (1-100). When specified, becomes the new default.", min=1, max=100),
|
|
19
19
|
search: str = typer.Option(None, "--search", help="Search query (searches tool IDs from registry like 'gmail', 'slack', and user connection names)"),
|
|
20
20
|
tool_filter: str = typer.Option(None, "--tool", help="Filter by tool type from registry (e.g., 'gmail' shows Gmail connections, 'google' shows all Google tools)"),
|
|
21
21
|
):
|
|
@@ -75,22 +75,41 @@ def list(
|
|
|
75
75
|
- Combines with search and pagination
|
|
76
76
|
"""
|
|
77
77
|
try:
|
|
78
|
+
from af_cli.core.config import get_config
|
|
79
|
+
config = get_config()
|
|
80
|
+
|
|
81
|
+
# Use provided page_size, or fall back to configured default
|
|
82
|
+
if page_size is None:
|
|
83
|
+
page_size = config.page_size
|
|
84
|
+
else:
|
|
85
|
+
# Save the new page_size as default
|
|
86
|
+
config.page_size = page_size
|
|
87
|
+
config.save()
|
|
88
|
+
|
|
78
89
|
with get_client() as client:
|
|
79
|
-
# Build query parameters
|
|
80
|
-
params = {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
params["page_size"] = page_size
|
|
90
|
+
# Build query parameters - always include all params for clarity
|
|
91
|
+
params = {
|
|
92
|
+
"page": page,
|
|
93
|
+
"page_size": page_size,
|
|
94
|
+
}
|
|
85
95
|
if search:
|
|
86
96
|
params["search"] = search
|
|
87
97
|
if tool_filter:
|
|
88
98
|
params["tool_filter"] = tool_filter
|
|
89
|
-
|
|
99
|
+
|
|
100
|
+
debug(f"Requesting connections with params: {params}")
|
|
90
101
|
connections = client.get("/api/v1/user-connections", params=params)
|
|
102
|
+
|
|
103
|
+
debug(f"Received {len(connections) if connections else 0} connections from API")
|
|
91
104
|
|
|
92
105
|
if not connections:
|
|
93
|
-
if
|
|
106
|
+
if page > 1:
|
|
107
|
+
# User requested a page beyond available data
|
|
108
|
+
error(f"Page {page} is out of range.")
|
|
109
|
+
info(f"Try 'afctl tools list --page 1' to see available connections.")
|
|
110
|
+
if search or tool_filter:
|
|
111
|
+
info(f"Or adjust your search/filter criteria to see more results.")
|
|
112
|
+
elif search or tool_filter:
|
|
94
113
|
warning(f"No tool connections found matching your criteria. Try adjusting your search or filter.")
|
|
95
114
|
else:
|
|
96
115
|
warning("No tool connections found. Add connections in the dashboard UI.")
|
|
@@ -114,7 +133,7 @@ def list(
|
|
|
114
133
|
"Added": conn.get("created_at", "N/A")[:10] if conn.get("created_at") else "N/A",
|
|
115
134
|
})
|
|
116
135
|
|
|
117
|
-
# Show pagination
|
|
136
|
+
# Show pagination and filter info
|
|
118
137
|
total_info = ""
|
|
119
138
|
if page != 1 or page_size != 20 or search or tool_filter:
|
|
120
139
|
total_info = f" (Page {page}, {len(connections)} shown"
|
|
@@ -123,6 +142,15 @@ def list(
|
|
|
123
142
|
if tool_filter:
|
|
124
143
|
total_info += f", Tool: '{tool_filter}'"
|
|
125
144
|
total_info += ")"
|
|
145
|
+
|
|
146
|
+
# Add summary info if filters are active
|
|
147
|
+
if search or tool_filter:
|
|
148
|
+
filter_parts = []
|
|
149
|
+
if search:
|
|
150
|
+
filter_parts.append(f"search='{search}'")
|
|
151
|
+
if tool_filter:
|
|
152
|
+
filter_parts.append(f"tool='{tool_filter}'")
|
|
153
|
+
info(f"🔍 Filtered results: {' AND '.join(filter_parts)}")
|
|
126
154
|
|
|
127
155
|
print_output(
|
|
128
156
|
display_data,
|
|
@@ -132,9 +160,10 @@ def list(
|
|
|
132
160
|
|
|
133
161
|
# Show helpful tips for pagination
|
|
134
162
|
if len(connections) == page_size:
|
|
135
|
-
info(f"💡
|
|
136
|
-
|
|
137
|
-
|
|
163
|
+
info(f"💡 Showing {len(connections)} results. Use --page {page + 1} to see more results")
|
|
164
|
+
|
|
165
|
+
if not search and not tool_filter:
|
|
166
|
+
info(f"💡 Tip: Use --search <term> to search, or --tool <type> to filter by tool type")
|
|
138
167
|
|
|
139
168
|
except Exception as e:
|
|
140
169
|
error(f"Failed to list tool connections: {e}")
|
|
@@ -463,6 +492,7 @@ def add(
|
|
|
463
492
|
@app.command()
|
|
464
493
|
def connect(
|
|
465
494
|
connection_id: str = typer.Argument(..., help="Connection ID to connect"),
|
|
495
|
+
yes: bool = typer.Option(False, "--yes", help="Skip confirmation when reconnecting"),
|
|
466
496
|
):
|
|
467
497
|
"""Complete OAuth connection (open browser for authorization)."""
|
|
468
498
|
try:
|
|
@@ -490,9 +520,10 @@ def connect(
|
|
|
490
520
|
# Check if connection is already set up (has credentials stored)
|
|
491
521
|
if connection.get("connected"):
|
|
492
522
|
warning(f"Connection '{connection_id}' is already connected")
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
523
|
+
if not yes:
|
|
524
|
+
confirm = typer.confirm("Do you want to reconnect (re-authorize)?")
|
|
525
|
+
if not confirm:
|
|
526
|
+
return
|
|
496
527
|
|
|
497
528
|
# Determine the API base tool name
|
|
498
529
|
# - Google tools all use "google" for API credentials, "google_oauth" for oauth3
|
af_cli/core/client.py
CHANGED
|
@@ -63,7 +63,12 @@ class AFClient:
|
|
|
63
63
|
def get(self, path: str, params: Optional[Dict] = None) -> Dict[str, Any]:
|
|
64
64
|
"""Make GET request."""
|
|
65
65
|
url = urljoin(self.config.gateway_url, path)
|
|
66
|
-
|
|
66
|
+
if params:
|
|
67
|
+
# Show params in debug output
|
|
68
|
+
param_str = "&".join(f"{k}={v}" for k, v in params.items())
|
|
69
|
+
debug(f"GET {url}?{param_str}")
|
|
70
|
+
else:
|
|
71
|
+
debug(f"GET {url}")
|
|
67
72
|
|
|
68
73
|
response = self.client.get(
|
|
69
74
|
path,
|
af_cli/core/config.py
CHANGED
|
@@ -13,7 +13,7 @@ from pydantic import BaseModel, Field
|
|
|
13
13
|
class CLIConfig(BaseModel):
|
|
14
14
|
"""CLI configuration model."""
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
# Gateway settings (support environment variable override)
|
|
17
17
|
gateway_url: str = Field(
|
|
18
18
|
default_factory=lambda: os.getenv("AF_GATEWAY_URL", "https://dashboard.agenticfabriq.com"),
|
|
19
19
|
description="Gateway URL"
|
|
@@ -39,6 +39,7 @@ class CLIConfig(BaseModel):
|
|
|
39
39
|
config_file: str = Field(default="", description="Configuration file path")
|
|
40
40
|
verbose: bool = Field(default=False, description="Verbose output")
|
|
41
41
|
output_format: str = Field(default="table", description="Output format (table, json, yaml)")
|
|
42
|
+
page_size: int = Field(default=20, description="Default page size for list commands")
|
|
42
43
|
|
|
43
44
|
def __init__(self, **data):
|
|
44
45
|
super().__init__(**data)
|
af_cli/main.py
CHANGED
|
@@ -12,8 +12,6 @@ from rich.table import Table
|
|
|
12
12
|
from af_cli.commands.applications import app as applications_app
|
|
13
13
|
from af_cli.commands.auth import app as auth_app
|
|
14
14
|
from af_cli.commands.config import app as config_app
|
|
15
|
-
from af_cli.commands.mcp_servers import app as mcp_servers_app
|
|
16
|
-
from af_cli.commands.secrets import app as secrets_app
|
|
17
15
|
from af_cli.commands.tools import app as tools_app
|
|
18
16
|
from af_cli.core.config import get_config
|
|
19
17
|
from af_cli.core.output import success, error, info
|
|
@@ -31,8 +29,6 @@ app.add_typer(auth_app, name="auth", help="Authentication commands")
|
|
|
31
29
|
app.add_typer(config_app, name="config", help="Configuration commands")
|
|
32
30
|
app.add_typer(tools_app, name="tools", help="Tool management commands")
|
|
33
31
|
app.add_typer(applications_app, name="applications", help="Application management commands")
|
|
34
|
-
app.add_typer(mcp_servers_app, name="mcp-servers", help="MCP server management commands")
|
|
35
|
-
app.add_typer(secrets_app, name="secrets", help="Secret management commands")
|
|
36
32
|
|
|
37
33
|
|
|
38
34
|
@app.command()
|
|
@@ -92,19 +88,19 @@ def init(
|
|
|
92
88
|
"--tenant-id",
|
|
93
89
|
help="Tenant ID"
|
|
94
90
|
),
|
|
95
|
-
|
|
91
|
+
yes: bool = typer.Option(
|
|
96
92
|
False,
|
|
97
|
-
"--
|
|
98
|
-
help="
|
|
93
|
+
"--yes",
|
|
94
|
+
help="Skip confirmation and overwrite existing config"
|
|
99
95
|
),
|
|
100
96
|
):
|
|
101
97
|
"""Initialize CLI configuration."""
|
|
102
98
|
config = get_config()
|
|
103
99
|
|
|
104
100
|
# Check if config exists
|
|
105
|
-
if os.path.exists(config.config_file) and not
|
|
101
|
+
if os.path.exists(config.config_file) and not yes:
|
|
106
102
|
error(f"Configuration already exists at {config.config_file}")
|
|
107
|
-
error("Use --
|
|
103
|
+
error("Use --yes to overwrite")
|
|
108
104
|
raise typer.Exit(1)
|
|
109
105
|
|
|
110
106
|
# Create config directory if it doesn't exist
|
|
@@ -152,7 +148,7 @@ def main(
|
|
|
152
148
|
"""
|
|
153
149
|
Agentic Fabric CLI - Manage your connectivity hub.
|
|
154
150
|
|
|
155
|
-
The CLI provides commands for managing
|
|
151
|
+
The CLI provides commands for managing tool connections and applications
|
|
156
152
|
in your Agentic Fabric deployment.
|
|
157
153
|
"""
|
|
158
154
|
# Configure global options
|
af_sdk/connectors/__init__.py
CHANGED
|
@@ -7,7 +7,6 @@ from .base import (
|
|
|
7
7
|
BaseConnector,
|
|
8
8
|
ConnectorContext,
|
|
9
9
|
HTTPConnectorMixin,
|
|
10
|
-
MCPConnector,
|
|
11
10
|
ToolConnector,
|
|
12
11
|
)
|
|
13
12
|
from .registry import ConnectorRegistry
|
|
@@ -16,7 +15,6 @@ __all__ = [
|
|
|
16
15
|
"BaseConnector",
|
|
17
16
|
"ToolConnector",
|
|
18
17
|
"AgentConnector",
|
|
19
|
-
"MCPConnector",
|
|
20
18
|
"ConnectorContext",
|
|
21
19
|
"HTTPConnectorMixin",
|
|
22
20
|
"ConnectorRegistry",
|
af_sdk/connectors/base.py
CHANGED
|
@@ -105,7 +105,7 @@ class ToolConnector(BaseConnector):
|
|
|
105
105
|
|
|
106
106
|
|
|
107
107
|
class AgentConnector(BaseConnector):
|
|
108
|
-
"""Base class for agent connectors that speak A2A
|
|
108
|
+
"""Base class for agent connectors that speak A2A."""
|
|
109
109
|
|
|
110
110
|
AGENT_ID: str = ""
|
|
111
111
|
|
|
@@ -144,47 +144,6 @@ class AgentConnector(BaseConnector):
|
|
|
144
144
|
return True
|
|
145
145
|
|
|
146
146
|
|
|
147
|
-
class MCPConnector(BaseConnector):
|
|
148
|
-
"""Base class for MCP (Model Context Protocol) connectors."""
|
|
149
|
-
|
|
150
|
-
MCP_VERSION: str = "1.0"
|
|
151
|
-
SERVER_NAME: str = ""
|
|
152
|
-
|
|
153
|
-
def __init__(self, ctx: ConnectorContext):
|
|
154
|
-
super().__init__(ctx)
|
|
155
|
-
if not self.SERVER_NAME:
|
|
156
|
-
raise ConnectorError("SERVER_NAME must be set in MCP connector subclass")
|
|
157
|
-
|
|
158
|
-
@abc.abstractmethod
|
|
159
|
-
async def list_tools(self) -> list[Dict[str, Any]]:
|
|
160
|
-
"""List available tools from the MCP server."""
|
|
161
|
-
pass
|
|
162
|
-
|
|
163
|
-
@abc.abstractmethod
|
|
164
|
-
async def call_tool(self, tool_name: str, arguments: Dict[str, Any]) -> Any:
|
|
165
|
-
"""Call a specific tool on the MCP server."""
|
|
166
|
-
pass
|
|
167
|
-
|
|
168
|
-
async def invoke(self, method: str, **kwargs) -> Any:
|
|
169
|
-
"""Generic invoke method for MCP connectors."""
|
|
170
|
-
if method == "list_tools":
|
|
171
|
-
return await self.list_tools()
|
|
172
|
-
elif method == "call_tool":
|
|
173
|
-
return await self.call_tool(
|
|
174
|
-
kwargs.get("tool_name", ""), kwargs.get("arguments", {})
|
|
175
|
-
)
|
|
176
|
-
else:
|
|
177
|
-
raise ConnectorError(f"Unknown method: {method}")
|
|
178
|
-
|
|
179
|
-
async def get_server_info(self) -> Dict[str, Any]:
|
|
180
|
-
"""Get MCP server information."""
|
|
181
|
-
return {
|
|
182
|
-
"name": self.SERVER_NAME,
|
|
183
|
-
"version": self.MCP_VERSION,
|
|
184
|
-
"tools": await self.list_tools(),
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
|
|
188
147
|
class HTTPConnectorMixin:
|
|
189
148
|
"""Mixin for connectors that make HTTP requests."""
|
|
190
149
|
|
af_sdk/dx/__init__.py
CHANGED
af_sdk/dx/runtime.py
CHANGED
|
@@ -40,25 +40,3 @@ class ToolFabric:
|
|
|
40
40
|
r.raise_for_status()
|
|
41
41
|
return r.json()
|
|
42
42
|
|
|
43
|
-
|
|
44
|
-
class MCPServer:
|
|
45
|
-
"""Facade for an MCP server registered in Fabriq (proxy layer)."""
|
|
46
|
-
|
|
47
|
-
def __init__(self, *, server_id: str, base_url: str, access_token: str, tenant_id: Optional[str] = None):
|
|
48
|
-
self.server_id = server_id
|
|
49
|
-
self.base_url = base_url.rstrip("/")
|
|
50
|
-
self.token = access_token
|
|
51
|
-
self.tenant_id = tenant_id
|
|
52
|
-
|
|
53
|
-
def get_tools(self, names: List[str]) -> List[str]:
|
|
54
|
-
return [f"mcp:{self.server_id}:{name}" for name in names]
|
|
55
|
-
|
|
56
|
-
def invoke(self, tool: str, args: Dict[str, Any]) -> Dict[str, Any]:
|
|
57
|
-
url = f"{self.base_url}/api/v1/proxy/mcp/{self.server_id}/invoke"
|
|
58
|
-
payload = {"payload": {"tool": tool, "args": args}}
|
|
59
|
-
with httpx.Client(timeout=60.0) as c:
|
|
60
|
-
r = c.post(url, json=payload, headers=_base_headers(self.token, self.tenant_id))
|
|
61
|
-
r.raise_for_status()
|
|
62
|
-
return r.json()
|
|
63
|
-
|
|
64
|
-
|
af_sdk/fabriq_client.py
CHANGED
|
@@ -3,7 +3,6 @@ FabriqClient: High-level async helper for Agentic Fabric Gateway.
|
|
|
3
3
|
|
|
4
4
|
This client wraps common Gateway API flows so agent developers can:
|
|
5
5
|
- List and invoke tools
|
|
6
|
-
- Register and invoke MCP servers (via proxy)
|
|
7
6
|
- Invoke agents (via proxy)
|
|
8
7
|
- Manage per-user secrets via the Gateway-backed Vault API
|
|
9
8
|
|
|
@@ -120,43 +119,6 @@ class FabriqClient:
|
|
|
120
119
|
)
|
|
121
120
|
return r.json()
|
|
122
121
|
|
|
123
|
-
# -----------------
|
|
124
|
-
# MCP Servers
|
|
125
|
-
# -----------------
|
|
126
|
-
async def register_mcp_server(
|
|
127
|
-
self,
|
|
128
|
-
*,
|
|
129
|
-
name: str,
|
|
130
|
-
base_url: str,
|
|
131
|
-
description: Optional[str] = None,
|
|
132
|
-
auth_type: str = "API_KEY",
|
|
133
|
-
source: str = "STATIC",
|
|
134
|
-
metadata: Optional[Dict[str, Any]] = None,
|
|
135
|
-
) -> Dict[str, Any]:
|
|
136
|
-
body: Dict[str, Any] = {
|
|
137
|
-
"name": name,
|
|
138
|
-
"base_url": base_url,
|
|
139
|
-
"auth_type": auth_type,
|
|
140
|
-
"source": source,
|
|
141
|
-
}
|
|
142
|
-
if description is not None:
|
|
143
|
-
body["description"] = description
|
|
144
|
-
if metadata is not None:
|
|
145
|
-
body["metadata"] = metadata
|
|
146
|
-
r = await self._http.post("/mcp/servers", json=body, headers=self._extra_headers)
|
|
147
|
-
return r.json()
|
|
148
|
-
|
|
149
|
-
async def list_mcp_servers(self, *, page: int = 1, page_size: int = 20, search: Optional[str] = None) -> Dict[str, Any]:
|
|
150
|
-
params: Dict[str, Any] = {"page": page, "page_size": page_size}
|
|
151
|
-
if search:
|
|
152
|
-
params["search"] = search
|
|
153
|
-
r = await self._http.get("/mcp/servers", params=params, headers=self._extra_headers)
|
|
154
|
-
return r.json()
|
|
155
|
-
|
|
156
|
-
async def invoke_mcp(self, *, server_id: str, payload: Dict[str, Any], raw: bool = False) -> Dict[str, Any]:
|
|
157
|
-
r = await self._http.post(f"/proxy/mcp/{server_id}/invoke", json={"payload": payload, "raw": raw}, headers=self._extra_headers)
|
|
158
|
-
return r.json()
|
|
159
|
-
|
|
160
122
|
# -----------------
|
|
161
123
|
# Secrets (Gateway-backed Vault)
|
|
162
124
|
# -----------------
|
af_sdk/models/__init__.py
CHANGED
|
@@ -5,8 +5,6 @@ Data models for Agentic Fabric SDK.
|
|
|
5
5
|
from .types import (
|
|
6
6
|
ErrorResponse,
|
|
7
7
|
HealthResponse,
|
|
8
|
-
McpServer,
|
|
9
|
-
McpServerCreate,
|
|
10
8
|
MetricsResponse,
|
|
11
9
|
OAuthToken,
|
|
12
10
|
PaginatedResponse,
|
|
@@ -24,8 +22,6 @@ __all__ = [
|
|
|
24
22
|
"Tool",
|
|
25
23
|
"ToolInvokeRequest",
|
|
26
24
|
"ToolInvokeResult",
|
|
27
|
-
"McpServer",
|
|
28
|
-
"McpServerCreate",
|
|
29
25
|
"Secret",
|
|
30
26
|
"SecretMetadata",
|
|
31
27
|
"SecretPutRequest",
|
af_sdk/models/types.py
CHANGED
|
@@ -75,28 +75,6 @@ class ToolInvokeResult(BaseModel):
|
|
|
75
75
|
headers: Dict[str, str] = Field(default_factory=dict)
|
|
76
76
|
|
|
77
77
|
|
|
78
|
-
class McpServer(BaseModel):
|
|
79
|
-
"""MCP Server model."""
|
|
80
|
-
|
|
81
|
-
id: UUID
|
|
82
|
-
base_url: str
|
|
83
|
-
auth_type: str = Field(description="One of: API_KEY, MTLS, OIDC")
|
|
84
|
-
source: str = Field(description="One of: STATIC, DISCOVERED, SLACK, CONFIG")
|
|
85
|
-
tenant_id: str
|
|
86
|
-
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
87
|
-
created_at: datetime
|
|
88
|
-
updated_at: datetime
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
class McpServerCreate(BaseModel):
|
|
92
|
-
"""Request model for creating an MCP server."""
|
|
93
|
-
|
|
94
|
-
base_url: str
|
|
95
|
-
auth_type: str = Field(description="One of: API_KEY, MTLS, OIDC")
|
|
96
|
-
source: str = Field(description="One of: STATIC, DISCOVERED, SLACK, CONFIG")
|
|
97
|
-
metadata: Dict[str, Any] = Field(default_factory=dict)
|
|
98
|
-
|
|
99
|
-
|
|
100
78
|
class Secret(BaseModel):
|
|
101
79
|
"""Secret model."""
|
|
102
80
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: agentic-fabriq-sdk
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.25
|
|
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
|
|
@@ -1,41 +1,39 @@
|
|
|
1
|
-
af_cli/__init__.py,sha256=
|
|
1
|
+
af_cli/__init__.py,sha256=87n70iK0TzFfSj5h4xhi6Pgfizx5IWZbGwv-nNiF-uo,506
|
|
2
2
|
af_cli/commands/__init__.py,sha256=Qngm2Yks6oTKazlxCZA_tIAHXwHoU6Oc7Pw5BGkA7W4,49
|
|
3
|
-
af_cli/commands/applications.py,sha256
|
|
4
|
-
af_cli/commands/auth.py,sha256=
|
|
5
|
-
af_cli/commands/config.py,sha256=
|
|
6
|
-
af_cli/commands/
|
|
7
|
-
af_cli/commands/secrets.py,sha256=z_rHfn8-KEonTC5dioBgH7sc1ppMOltFVFz-58Nqwa0,3436
|
|
8
|
-
af_cli/commands/tools.py,sha256=CRtxGzMfm9etq9SLrC3v_MvBNIh3Myw4xblv5aMVTL0,30828
|
|
3
|
+
af_cli/commands/applications.py,sha256=-54W1H2XtdzqJe1wEHKv2hHrvQZ1ixyVf5w_eOezGJ0,17360
|
|
4
|
+
af_cli/commands/auth.py,sha256=Or-3gUczyTCyScuWu6YdYk-IRbvWhaJV2437FB-HWOw,11981
|
|
5
|
+
af_cli/commands/config.py,sha256=GtAPWd5tLoZaUeZN8aDrfid4Vgspoif3mTi1ZJ58dQE,3827
|
|
6
|
+
af_cli/commands/tools.py,sha256=r26i81kiOpLh2C87eS5F9x-KEWR3sL1J0MpVsZ2Y644,32310
|
|
9
7
|
af_cli/core/__init__.py,sha256=cQ0H7rGfaMISQPhoNe4Xfu_EKU2TqRVt2OMI7tPea5U,51
|
|
10
|
-
af_cli/core/client.py,sha256=
|
|
11
|
-
af_cli/core/config.py,sha256=
|
|
8
|
+
af_cli/core/client.py,sha256=Emn1xw3qJ1f0ghryyn1smNEmQDVJGjvaiM1RnzBmtCg,5277
|
|
9
|
+
af_cli/core/config.py,sha256=dNibuz5vr1SiY34gDTph56jZ3N-aKAXAZamnqcqol_4,8632
|
|
12
10
|
af_cli/core/oauth.py,sha256=sCzo97TZyx8sLmo0GYv53P3zoABK6htRCivHhRpPRdI,18162
|
|
13
11
|
af_cli/core/output.py,sha256=tL5z7M-tLu2M1Yl8O4M7OPP7iQivC1b_YUUB468Od5Y,4811
|
|
14
12
|
af_cli/core/token_storage.py,sha256=WhOQLx5_9pn2lAlFX01Y5DmWO7B9BJYfEkASCsBQwaI,7738
|
|
15
|
-
af_cli/main.py,sha256=
|
|
13
|
+
af_cli/main.py,sha256=bCS-EviJAKuXYKLiBbUXcqBgPeGVtvoqehcb-4A7kAY,4800
|
|
16
14
|
af_sdk/__init__.py,sha256=uKLl3Sqlg_TgmGZtI0Tt14JNXIhdHSb6dplw6acBDvU,1569
|
|
17
15
|
af_sdk/auth/__init__.py,sha256=laIiIwgxy0mEgRoXEakoEs7BuUYnyrlzHJbdf8Sqa5E,1137
|
|
18
16
|
af_sdk/auth/applications.py,sha256=Tc5j47S5WWGFPOhsjEI40SZuCd5ynzzAohy1NS0DyAM,7474
|
|
19
17
|
af_sdk/auth/dpop.py,sha256=s0uiyxxuzsVQNtSexji1htJoxrALwlf1P9507xa-M3Y,1285
|
|
20
18
|
af_sdk/auth/oauth.py,sha256=iAXrOYRTWUqo2eEzX_gRmzHZRTzdo3DrZwxOeYFAZ28,8284
|
|
21
19
|
af_sdk/auth/token_cache.py,sha256=X36E6K0lWqMAqlJXC3i343y8oy-uFm1q-FEdVKXdL1Y,11300
|
|
22
|
-
af_sdk/connectors/__init__.py,sha256=
|
|
23
|
-
af_sdk/connectors/base.py,sha256=
|
|
20
|
+
af_sdk/connectors/__init__.py,sha256=nUpqljyeq2TAO0Jffwk3NaXf5Wgujex8mo-em-qGAsw,373
|
|
21
|
+
af_sdk/connectors/base.py,sha256=TwQ2E-CrHbFeb0yKD93Yhf9zYrym6cqs7oO-k3k3dFw,6430
|
|
24
22
|
af_sdk/connectors/registry.py,sha256=ZH0wYIZBqDnTWJ_IhfwZzifO5r3Rkb0VlEyXDhrGWIY,8799
|
|
25
|
-
af_sdk/dx/__init__.py,sha256=
|
|
23
|
+
af_sdk/dx/__init__.py,sha256=dgAWKjnpH1-PQ17y_3slI3tWyHZs5on8J_boHTjYaBM,104
|
|
26
24
|
af_sdk/dx/decorators.py,sha256=o_EmvE_8pp2vTgMJMgfTy5SXG_24yabuKdoytah02Hk,1294
|
|
27
|
-
af_sdk/dx/runtime.py,sha256=
|
|
25
|
+
af_sdk/dx/runtime.py,sha256=nRqoUoqXvzY48wobkh0C0fkJY9ng3PcB3zxb164DW1g,1589
|
|
28
26
|
af_sdk/events.py,sha256=onz1wWwATpm71HFfcEJLhvXBDSmE0M9RC92lMS-Sd3k,22609
|
|
29
27
|
af_sdk/exceptions.py,sha256=ZVjjBeq17CGK69N2OTkVTjPXqXSI_gA7cZk9rCvARcI,4381
|
|
30
|
-
af_sdk/fabriq_client.py,sha256=
|
|
31
|
-
af_sdk/models/__init__.py,sha256=
|
|
28
|
+
af_sdk/fabriq_client.py,sha256=Ne83OcD3bijYKhf09G8LIjz6XB9mYMKboZWUecl6p4I,6090
|
|
29
|
+
af_sdk/models/__init__.py,sha256=n1JnbCXY6VCT-qKbx-u1ggTjDSUT_K7XEc1eADFR4qA,631
|
|
32
30
|
af_sdk/models/audit.py,sha256=_wRahNV7M7ftc2AHFf7J3WzIJ5cUyZhFn_lZX9NITp8,1476
|
|
33
|
-
af_sdk/models/types.py,sha256=
|
|
31
|
+
af_sdk/models/types.py,sha256=osZjbrQAVnhh89RSWDYpxzaHEtkuXZ33vL5pkAocqz0,4420
|
|
34
32
|
af_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
35
33
|
af_sdk/transport/__init__.py,sha256=HsOc6MmlxIS-PSYC_-6E36-dZYyT_auZeoXvGzVAqeg,104
|
|
36
34
|
af_sdk/transport/http.py,sha256=QB3eqQbwug95QHf5PG_714NKtlTjV9PzVTo8izJCylc,13203
|
|
37
35
|
af_sdk/vault.py,sha256=QVNGigIw8ND5sVXt05gvUY222b5-i9EbzLWNsDGdOU4,17926
|
|
38
|
-
agentic_fabriq_sdk-0.1.
|
|
39
|
-
agentic_fabriq_sdk-0.1.
|
|
40
|
-
agentic_fabriq_sdk-0.1.
|
|
41
|
-
agentic_fabriq_sdk-0.1.
|
|
36
|
+
agentic_fabriq_sdk-0.1.25.dist-info/METADATA,sha256=wh72ugR9EmsaBEPGec3QyzYv45y-pw4lg7h-OfU8kKY,4259
|
|
37
|
+
agentic_fabriq_sdk-0.1.25.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
38
|
+
agentic_fabriq_sdk-0.1.25.dist-info/entry_points.txt,sha256=XUO2EaJhUtUS5pwVIkhemC-nEeFbKgXXLW97gQCCGm8,41
|
|
39
|
+
agentic_fabriq_sdk-0.1.25.dist-info/RECORD,,
|
af_cli/commands/mcp_servers.py
DELETED
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
MCP server management commands for the Agentic Fabric CLI.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import typer
|
|
6
|
-
|
|
7
|
-
from af_cli.core.client import get_client
|
|
8
|
-
from af_cli.core.output import error, info, print_output, success, warning
|
|
9
|
-
|
|
10
|
-
app = typer.Typer(help="MCP server management commands")
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@app.command()
|
|
14
|
-
def list(
|
|
15
|
-
format: str = typer.Option("table", "--format", help="Output format"),
|
|
16
|
-
):
|
|
17
|
-
"""List MCP servers."""
|
|
18
|
-
try:
|
|
19
|
-
with get_client() as client:
|
|
20
|
-
response = client.get("/api/v1/mcp-servers")
|
|
21
|
-
servers = response["servers"]
|
|
22
|
-
|
|
23
|
-
if not servers:
|
|
24
|
-
warning("No MCP servers found")
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
print_output(
|
|
28
|
-
servers,
|
|
29
|
-
format_type=format,
|
|
30
|
-
title="MCP Servers"
|
|
31
|
-
)
|
|
32
|
-
|
|
33
|
-
except Exception as e:
|
|
34
|
-
error(f"Failed to list MCP servers: {e}")
|
|
35
|
-
raise typer.Exit(1)
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
@app.command()
|
|
39
|
-
def get(
|
|
40
|
-
server_id: str = typer.Argument(..., help="MCP server ID"),
|
|
41
|
-
format: str = typer.Option("table", "--format", help="Output format"),
|
|
42
|
-
):
|
|
43
|
-
"""Get MCP server details."""
|
|
44
|
-
try:
|
|
45
|
-
with get_client() as client:
|
|
46
|
-
server = client.get(f"/api/v1/mcp-servers/{server_id}")
|
|
47
|
-
|
|
48
|
-
print_output(
|
|
49
|
-
server,
|
|
50
|
-
format_type=format,
|
|
51
|
-
title=f"MCP Server {server_id}"
|
|
52
|
-
)
|
|
53
|
-
|
|
54
|
-
except Exception as e:
|
|
55
|
-
error(f"Failed to get MCP server: {e}")
|
|
56
|
-
raise typer.Exit(1)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
@app.command()
|
|
60
|
-
def create(
|
|
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
|
-
):
|
|
65
|
-
"""Create a new MCP server."""
|
|
66
|
-
try:
|
|
67
|
-
with get_client() as client:
|
|
68
|
-
data = {
|
|
69
|
-
"name": name,
|
|
70
|
-
"base_url": base_url,
|
|
71
|
-
"auth_type": auth_type,
|
|
72
|
-
"source": "STATIC",
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
server = client.post("/api/v1/mcp-servers", data)
|
|
76
|
-
|
|
77
|
-
success(f"MCP server created: {server['id']}")
|
|
78
|
-
info(f"Name: {server['name']}")
|
|
79
|
-
info(f"Base URL: {server['base_url']}")
|
|
80
|
-
|
|
81
|
-
except Exception as e:
|
|
82
|
-
error(f"Failed to create MCP server: {e}")
|
|
83
|
-
raise typer.Exit(1)
|
af_cli/commands/secrets.py
DELETED
|
@@ -1,109 +0,0 @@
|
|
|
1
|
-
"""
|
|
2
|
-
Secret management commands for the Agentic Fabric CLI.
|
|
3
|
-
"""
|
|
4
|
-
|
|
5
|
-
import typer
|
|
6
|
-
|
|
7
|
-
from af_cli.core.client import get_client
|
|
8
|
-
from af_cli.core.output import error, info, print_output, success
|
|
9
|
-
|
|
10
|
-
app = typer.Typer(help="Secret management commands")
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
@app.command()
|
|
14
|
-
def get(
|
|
15
|
-
path: str = typer.Argument(..., help="Secret path"),
|
|
16
|
-
format: str = typer.Option("table", "--format", help="Output format"),
|
|
17
|
-
):
|
|
18
|
-
"""Get a secret."""
|
|
19
|
-
try:
|
|
20
|
-
with get_client() as client:
|
|
21
|
-
secret = client.get(f"/api/v1/secrets/{path}")
|
|
22
|
-
|
|
23
|
-
# Don't display the actual secret value in table format
|
|
24
|
-
if format == "table":
|
|
25
|
-
display_data = {
|
|
26
|
-
"path": secret["path"],
|
|
27
|
-
"description": secret.get("description", ""),
|
|
28
|
-
"version": secret["version"],
|
|
29
|
-
"created_at": secret["created_at"],
|
|
30
|
-
"updated_at": secret["updated_at"],
|
|
31
|
-
}
|
|
32
|
-
print_output(display_data, format_type=format, title=f"Secret {path}")
|
|
33
|
-
info("Use --format=json to see the secret value")
|
|
34
|
-
else:
|
|
35
|
-
print_output(secret, format_type=format)
|
|
36
|
-
|
|
37
|
-
except Exception as e:
|
|
38
|
-
error(f"Failed to get secret: {e}")
|
|
39
|
-
raise typer.Exit(1)
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
@app.command()
|
|
43
|
-
def create(
|
|
44
|
-
path: str = typer.Argument(..., help="Secret path"),
|
|
45
|
-
value: str = typer.Option(..., "--value", help="Secret value"),
|
|
46
|
-
description: str = typer.Option("", "--description", help="Secret description"),
|
|
47
|
-
):
|
|
48
|
-
"""Create a new secret."""
|
|
49
|
-
try:
|
|
50
|
-
with get_client() as client:
|
|
51
|
-
data = {
|
|
52
|
-
"value": value,
|
|
53
|
-
"description": description,
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
secret = client.post(f"/api/v1/secrets/{path}", data)
|
|
57
|
-
|
|
58
|
-
success(f"Secret created: {secret['path']}")
|
|
59
|
-
info(f"Version: {secret['version']}")
|
|
60
|
-
|
|
61
|
-
except Exception as e:
|
|
62
|
-
error(f"Failed to create secret: {e}")
|
|
63
|
-
raise typer.Exit(1)
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
@app.command()
|
|
67
|
-
def update(
|
|
68
|
-
path: str = typer.Argument(..., help="Secret path"),
|
|
69
|
-
value: str = typer.Option(..., "--value", help="Secret value"),
|
|
70
|
-
description: str = typer.Option("", "--description", help="Secret description"),
|
|
71
|
-
):
|
|
72
|
-
"""Update a secret."""
|
|
73
|
-
try:
|
|
74
|
-
with get_client() as client:
|
|
75
|
-
data = {
|
|
76
|
-
"value": value,
|
|
77
|
-
"description": description,
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
secret = client.put(f"/api/v1/secrets/{path}", data)
|
|
81
|
-
|
|
82
|
-
success(f"Secret updated: {secret['path']}")
|
|
83
|
-
info(f"Version: {secret['version']}")
|
|
84
|
-
|
|
85
|
-
except Exception as e:
|
|
86
|
-
error(f"Failed to update secret: {e}")
|
|
87
|
-
raise typer.Exit(1)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
@app.command()
|
|
91
|
-
def delete(
|
|
92
|
-
path: str = typer.Argument(..., help="Secret path"),
|
|
93
|
-
force: bool = typer.Option(False, "--force", help="Force deletion"),
|
|
94
|
-
):
|
|
95
|
-
"""Delete a secret."""
|
|
96
|
-
try:
|
|
97
|
-
if not force:
|
|
98
|
-
if not typer.confirm(f"Are you sure you want to delete secret {path}?"):
|
|
99
|
-
info("Deletion cancelled")
|
|
100
|
-
return
|
|
101
|
-
|
|
102
|
-
with get_client() as client:
|
|
103
|
-
client.delete(f"/api/v1/secrets/{path}")
|
|
104
|
-
|
|
105
|
-
success(f"Secret deleted: {path}")
|
|
106
|
-
|
|
107
|
-
except Exception as e:
|
|
108
|
-
error(f"Failed to delete secret: {e}")
|
|
109
|
-
raise typer.Exit(1)
|
|
File without changes
|
{agentic_fabriq_sdk-0.1.24.dist-info → agentic_fabriq_sdk-0.1.25.dist-info}/entry_points.txt
RENAMED
|
File without changes
|