agentic-fabriq-sdk 0.1.14__py3-none-any.whl → 0.1.16__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/commands/agents.py +18 -18
- af_cli/commands/applications.py +323 -0
- af_cli/commands/auth.py +0 -6
- af_cli/commands/config.py +1 -1
- af_cli/commands/mcp_servers.py +5 -5
- af_cli/commands/secrets.py +6 -6
- af_cli/commands/tools.py +163 -89
- af_cli/main.py +2 -7
- af_sdk/__init__.py +15 -0
- af_sdk/auth/__init__.py +16 -0
- af_sdk/auth/application.py +264 -0
- af_sdk/auth/applications.py +264 -0
- af_sdk/fabriq_client.py +54 -1
- {agentic_fabriq_sdk-0.1.14.dist-info → agentic_fabriq_sdk-0.1.16.dist-info}/METADATA +14 -3
- {agentic_fabriq_sdk-0.1.14.dist-info → agentic_fabriq_sdk-0.1.16.dist-info}/RECORD +17 -14
- {agentic_fabriq_sdk-0.1.14.dist-info → agentic_fabriq_sdk-0.1.16.dist-info}/WHEEL +0 -0
- {agentic_fabriq_sdk-0.1.14.dist-info → agentic_fabriq_sdk-0.1.16.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authentication helpers for Agentic Fabric SDK.
|
|
3
|
+
|
|
4
|
+
Provides utilities for loading application credentials and creating
|
|
5
|
+
authenticated clients.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import json
|
|
10
|
+
import httpx
|
|
11
|
+
from typing import Optional, List, Dict
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
from .fabriq_client import FabriqClient
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ApplicationNotFoundError(Exception):
|
|
20
|
+
"""Raised when an application configuration is not found."""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AuthenticationError(Exception):
|
|
25
|
+
"""Raised when authentication fails."""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def get_application_client(
|
|
30
|
+
app_id: str,
|
|
31
|
+
config_dir: Optional[Path] = None,
|
|
32
|
+
gateway_url: Optional[str] = None,
|
|
33
|
+
) -> FabriqClient:
|
|
34
|
+
"""
|
|
35
|
+
Get authenticated FabriqClient for an application.
|
|
36
|
+
|
|
37
|
+
Automatically loads credentials from ~/.af/applications/{app_id}.json
|
|
38
|
+
and exchanges them for a JWT token.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
app_id: Application identifier (e.g., "my-slack-bot")
|
|
42
|
+
config_dir: Optional custom config directory (default: ~/.af)
|
|
43
|
+
gateway_url: Optional gateway URL override (default: from app config)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Authenticated FabriqClient instance
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
ApplicationNotFoundError: If application config doesn't exist
|
|
50
|
+
AuthenticationError: If authentication fails
|
|
51
|
+
|
|
52
|
+
Example:
|
|
53
|
+
>>> client = await get_application_client("my-slack-bot")
|
|
54
|
+
>>> result = await client.invoke_tool("slack-uuid", "post_message", {...})
|
|
55
|
+
"""
|
|
56
|
+
# 1. Load application config
|
|
57
|
+
try:
|
|
58
|
+
app_config = load_application_config(app_id, config_dir)
|
|
59
|
+
except FileNotFoundError as e:
|
|
60
|
+
raise ApplicationNotFoundError(
|
|
61
|
+
f"Application '{app_id}' not found. "
|
|
62
|
+
f"Register it first with: afctl applications create --app-id {app_id} ..."
|
|
63
|
+
) from e
|
|
64
|
+
|
|
65
|
+
# Use provided gateway_url or fall back to config
|
|
66
|
+
base_url = gateway_url or app_config.get("gateway_url", "https://dashboard.agenticfabriq.com")
|
|
67
|
+
|
|
68
|
+
# 2. Exchange credentials for JWT token
|
|
69
|
+
try:
|
|
70
|
+
async with httpx.AsyncClient() as http:
|
|
71
|
+
response = await http.post(
|
|
72
|
+
f"{base_url}/api/v1/applications/token",
|
|
73
|
+
json={
|
|
74
|
+
"app_id": app_config["app_id"],
|
|
75
|
+
"secret_key": app_config["secret_key"]
|
|
76
|
+
},
|
|
77
|
+
timeout=30.0
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if response.status_code != 200:
|
|
81
|
+
error_detail = response.text
|
|
82
|
+
try:
|
|
83
|
+
error_json = response.json()
|
|
84
|
+
error_detail = error_json.get("detail", response.text)
|
|
85
|
+
except:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
raise AuthenticationError(
|
|
89
|
+
f"Failed to authenticate application '{app_id}': {error_detail}"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
token_data = response.json()
|
|
93
|
+
except httpx.HTTPError as e:
|
|
94
|
+
raise AuthenticationError(
|
|
95
|
+
f"Network error while authenticating application '{app_id}': {e}"
|
|
96
|
+
) from e
|
|
97
|
+
|
|
98
|
+
# 3. Create and return authenticated client
|
|
99
|
+
client = FabriqClient(
|
|
100
|
+
base_url=base_url,
|
|
101
|
+
auth_token=token_data["access_token"]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Store metadata for potential refresh
|
|
105
|
+
client._app_id = app_id
|
|
106
|
+
client._expires_in = token_data.get("expires_in", 86400)
|
|
107
|
+
|
|
108
|
+
logger.info(
|
|
109
|
+
f"Authenticated as application '{app_id}' "
|
|
110
|
+
f"(user_id={token_data.get('user_id')}, tenant_id={token_data.get('tenant_id')})"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return client
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def load_application_config(
|
|
117
|
+
app_id: str,
|
|
118
|
+
config_dir: Optional[Path] = None
|
|
119
|
+
) -> Dict:
|
|
120
|
+
"""
|
|
121
|
+
Load application config from disk.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
app_id: Application identifier
|
|
125
|
+
config_dir: Optional custom config directory (default: ~/.af)
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Application configuration dictionary
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
FileNotFoundError: If application config doesn't exist
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
>>> config = load_application_config("my-slack-bot")
|
|
135
|
+
>>> print(config["app_id"], config["created_at"])
|
|
136
|
+
"""
|
|
137
|
+
if config_dir is None:
|
|
138
|
+
config_dir = Path.home() / ".af"
|
|
139
|
+
|
|
140
|
+
app_file = config_dir / "applications" / f"{app_id}.json"
|
|
141
|
+
|
|
142
|
+
if not app_file.exists():
|
|
143
|
+
raise FileNotFoundError(
|
|
144
|
+
f"Application '{app_id}' not found at {app_file}. "
|
|
145
|
+
f"Register it with: afctl applications create --app-id {app_id}"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
with open(app_file, "r") as f:
|
|
149
|
+
return json.load(f)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def save_application_config(
|
|
153
|
+
app_id: str,
|
|
154
|
+
config: Dict,
|
|
155
|
+
config_dir: Optional[Path] = None
|
|
156
|
+
) -> Path:
|
|
157
|
+
"""
|
|
158
|
+
Save application config to disk.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
app_id: Application identifier
|
|
162
|
+
config: Application configuration dictionary
|
|
163
|
+
config_dir: Optional custom config directory (default: ~/.af)
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Path to saved config file
|
|
167
|
+
|
|
168
|
+
Example:
|
|
169
|
+
>>> config = {
|
|
170
|
+
... "app_id": "my-bot",
|
|
171
|
+
... "secret_key": "sk_...",
|
|
172
|
+
... "gateway_url": "https://dashboard.agenticfabriq.com"
|
|
173
|
+
... }
|
|
174
|
+
>>> path = save_application_config("my-bot", config)
|
|
175
|
+
"""
|
|
176
|
+
if config_dir is None:
|
|
177
|
+
config_dir = Path.home() / ".af"
|
|
178
|
+
|
|
179
|
+
# Create applications directory if it doesn't exist
|
|
180
|
+
app_dir = config_dir / "applications"
|
|
181
|
+
app_dir.mkdir(parents=True, exist_ok=True)
|
|
182
|
+
|
|
183
|
+
# Write config file
|
|
184
|
+
app_file = app_dir / f"{app_id}.json"
|
|
185
|
+
with open(app_file, "w") as f:
|
|
186
|
+
json.dump(config, f, indent=2)
|
|
187
|
+
|
|
188
|
+
# Secure the file (user read/write only)
|
|
189
|
+
app_file.chmod(0o600)
|
|
190
|
+
|
|
191
|
+
logger.info(f"Saved application config to {app_file}")
|
|
192
|
+
|
|
193
|
+
return app_file
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def list_applications(
|
|
197
|
+
config_dir: Optional[Path] = None
|
|
198
|
+
) -> List[Dict]:
|
|
199
|
+
"""
|
|
200
|
+
List all registered applications.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
config_dir: Optional custom config directory (default: ~/.af)
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
List of application configuration dictionaries
|
|
207
|
+
|
|
208
|
+
Example:
|
|
209
|
+
>>> apps = list_applications()
|
|
210
|
+
>>> for app in apps:
|
|
211
|
+
... print(f"{app['app_id']}: {app.get('name', 'N/A')}")
|
|
212
|
+
"""
|
|
213
|
+
if config_dir is None:
|
|
214
|
+
config_dir = Path.home() / ".af"
|
|
215
|
+
|
|
216
|
+
app_dir = config_dir / "applications"
|
|
217
|
+
|
|
218
|
+
if not app_dir.exists():
|
|
219
|
+
return []
|
|
220
|
+
|
|
221
|
+
apps = []
|
|
222
|
+
for app_file in sorted(app_dir.glob("*.json")):
|
|
223
|
+
try:
|
|
224
|
+
with open(app_file, "r") as f:
|
|
225
|
+
app_config = json.load(f)
|
|
226
|
+
apps.append(app_config)
|
|
227
|
+
except Exception as e:
|
|
228
|
+
logger.warning(f"Failed to load application config from {app_file}: {e}")
|
|
229
|
+
|
|
230
|
+
return apps
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def delete_application_config(
|
|
234
|
+
app_id: str,
|
|
235
|
+
config_dir: Optional[Path] = None
|
|
236
|
+
) -> bool:
|
|
237
|
+
"""
|
|
238
|
+
Delete application config from disk.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
app_id: Application identifier
|
|
242
|
+
config_dir: Optional custom config directory (default: ~/.af)
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
True if deleted, False if not found
|
|
246
|
+
|
|
247
|
+
Example:
|
|
248
|
+
>>> deleted = delete_application_config("my-old-bot")
|
|
249
|
+
>>> if deleted:
|
|
250
|
+
... print("Deleted successfully")
|
|
251
|
+
"""
|
|
252
|
+
if config_dir is None:
|
|
253
|
+
config_dir = Path.home() / ".af"
|
|
254
|
+
|
|
255
|
+
app_file = config_dir / "applications" / f"{app_id}.json"
|
|
256
|
+
|
|
257
|
+
if not app_file.exists():
|
|
258
|
+
return False
|
|
259
|
+
|
|
260
|
+
app_file.unlink()
|
|
261
|
+
logger.info(f"Deleted application config: {app_file}")
|
|
262
|
+
|
|
263
|
+
return True
|
|
264
|
+
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Authentication helpers for Agentic Fabric SDK.
|
|
3
|
+
|
|
4
|
+
Provides utilities for loading application credentials and creating
|
|
5
|
+
authenticated clients.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
import json
|
|
10
|
+
import httpx
|
|
11
|
+
from typing import Optional, List, Dict
|
|
12
|
+
import logging
|
|
13
|
+
|
|
14
|
+
from ..fabriq_client import FabriqClient
|
|
15
|
+
|
|
16
|
+
logger = logging.getLogger(__name__)
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ApplicationNotFoundError(Exception):
|
|
20
|
+
"""Raised when an application configuration is not found."""
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AuthenticationError(Exception):
|
|
25
|
+
"""Raised when authentication fails."""
|
|
26
|
+
pass
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
async def get_application_client(
|
|
30
|
+
app_id: str,
|
|
31
|
+
config_dir: Optional[Path] = None,
|
|
32
|
+
gateway_url: Optional[str] = None,
|
|
33
|
+
) -> FabriqClient:
|
|
34
|
+
"""
|
|
35
|
+
Get authenticated FabriqClient for an application.
|
|
36
|
+
|
|
37
|
+
Automatically loads credentials from ~/.af/applications/{app_id}.json
|
|
38
|
+
and exchanges them for a JWT token.
|
|
39
|
+
|
|
40
|
+
Args:
|
|
41
|
+
app_id: Application identifier (e.g., "my-slack-bot")
|
|
42
|
+
config_dir: Optional custom config directory (default: ~/.af)
|
|
43
|
+
gateway_url: Optional gateway URL override (default: from app config)
|
|
44
|
+
|
|
45
|
+
Returns:
|
|
46
|
+
Authenticated FabriqClient instance
|
|
47
|
+
|
|
48
|
+
Raises:
|
|
49
|
+
ApplicationNotFoundError: If application config doesn't exist
|
|
50
|
+
AuthenticationError: If authentication fails
|
|
51
|
+
|
|
52
|
+
Example:
|
|
53
|
+
>>> client = await get_application_client("my-slack-bot")
|
|
54
|
+
>>> result = await client.invoke_tool("slack-uuid", "post_message", {...})
|
|
55
|
+
"""
|
|
56
|
+
# 1. Load application config
|
|
57
|
+
try:
|
|
58
|
+
app_config = load_application_config(app_id, config_dir)
|
|
59
|
+
except FileNotFoundError as e:
|
|
60
|
+
raise ApplicationNotFoundError(
|
|
61
|
+
f"Application '{app_id}' not found. "
|
|
62
|
+
f"Register it first with: afctl applications create --app-id {app_id} ..."
|
|
63
|
+
) from e
|
|
64
|
+
|
|
65
|
+
# Use provided gateway_url or fall back to config
|
|
66
|
+
base_url = gateway_url or app_config.get("gateway_url", "https://dashboard.agenticfabriq.com")
|
|
67
|
+
|
|
68
|
+
# 2. Exchange credentials for JWT token
|
|
69
|
+
try:
|
|
70
|
+
async with httpx.AsyncClient() as http:
|
|
71
|
+
response = await http.post(
|
|
72
|
+
f"{base_url}/api/v1/applications/token",
|
|
73
|
+
json={
|
|
74
|
+
"app_id": app_config["app_id"],
|
|
75
|
+
"secret_key": app_config["secret_key"]
|
|
76
|
+
},
|
|
77
|
+
timeout=30.0
|
|
78
|
+
)
|
|
79
|
+
|
|
80
|
+
if response.status_code != 200:
|
|
81
|
+
error_detail = response.text
|
|
82
|
+
try:
|
|
83
|
+
error_json = response.json()
|
|
84
|
+
error_detail = error_json.get("detail", response.text)
|
|
85
|
+
except:
|
|
86
|
+
pass
|
|
87
|
+
|
|
88
|
+
raise AuthenticationError(
|
|
89
|
+
f"Failed to authenticate application '{app_id}': {error_detail}"
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
token_data = response.json()
|
|
93
|
+
except httpx.HTTPError as e:
|
|
94
|
+
raise AuthenticationError(
|
|
95
|
+
f"Network error while authenticating application '{app_id}': {e}"
|
|
96
|
+
) from e
|
|
97
|
+
|
|
98
|
+
# 3. Create and return authenticated client
|
|
99
|
+
client = FabriqClient(
|
|
100
|
+
base_url=base_url,
|
|
101
|
+
auth_token=token_data["access_token"]
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Store metadata for potential refresh
|
|
105
|
+
client._app_id = app_id
|
|
106
|
+
client._expires_in = token_data.get("expires_in", 86400)
|
|
107
|
+
|
|
108
|
+
logger.info(
|
|
109
|
+
f"Authenticated as application '{app_id}' "
|
|
110
|
+
f"(user_id={token_data.get('user_id')}, tenant_id={token_data.get('tenant_id')})"
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
return client
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def load_application_config(
|
|
117
|
+
app_id: str,
|
|
118
|
+
config_dir: Optional[Path] = None
|
|
119
|
+
) -> Dict:
|
|
120
|
+
"""
|
|
121
|
+
Load application config from disk.
|
|
122
|
+
|
|
123
|
+
Args:
|
|
124
|
+
app_id: Application identifier
|
|
125
|
+
config_dir: Optional custom config directory (default: ~/.af)
|
|
126
|
+
|
|
127
|
+
Returns:
|
|
128
|
+
Application configuration dictionary
|
|
129
|
+
|
|
130
|
+
Raises:
|
|
131
|
+
FileNotFoundError: If application config doesn't exist
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
>>> config = load_application_config("my-slack-bot")
|
|
135
|
+
>>> print(config["app_id"], config["created_at"])
|
|
136
|
+
"""
|
|
137
|
+
if config_dir is None:
|
|
138
|
+
config_dir = Path.home() / ".af"
|
|
139
|
+
|
|
140
|
+
app_file = config_dir / "applications" / f"{app_id}.json"
|
|
141
|
+
|
|
142
|
+
if not app_file.exists():
|
|
143
|
+
raise FileNotFoundError(
|
|
144
|
+
f"Application '{app_id}' not found at {app_file}. "
|
|
145
|
+
f"Register it with: afctl applications create --app-id {app_id}"
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
with open(app_file, "r") as f:
|
|
149
|
+
return json.load(f)
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def save_application_config(
|
|
153
|
+
app_id: str,
|
|
154
|
+
config: Dict,
|
|
155
|
+
config_dir: Optional[Path] = None
|
|
156
|
+
) -> Path:
|
|
157
|
+
"""
|
|
158
|
+
Save application config to disk.
|
|
159
|
+
|
|
160
|
+
Args:
|
|
161
|
+
app_id: Application identifier
|
|
162
|
+
config: Application configuration dictionary
|
|
163
|
+
config_dir: Optional custom config directory (default: ~/.af)
|
|
164
|
+
|
|
165
|
+
Returns:
|
|
166
|
+
Path to saved config file
|
|
167
|
+
|
|
168
|
+
Example:
|
|
169
|
+
>>> config = {
|
|
170
|
+
... "app_id": "my-bot",
|
|
171
|
+
... "secret_key": "sk_...",
|
|
172
|
+
... "gateway_url": "https://dashboard.agenticfabriq.com"
|
|
173
|
+
... }
|
|
174
|
+
>>> path = save_application_config("my-bot", config)
|
|
175
|
+
"""
|
|
176
|
+
if config_dir is None:
|
|
177
|
+
config_dir = Path.home() / ".af"
|
|
178
|
+
|
|
179
|
+
# Create applications directory if it doesn't exist
|
|
180
|
+
app_dir = config_dir / "applications"
|
|
181
|
+
app_dir.mkdir(parents=True, exist_ok=True)
|
|
182
|
+
|
|
183
|
+
# Write config file
|
|
184
|
+
app_file = app_dir / f"{app_id}.json"
|
|
185
|
+
with open(app_file, "w") as f:
|
|
186
|
+
json.dump(config, f, indent=2)
|
|
187
|
+
|
|
188
|
+
# Secure the file (user read/write only)
|
|
189
|
+
app_file.chmod(0o600)
|
|
190
|
+
|
|
191
|
+
logger.info(f"Saved application config to {app_file}")
|
|
192
|
+
|
|
193
|
+
return app_file
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def list_applications(
|
|
197
|
+
config_dir: Optional[Path] = None
|
|
198
|
+
) -> List[Dict]:
|
|
199
|
+
"""
|
|
200
|
+
List all registered applications.
|
|
201
|
+
|
|
202
|
+
Args:
|
|
203
|
+
config_dir: Optional custom config directory (default: ~/.af)
|
|
204
|
+
|
|
205
|
+
Returns:
|
|
206
|
+
List of application configuration dictionaries
|
|
207
|
+
|
|
208
|
+
Example:
|
|
209
|
+
>>> apps = list_applications()
|
|
210
|
+
>>> for app in apps:
|
|
211
|
+
... print(f"{app['app_id']}: {app.get('name', 'N/A')}")
|
|
212
|
+
"""
|
|
213
|
+
if config_dir is None:
|
|
214
|
+
config_dir = Path.home() / ".af"
|
|
215
|
+
|
|
216
|
+
app_dir = config_dir / "applications"
|
|
217
|
+
|
|
218
|
+
if not app_dir.exists():
|
|
219
|
+
return []
|
|
220
|
+
|
|
221
|
+
apps = []
|
|
222
|
+
for app_file in sorted(app_dir.glob("*.json")):
|
|
223
|
+
try:
|
|
224
|
+
with open(app_file, "r") as f:
|
|
225
|
+
app_config = json.load(f)
|
|
226
|
+
apps.append(app_config)
|
|
227
|
+
except Exception as e:
|
|
228
|
+
logger.warning(f"Failed to load application config from {app_file}: {e}")
|
|
229
|
+
|
|
230
|
+
return apps
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def delete_application_config(
|
|
234
|
+
app_id: str,
|
|
235
|
+
config_dir: Optional[Path] = None
|
|
236
|
+
) -> bool:
|
|
237
|
+
"""
|
|
238
|
+
Delete application config from disk.
|
|
239
|
+
|
|
240
|
+
Args:
|
|
241
|
+
app_id: Application identifier
|
|
242
|
+
config_dir: Optional custom config directory (default: ~/.af)
|
|
243
|
+
|
|
244
|
+
Returns:
|
|
245
|
+
True if deleted, False if not found
|
|
246
|
+
|
|
247
|
+
Example:
|
|
248
|
+
>>> deleted = delete_application_config("my-old-bot")
|
|
249
|
+
>>> if deleted:
|
|
250
|
+
... print("Deleted successfully")
|
|
251
|
+
"""
|
|
252
|
+
if config_dir is None:
|
|
253
|
+
config_dir = Path.home() / ".af"
|
|
254
|
+
|
|
255
|
+
app_file = config_dir / "applications" / f"{app_id}.json"
|
|
256
|
+
|
|
257
|
+
if not app_file.exists():
|
|
258
|
+
return False
|
|
259
|
+
|
|
260
|
+
app_file.unlink()
|
|
261
|
+
logger.info(f"Deleted application config: {app_file}")
|
|
262
|
+
|
|
263
|
+
return True
|
|
264
|
+
|
af_sdk/fabriq_client.py
CHANGED
|
@@ -80,12 +80,65 @@ class FabriqClient:
|
|
|
80
80
|
|
|
81
81
|
async def invoke_tool(
|
|
82
82
|
self,
|
|
83
|
-
|
|
83
|
+
tool_identifier: str,
|
|
84
84
|
*,
|
|
85
85
|
method: str,
|
|
86
86
|
parameters: Optional[Dict[str, Any]] = None,
|
|
87
87
|
context: Optional[Dict[str, Any]] = None,
|
|
88
88
|
) -> Dict[str, Any]:
|
|
89
|
+
"""Invoke a tool by name or UUID.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
tool_identifier: Tool name (e.g., 'slack') or UUID
|
|
93
|
+
method: Method name to invoke
|
|
94
|
+
parameters: Method parameters
|
|
95
|
+
context: Additional context
|
|
96
|
+
|
|
97
|
+
Returns:
|
|
98
|
+
Tool invocation result
|
|
99
|
+
|
|
100
|
+
Examples:
|
|
101
|
+
result = await client.invoke_tool("slack", method="get_channels")
|
|
102
|
+
result = await client.invoke_tool("slack", method="post_message",
|
|
103
|
+
parameters={"channel": "test", "text": "Hello!"})
|
|
104
|
+
"""
|
|
105
|
+
# Try to resolve tool name to UUID if not already a UUID
|
|
106
|
+
tool_id = tool_identifier
|
|
107
|
+
try:
|
|
108
|
+
from uuid import UUID
|
|
109
|
+
UUID(tool_identifier)
|
|
110
|
+
# It's already a UUID, use it directly
|
|
111
|
+
except ValueError:
|
|
112
|
+
# Not a UUID, try to look up by name
|
|
113
|
+
try:
|
|
114
|
+
tools_response = await self.list_tools()
|
|
115
|
+
except Exception as e:
|
|
116
|
+
raise ValueError(f"Failed to list tools for name resolution: {e}. Please use UUID directly.")
|
|
117
|
+
|
|
118
|
+
# Handle different response formats
|
|
119
|
+
if isinstance(tools_response, dict) and "tools" in tools_response:
|
|
120
|
+
tools = tools_response["tools"]
|
|
121
|
+
elif isinstance(tools_response, dict) and "items" in tools_response:
|
|
122
|
+
tools = tools_response["items"]
|
|
123
|
+
elif hasattr(tools_response, '__iter__') and not isinstance(tools_response, (str, dict)):
|
|
124
|
+
tools = list(tools_response)
|
|
125
|
+
else:
|
|
126
|
+
tools = []
|
|
127
|
+
|
|
128
|
+
# Find tool by name (case-insensitive)
|
|
129
|
+
matching_tools = [t for t in tools if isinstance(t, dict) and t.get("name", "").lower() == tool_identifier.lower()]
|
|
130
|
+
|
|
131
|
+
if not matching_tools:
|
|
132
|
+
available = [t.get('name') for t in tools if isinstance(t, dict) and t.get('name')]
|
|
133
|
+
raise ValueError(f"Tool '{tool_identifier}' not found. Available tools: {available}")
|
|
134
|
+
|
|
135
|
+
if len(matching_tools) > 1:
|
|
136
|
+
raise ValueError(f"Multiple tools found with name '{tool_identifier}'. Please use UUID instead.")
|
|
137
|
+
|
|
138
|
+
tool_id = matching_tools[0].get("id")
|
|
139
|
+
if not tool_id:
|
|
140
|
+
raise ValueError(f"Tool '{tool_identifier}' found but has no ID")
|
|
141
|
+
|
|
89
142
|
body = {"method": method}
|
|
90
143
|
if parameters is not None:
|
|
91
144
|
body["parameters"] = parameters
|
|
@@ -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:
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
af_cli/__init__.py,sha256=F2T4x4H3VIdmTjHRyV5DkaRvbZcdHYzAWD2hxtTplE4,188
|
|
2
2
|
af_cli/commands/__init__.py,sha256=Qngm2Yks6oTKazlxCZA_tIAHXwHoU6Oc7Pw5BGkA7W4,49
|
|
3
|
-
af_cli/commands/agents.py,sha256=
|
|
4
|
-
af_cli/commands/
|
|
5
|
-
af_cli/commands/
|
|
6
|
-
af_cli/commands/
|
|
7
|
-
af_cli/commands/
|
|
8
|
-
af_cli/commands/
|
|
3
|
+
af_cli/commands/agents.py,sha256=XkXL0eB6miWteK-EUet-kkEqVib3L4USncMrJmmnFjY,8325
|
|
4
|
+
af_cli/commands/applications.py,sha256=o6P-rJHO3tdqOQTBksntlVO7Y6hoDSPQp_9nB1azwzc,11958
|
|
5
|
+
af_cli/commands/auth.py,sha256=gz7DLiTTV0nv2DnKaa7_9nLHevCWslG54rRKRA1alx0,11967
|
|
6
|
+
af_cli/commands/config.py,sha256=_k4ixGZfa7Yl_yUL-W1jeMQzykFcPcbw20Yr1v6_ShM,2748
|
|
7
|
+
af_cli/commands/mcp_servers.py,sha256=1hEb4tX80dYb-j_2Z3gDud_v1FcMxl83syJ2N2eLWzI,2390
|
|
8
|
+
af_cli/commands/secrets.py,sha256=z_rHfn8-KEonTC5dioBgH7sc1ppMOltFVFz-58Nqwa0,3436
|
|
9
|
+
af_cli/commands/tools.py,sha256=CUbqxl1UGdoOzz6h-Gp5mrXFeq7CQ7GQDUXathzQIWQ,25428
|
|
9
10
|
af_cli/core/__init__.py,sha256=cQ0H7rGfaMISQPhoNe4Xfu_EKU2TqRVt2OMI7tPea5U,51
|
|
10
11
|
af_cli/core/client.py,sha256=BELf6889BOzOWP6kCk4hSY4DolIATB0YNpui38l7EaI,3939
|
|
11
12
|
af_cli/core/config.py,sha256=fwLUF0blNII2RKdFlJ3Hat51vwwNyxpIkU_HvViz8To,8538
|
|
12
13
|
af_cli/core/oauth.py,sha256=sCzo97TZyx8sLmo0GYv53P3zoABK6htRCivHhRpPRdI,18162
|
|
13
14
|
af_cli/core/output.py,sha256=tL5z7M-tLu2M1Yl8O4M7OPP7iQivC1b_YUUB468Od5Y,4811
|
|
14
15
|
af_cli/core/token_storage.py,sha256=WhOQLx5_9pn2lAlFX01Y5DmWO7B9BJYfEkASCsBQwaI,7738
|
|
15
|
-
af_cli/main.py,sha256=
|
|
16
|
-
af_sdk/__init__.py,sha256=
|
|
17
|
-
af_sdk/auth/__init__.py,sha256=
|
|
16
|
+
af_cli/main.py,sha256=mNDMG4K0837I4je2eVB0o_T23PukBjYRRxVGbqIWBPA,5228
|
|
17
|
+
af_sdk/__init__.py,sha256=KHJn2RkgM2W7bKSshPtgAIHWBluoApoDXaLBUjA02dI,1667
|
|
18
|
+
af_sdk/auth/__init__.py,sha256=laIiIwgxy0mEgRoXEakoEs7BuUYnyrlzHJbdf8Sqa5E,1137
|
|
19
|
+
af_sdk/auth/application.py,sha256=kg5wSbhkQj00vje8_f1mgKVD7ax4b8vKAn_npkI9Bms,7451
|
|
20
|
+
af_sdk/auth/applications.py,sha256=tJ4UMvCJF73i55_4uqsBM04GxDHk5X0SQRWdHVJDcNM,7452
|
|
18
21
|
af_sdk/auth/dpop.py,sha256=s0uiyxxuzsVQNtSexji1htJoxrALwlf1P9507xa-M3Y,1285
|
|
19
22
|
af_sdk/auth/oauth.py,sha256=WRTrrBzs9ieiNnWfxagP6Ag4oI9k0soYjEkjfS2y5Lg,8120
|
|
20
23
|
af_sdk/auth/token_cache.py,sha256=X36E6K0lWqMAqlJXC3i343y8oy-uFm1q-FEdVKXdL1Y,11300
|
|
@@ -26,7 +29,7 @@ af_sdk/dx/decorators.py,sha256=o_EmvE_8pp2vTgMJMgfTy5SXG_24yabuKdoytah02Hk,1294
|
|
|
26
29
|
af_sdk/dx/runtime.py,sha256=4vuPoH-kioTIHxlobrrK1pHvmeFmAIkM7wvKNTJIJ8I,7111
|
|
27
30
|
af_sdk/events.py,sha256=vPlDQHuRQ5eVOchfheAHnKXhoEyJFFqL83_5oliyi3A,23525
|
|
28
31
|
af_sdk/exceptions.py,sha256=ZVjjBeq17CGK69N2OTkVTjPXqXSI_gA7cZk9rCvARcI,4381
|
|
29
|
-
af_sdk/fabriq_client.py,sha256=
|
|
32
|
+
af_sdk/fabriq_client.py,sha256=L836WmMrsQsiOXIrLbCv5EddCrTNsyB0VNc2XEmfJzQ,9678
|
|
30
33
|
af_sdk/models/__init__.py,sha256=_iLKq4SFuxAS-rp1ytq4RN3daAvAUz59wqmfEstwd28,865
|
|
31
34
|
af_sdk/models/audit.py,sha256=_wRahNV7M7ftc2AHFf7J3WzIJ5cUyZhFn_lZX9NITp8,1476
|
|
32
35
|
af_sdk/models/types.py,sha256=Hiwi97xpfvmE-U78-_ft998iBFi4atu6ceCbJBZ-eF0,6435
|
|
@@ -34,7 +37,7 @@ af_sdk/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
|
34
37
|
af_sdk/transport/__init__.py,sha256=HsOc6MmlxIS-PSYC_-6E36-dZYyT_auZeoXvGzVAqeg,104
|
|
35
38
|
af_sdk/transport/http.py,sha256=QB3eqQbwug95QHf5PG_714NKtlTjV9PzVTo8izJCylc,13203
|
|
36
39
|
af_sdk/vault.py,sha256=QVNGigIw8ND5sVXt05gvUY222b5-i9EbzLWNsDGdOU4,17926
|
|
37
|
-
agentic_fabriq_sdk-0.1.
|
|
38
|
-
agentic_fabriq_sdk-0.1.
|
|
39
|
-
agentic_fabriq_sdk-0.1.
|
|
40
|
-
agentic_fabriq_sdk-0.1.
|
|
40
|
+
agentic_fabriq_sdk-0.1.16.dist-info/METADATA,sha256=9MnQ-H6MHNT1cQkbaGJ5nOP-HI4VKRRMYbrweiGcp3M,3658
|
|
41
|
+
agentic_fabriq_sdk-0.1.16.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
|
42
|
+
agentic_fabriq_sdk-0.1.16.dist-info/entry_points.txt,sha256=XUO2EaJhUtUS5pwVIkhemC-nEeFbKgXXLW97gQCCGm8,41
|
|
43
|
+
agentic_fabriq_sdk-0.1.16.dist-info/RECORD,,
|
|
File without changes
|
{agentic_fabriq_sdk-0.1.14.dist-info → agentic_fabriq_sdk-0.1.16.dist-info}/entry_points.txt
RENAMED
|
File without changes
|