applied-cli 0.5.73__tar.gz → 0.6.0__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.
- {applied_cli-0.5.73 → applied_cli-0.6.0}/PKG-INFO +15 -1
- {applied_cli-0.5.73 → applied_cli-0.6.0}/README.md +13 -0
- applied_cli-0.6.0/applied_cli/__init__.py +20 -0
- applied_cli-0.6.0/applied_cli/auth.py +111 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli/cli.py +254 -6
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli/client.py +394 -12
- applied_cli-0.6.0/applied_cli/mcp.py +222 -0
- applied_cli-0.6.0/applied_cli/toolkit.py +255 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli/tools.py +4 -7
- applied_cli-0.6.0/applied_cli/v2/__init__.py +11 -0
- applied_cli-0.6.0/applied_cli/v2/agents.py +448 -0
- applied_cli-0.6.0/applied_cli/v2/articles.py +364 -0
- applied_cli-0.6.0/applied_cli/v2/catalog.py +278 -0
- applied_cli-0.6.0/applied_cli/v2/connectors.py +122 -0
- applied_cli-0.6.0/applied_cli/v2/content.py +337 -0
- applied_cli-0.6.0/applied_cli/v2/conversations.py +709 -0
- applied_cli-0.6.0/applied_cli/v2/domains.py +141 -0
- applied_cli-0.6.0/applied_cli/v2/flows.py +1889 -0
- applied_cli-0.6.0/applied_cli/v2/knowledge.py +609 -0
- applied_cli-0.6.0/applied_cli/v2/manifest.py +129 -0
- applied_cli-0.6.0/applied_cli/v2/products.py +350 -0
- applied_cli-0.6.0/applied_cli/v2/scenarios.py +948 -0
- applied_cli-0.6.0/applied_cli/v2/taxonomy.py +261 -0
- applied_cli-0.6.0/applied_cli/v2/tickets.py +292 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli.egg-info/PKG-INFO +15 -1
- applied_cli-0.6.0/applied_cli.egg-info/SOURCES.txt +60 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli.egg-info/requires.txt +1 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/pyproject.toml +2 -1
- applied_cli-0.6.0/tests/test_auth_context.py +51 -0
- applied_cli-0.6.0/tests/test_cli_v2.py +164 -0
- applied_cli-0.6.0/tests/test_client_v2.py +95 -0
- applied_cli-0.6.0/tests/test_toolkit_contract.py +230 -0
- applied_cli-0.6.0/tests/test_v2_agents.py +536 -0
- applied_cli-0.6.0/tests/test_v2_articles.py +387 -0
- applied_cli-0.6.0/tests/test_v2_catalog_and_mcp.py +327 -0
- applied_cli-0.6.0/tests/test_v2_connectors.py +100 -0
- applied_cli-0.6.0/tests/test_v2_content.py +253 -0
- applied_cli-0.6.0/tests/test_v2_conversations.py +426 -0
- applied_cli-0.6.0/tests/test_v2_flows.py +1740 -0
- applied_cli-0.6.0/tests/test_v2_knowledge.py +551 -0
- applied_cli-0.6.0/tests/test_v2_products.py +382 -0
- applied_cli-0.6.0/tests/test_v2_scenarios.py +876 -0
- applied_cli-0.6.0/tests/test_v2_taxonomy.py +284 -0
- applied_cli-0.6.0/tests/test_v2_tickets.py +328 -0
- applied_cli-0.5.73/applied_cli/__init__.py +0 -9
- applied_cli-0.5.73/applied_cli.egg-info/SOURCES.txt +0 -26
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli/agent_scoped_flows.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli/conversation_lookup.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli/conversations.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli/credentials.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli/flow_helpers.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli/formatters.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli.egg-info/dependency_links.txt +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli.egg-info/entry_points.txt +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/applied_cli.egg-info/top_level.txt +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/setup.cfg +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/tests/test_agent_scoped_flows.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/tests/test_audit_tools.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/tests/test_benchmark_scenario_tools.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/tests/test_cli.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/tests/test_client.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/tests/test_conversation_tools.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/tests/test_flow_tools.py +0 -0
- {applied_cli-0.5.73 → applied_cli-0.6.0}/tests/test_knowledge_content_tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: applied-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.0
|
|
4
4
|
Summary: CLI and shared client library for Applied Labs AI support agents
|
|
5
5
|
Author: Applied Labs
|
|
6
6
|
License-Expression: MIT
|
|
@@ -15,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
15
15
|
Requires-Python: >=3.11
|
|
16
16
|
Description-Content-Type: text/markdown
|
|
17
17
|
Requires-Dist: httpx>=0.27.0
|
|
18
|
+
Requires-Dist: pydantic>=2.0.0
|
|
18
19
|
Requires-Dist: typer>=0.9.0
|
|
19
20
|
Provides-Extra: dev
|
|
20
21
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
@@ -67,8 +68,18 @@ applied knowledge-unprotect <id>
|
|
|
67
68
|
# Taxonomy
|
|
68
69
|
applied taxonomy --type topics
|
|
69
70
|
applied taxonomy-counts --start 2026-03-01 --end 2026-03-18 --format csv
|
|
71
|
+
|
|
72
|
+
# Analytics
|
|
73
|
+
applied analytics-report --view overview_aggregate_metrics --model conversation --start 2026-04-01 --end 2026-04-30 --format json
|
|
74
|
+
applied analytics --group-by topic --metrics count --start 2026-04-01 --end 2026-04-30 --format json
|
|
75
|
+
applied analytics --group-by intent --metrics count --start 2026-04-01 --end 2026-04-30 --format json
|
|
76
|
+
applied metrics --metric-name conversation.resolve --start 2026-04-01 --end 2026-04-30 --period day --format json
|
|
70
77
|
```
|
|
71
78
|
|
|
79
|
+
`analytics-report` returns the selected report payload, not necessarily a `{ "rows": [...] }`
|
|
80
|
+
object. `analytics` returns grouped rows and currently supports `--metrics count`.
|
|
81
|
+
Raw analytics SQL is not available through the public CLI surface.
|
|
82
|
+
|
|
72
83
|
## Library Usage
|
|
73
84
|
|
|
74
85
|
```python
|
|
@@ -99,6 +110,9 @@ conversations = await tools.conversation_query(
|
|
|
99
110
|
| `knowledge_list` | List knowledge base items |
|
|
100
111
|
| `taxonomy_list` | List topics, intents, and flags |
|
|
101
112
|
| `taxonomy_counts` | Aggregate conversation counts by topic and intent |
|
|
113
|
+
| `analytics_report` | Read standard dashboard/report analytics views |
|
|
114
|
+
| `analytics_query` | Aggregate supported conversation dimensions with count |
|
|
115
|
+
| `metrics_query` | Roll up named metric events |
|
|
102
116
|
|
|
103
117
|
## Examples
|
|
104
118
|
|
|
@@ -42,8 +42,18 @@ applied knowledge-unprotect <id>
|
|
|
42
42
|
# Taxonomy
|
|
43
43
|
applied taxonomy --type topics
|
|
44
44
|
applied taxonomy-counts --start 2026-03-01 --end 2026-03-18 --format csv
|
|
45
|
+
|
|
46
|
+
# Analytics
|
|
47
|
+
applied analytics-report --view overview_aggregate_metrics --model conversation --start 2026-04-01 --end 2026-04-30 --format json
|
|
48
|
+
applied analytics --group-by topic --metrics count --start 2026-04-01 --end 2026-04-30 --format json
|
|
49
|
+
applied analytics --group-by intent --metrics count --start 2026-04-01 --end 2026-04-30 --format json
|
|
50
|
+
applied metrics --metric-name conversation.resolve --start 2026-04-01 --end 2026-04-30 --period day --format json
|
|
45
51
|
```
|
|
46
52
|
|
|
53
|
+
`analytics-report` returns the selected report payload, not necessarily a `{ "rows": [...] }`
|
|
54
|
+
object. `analytics` returns grouped rows and currently supports `--metrics count`.
|
|
55
|
+
Raw analytics SQL is not available through the public CLI surface.
|
|
56
|
+
|
|
47
57
|
## Library Usage
|
|
48
58
|
|
|
49
59
|
```python
|
|
@@ -74,6 +84,9 @@ conversations = await tools.conversation_query(
|
|
|
74
84
|
| `knowledge_list` | List knowledge base items |
|
|
75
85
|
| `taxonomy_list` | List topics, intents, and flags |
|
|
76
86
|
| `taxonomy_counts` | Aggregate conversation counts by topic and intent |
|
|
87
|
+
| `analytics_report` | Read standard dashboard/report analytics views |
|
|
88
|
+
| `analytics_query` | Aggregate supported conversation dimensions with count |
|
|
89
|
+
| `metrics_query` | Roll up named metric events |
|
|
77
90
|
|
|
78
91
|
## Examples
|
|
79
92
|
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"""Applied Labs CLI - shared client library for CLI and MCP server."""
|
|
2
|
+
|
|
3
|
+
from applied_cli import tools
|
|
4
|
+
from applied_cli.auth import AuthContext
|
|
5
|
+
from applied_cli.client import AppliedClient
|
|
6
|
+
from applied_cli.formatters import to_csv, to_json
|
|
7
|
+
from applied_cli.toolkit import ToolResult, ToolSpec
|
|
8
|
+
|
|
9
|
+
__version__ = "0.6.0"
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"AppliedClient",
|
|
13
|
+
"AuthContext",
|
|
14
|
+
"ToolResult",
|
|
15
|
+
"ToolSpec",
|
|
16
|
+
"tools",
|
|
17
|
+
"to_csv",
|
|
18
|
+
"to_json",
|
|
19
|
+
"__version__",
|
|
20
|
+
]
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
"""Shared authentication context for local CLI and MCP-injected sessions."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
from typing import Protocol
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
from applied_cli.client import AppliedAPIError, AppliedClient
|
|
11
|
+
from applied_cli.credentials import load_credentials
|
|
12
|
+
|
|
13
|
+
DEFAULT_BASE_URL = "https://api.appliedlabs.ai"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class AuthStatus(BaseModel):
|
|
17
|
+
ok: bool
|
|
18
|
+
shop_id: str | None = None
|
|
19
|
+
base_url: str = DEFAULT_BASE_URL
|
|
20
|
+
message: str
|
|
21
|
+
status_code: int | None = None
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class AuthContext(BaseModel):
|
|
25
|
+
token: str
|
|
26
|
+
shop_id: str | None = None
|
|
27
|
+
base_url: str = DEFAULT_BASE_URL
|
|
28
|
+
access_mode: str | None = None
|
|
29
|
+
|
|
30
|
+
def to_client(self) -> AppliedClient:
|
|
31
|
+
return AppliedClient(
|
|
32
|
+
token=self.token,
|
|
33
|
+
shop_id=self.shop_id,
|
|
34
|
+
base_url=self.base_url,
|
|
35
|
+
access_mode=self.access_mode,
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
async def validate(self) -> AuthStatus:
|
|
39
|
+
client = self.to_client()
|
|
40
|
+
try:
|
|
41
|
+
await client.request(
|
|
42
|
+
"GET",
|
|
43
|
+
"/v1/agents/",
|
|
44
|
+
params={"limit": 1},
|
|
45
|
+
shop_id=self.shop_id,
|
|
46
|
+
)
|
|
47
|
+
except AppliedAPIError as exc:
|
|
48
|
+
return AuthStatus(
|
|
49
|
+
ok=False,
|
|
50
|
+
shop_id=self.shop_id,
|
|
51
|
+
base_url=self.base_url,
|
|
52
|
+
message=str(exc),
|
|
53
|
+
status_code=exc.status_code,
|
|
54
|
+
)
|
|
55
|
+
return AuthStatus(
|
|
56
|
+
ok=True,
|
|
57
|
+
shop_id=self.shop_id,
|
|
58
|
+
base_url=self.base_url,
|
|
59
|
+
message="Authenticated.",
|
|
60
|
+
)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
class CredentialProvider(Protocol):
|
|
64
|
+
def load(self) -> AuthContext | None:
|
|
65
|
+
...
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class EnvCredentialProvider:
|
|
69
|
+
"""Load credentials from environment variables for automation/MCP smoke tests."""
|
|
70
|
+
|
|
71
|
+
def load(self) -> AuthContext | None:
|
|
72
|
+
token = os.environ.get("APPLIED_TOKEN") or os.environ.get("APPLIED_API_TOKEN")
|
|
73
|
+
if not token:
|
|
74
|
+
return None
|
|
75
|
+
return AuthContext(
|
|
76
|
+
token=token,
|
|
77
|
+
shop_id=os.environ.get("APPLIED_SHOP_ID"),
|
|
78
|
+
base_url=os.environ.get("APPLIED_BASE_URL", DEFAULT_BASE_URL),
|
|
79
|
+
access_mode=os.environ.get("APPLIED_ASSISTANT_ACCESS_MODE"),
|
|
80
|
+
)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class StoredCredentialProvider:
|
|
84
|
+
"""Load credentials written by `applied login`."""
|
|
85
|
+
|
|
86
|
+
def load(self) -> AuthContext | None:
|
|
87
|
+
credentials = load_credentials()
|
|
88
|
+
if not credentials or not credentials.get("api_token"):
|
|
89
|
+
return None
|
|
90
|
+
return AuthContext(
|
|
91
|
+
token=credentials["api_token"],
|
|
92
|
+
shop_id=credentials.get("shop_id"),
|
|
93
|
+
base_url=credentials.get("base_url", DEFAULT_BASE_URL),
|
|
94
|
+
access_mode=os.environ.get("APPLIED_ASSISTANT_ACCESS_MODE"),
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class ChainedCredentialProvider:
|
|
99
|
+
def __init__(self, *providers: CredentialProvider):
|
|
100
|
+
self.providers = providers
|
|
101
|
+
|
|
102
|
+
def load(self) -> AuthContext | None:
|
|
103
|
+
for provider in self.providers:
|
|
104
|
+
context = provider.load()
|
|
105
|
+
if context:
|
|
106
|
+
return context
|
|
107
|
+
return None
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def default_credential_provider() -> CredentialProvider:
|
|
111
|
+
return ChainedCredentialProvider(EnvCredentialProvider(), StoredCredentialProvider())
|
|
@@ -10,12 +10,21 @@ import typer
|
|
|
10
10
|
|
|
11
11
|
from applied_cli import __version__, tools
|
|
12
12
|
from applied_cli.agent_scoped_flows import run_agent_scoped_flow_command
|
|
13
|
+
from applied_cli.auth import AuthContext, default_credential_provider
|
|
13
14
|
from applied_cli.client import AppliedClient
|
|
14
15
|
from applied_cli.credentials import (
|
|
15
16
|
clear_credentials,
|
|
16
17
|
load_credentials,
|
|
17
18
|
save_credentials,
|
|
18
19
|
)
|
|
20
|
+
from applied_cli.toolkit import render_tool_result
|
|
21
|
+
from applied_cli.v2.catalog import (
|
|
22
|
+
execute_tool as execute_v2_tool,
|
|
23
|
+
)
|
|
24
|
+
from applied_cli.v2.catalog import (
|
|
25
|
+
get_tool_catalog,
|
|
26
|
+
)
|
|
27
|
+
from applied_cli.v2.manifest import build_doctor_report, build_tool_manifest
|
|
19
28
|
|
|
20
29
|
app = typer.Typer(
|
|
21
30
|
name="applied",
|
|
@@ -24,9 +33,19 @@ app = typer.Typer(
|
|
|
24
33
|
)
|
|
25
34
|
content_app = typer.Typer(help="Inspect synced content items.")
|
|
26
35
|
app.add_typer(content_app, name="content")
|
|
36
|
+
v2_app = typer.Typer(help="Run v2 typed Applied tools.")
|
|
37
|
+
v2_tools_app = typer.Typer(help="Inspect v2 tool schemas.")
|
|
38
|
+
v2_conversations_app = typer.Typer(help="Conversation tools.")
|
|
39
|
+
v2_app.add_typer(v2_tools_app, name="tools")
|
|
40
|
+
v2_app.add_typer(v2_conversations_app, name="conversations")
|
|
41
|
+
app.add_typer(v2_app, name="v2")
|
|
27
42
|
|
|
28
43
|
DEFAULT_BASE_URL = "https://api.appliedlabs.ai"
|
|
29
44
|
DEFAULT_CLIENT_URL = "https://appliedlabs.ai"
|
|
45
|
+
AGENT_DEPLOY_AGENT_IDS_ARGUMENT = typer.Argument(
|
|
46
|
+
...,
|
|
47
|
+
help="One or more Agent IDs to deploy",
|
|
48
|
+
)
|
|
30
49
|
|
|
31
50
|
|
|
32
51
|
def _version_callback(value: bool) -> None:
|
|
@@ -144,6 +163,47 @@ def _build_conversation_filters(
|
|
|
144
163
|
return filters or None
|
|
145
164
|
|
|
146
165
|
|
|
166
|
+
def _v2_auth_context() -> AuthContext | None:
|
|
167
|
+
return default_credential_provider().load()
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
def _parse_v2_input(value: str | None) -> dict:
|
|
171
|
+
if not value:
|
|
172
|
+
return {}
|
|
173
|
+
parsed = _parse_json_option(value, option_name="--input")
|
|
174
|
+
return parsed or {}
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _v2_tool_metadata(include_debug: bool = False) -> list[dict]:
|
|
178
|
+
return [
|
|
179
|
+
{
|
|
180
|
+
"name": spec.name,
|
|
181
|
+
"namespace": spec.namespace,
|
|
182
|
+
"description": spec.description,
|
|
183
|
+
"read_write_mode": spec.read_write_mode,
|
|
184
|
+
"tags": spec.tags,
|
|
185
|
+
}
|
|
186
|
+
for spec in get_tool_catalog(include_debug=include_debug)
|
|
187
|
+
]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def _v2_tool_detail(tool_name: str, include_debug: bool = False) -> dict:
|
|
191
|
+
catalog = get_tool_catalog(include_debug=include_debug)
|
|
192
|
+
spec = catalog.get(tool_name)
|
|
193
|
+
if spec is None:
|
|
194
|
+
typer.echo(f"Unknown v2 tool: {tool_name}", err=True)
|
|
195
|
+
raise typer.Exit(1)
|
|
196
|
+
return {
|
|
197
|
+
"name": spec.name,
|
|
198
|
+
"namespace": spec.namespace,
|
|
199
|
+
"description": spec.description,
|
|
200
|
+
"read_write_mode": spec.read_write_mode,
|
|
201
|
+
"tags": spec.tags,
|
|
202
|
+
"examples": spec.examples,
|
|
203
|
+
"input_schema": spec.input_schema(),
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
|
|
147
207
|
# -----------------------------------------------------------------------------
|
|
148
208
|
# Auth commands
|
|
149
209
|
# -----------------------------------------------------------------------------
|
|
@@ -230,7 +290,13 @@ def logout() -> None:
|
|
|
230
290
|
|
|
231
291
|
|
|
232
292
|
@app.command()
|
|
233
|
-
def whoami(
|
|
293
|
+
def whoami(
|
|
294
|
+
verify: bool = typer.Option(
|
|
295
|
+
True,
|
|
296
|
+
"--verify/--no-verify",
|
|
297
|
+
help="Verify the token against the Applied API.",
|
|
298
|
+
),
|
|
299
|
+
) -> None:
|
|
234
300
|
"""Show current authentication status."""
|
|
235
301
|
creds = load_credentials()
|
|
236
302
|
if not creds or not creds.get("api_token"):
|
|
@@ -243,6 +309,189 @@ def whoami() -> None:
|
|
|
243
309
|
typer.echo(f"Base URL: {creds.get('base_url', DEFAULT_BASE_URL)}")
|
|
244
310
|
if creds.get("shop_id"):
|
|
245
311
|
typer.echo(f"Shop ID: {creds['shop_id']}")
|
|
312
|
+
if verify:
|
|
313
|
+
context = AuthContext(
|
|
314
|
+
token=creds["api_token"],
|
|
315
|
+
shop_id=creds.get("shop_id"),
|
|
316
|
+
base_url=creds.get("base_url", DEFAULT_BASE_URL),
|
|
317
|
+
)
|
|
318
|
+
status = asyncio.run(context.validate())
|
|
319
|
+
typer.echo(f"Verified: {'yes' if status.ok else 'no'}")
|
|
320
|
+
if not status.ok:
|
|
321
|
+
typer.echo(status.message, err=True)
|
|
322
|
+
raise typer.Exit(1)
|
|
323
|
+
|
|
324
|
+
|
|
325
|
+
@v2_conversations_app.command("search")
|
|
326
|
+
def v2_conversations_search(
|
|
327
|
+
search: str | None = typer.Option(None, "--search", help="Full-text search query"),
|
|
328
|
+
limit: int = typer.Option(10, "--limit", "-l", help="Max conversations"),
|
|
329
|
+
json_output: bool = typer.Option(
|
|
330
|
+
False,
|
|
331
|
+
"--json",
|
|
332
|
+
help="Render the structured tool result as JSON.",
|
|
333
|
+
),
|
|
334
|
+
) -> None:
|
|
335
|
+
"""Search conversations through the v2 typed tool catalog."""
|
|
336
|
+
|
|
337
|
+
async def _run():
|
|
338
|
+
return await execute_v2_tool(
|
|
339
|
+
"conversations_search",
|
|
340
|
+
{"search": search, "limit": limit},
|
|
341
|
+
auth_context=_v2_auth_context(),
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
result = asyncio.run(_run())
|
|
345
|
+
typer.echo(render_tool_result(result, "json" if json_output else "text"))
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
@v2_tools_app.command("list")
|
|
349
|
+
def v2_tools_list(
|
|
350
|
+
include_debug: bool = typer.Option(
|
|
351
|
+
False,
|
|
352
|
+
"--include-debug",
|
|
353
|
+
help="Include gated debug tools.",
|
|
354
|
+
),
|
|
355
|
+
json_output: bool = typer.Option(
|
|
356
|
+
False,
|
|
357
|
+
"--json",
|
|
358
|
+
help="Render tool metadata as JSON.",
|
|
359
|
+
),
|
|
360
|
+
) -> None:
|
|
361
|
+
"""List registered v2 Applied tools."""
|
|
362
|
+
payload = {"tools": _v2_tool_metadata(include_debug=include_debug)}
|
|
363
|
+
if json_output:
|
|
364
|
+
typer.echo(json.dumps(payload, indent=2, default=str))
|
|
365
|
+
return
|
|
366
|
+
|
|
367
|
+
for row in payload["tools"]:
|
|
368
|
+
first_line = row["description"].splitlines()[0]
|
|
369
|
+
typer.echo(
|
|
370
|
+
f"{row['name']}\t{row['read_write_mode']}\t{row['namespace']}\t{first_line}"
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
|
|
374
|
+
@v2_tools_app.command("describe")
|
|
375
|
+
def v2_tools_describe(
|
|
376
|
+
tool_name: str = typer.Argument(..., help="v2 tool name"),
|
|
377
|
+
include_debug: bool = typer.Option(
|
|
378
|
+
False,
|
|
379
|
+
"--include-debug",
|
|
380
|
+
help="Include gated debug tools.",
|
|
381
|
+
),
|
|
382
|
+
json_output: bool = typer.Option(
|
|
383
|
+
False,
|
|
384
|
+
"--json",
|
|
385
|
+
help="Render schema as JSON.",
|
|
386
|
+
),
|
|
387
|
+
) -> None:
|
|
388
|
+
"""Describe one v2 Applied tool and its input schema."""
|
|
389
|
+
payload = _v2_tool_detail(tool_name, include_debug=include_debug)
|
|
390
|
+
if json_output:
|
|
391
|
+
typer.echo(json.dumps(payload, indent=2, default=str))
|
|
392
|
+
return
|
|
393
|
+
|
|
394
|
+
typer.echo(f"{payload['name']} ({payload['read_write_mode']})")
|
|
395
|
+
typer.echo(payload["description"])
|
|
396
|
+
typer.echo(json.dumps(payload["input_schema"], indent=2, default=str))
|
|
397
|
+
|
|
398
|
+
|
|
399
|
+
@v2_tools_app.command("manifest")
|
|
400
|
+
def v2_tools_manifest(
|
|
401
|
+
include_debug: bool = typer.Option(
|
|
402
|
+
False,
|
|
403
|
+
"--include-debug",
|
|
404
|
+
help="Include gated debug tools.",
|
|
405
|
+
),
|
|
406
|
+
json_output: bool = typer.Option(
|
|
407
|
+
False,
|
|
408
|
+
"--json",
|
|
409
|
+
help="Render the complete manifest as JSON.",
|
|
410
|
+
),
|
|
411
|
+
) -> None:
|
|
412
|
+
"""Emit the complete v2 tool manifest for agents and MCP packaging."""
|
|
413
|
+
payload = build_tool_manifest(include_debug=include_debug)
|
|
414
|
+
if json_output:
|
|
415
|
+
typer.echo(json.dumps(payload, indent=2, default=str))
|
|
416
|
+
return
|
|
417
|
+
|
|
418
|
+
typer.echo(
|
|
419
|
+
f"Applied CLI v2 manifest: {payload['tool_count']} tools "
|
|
420
|
+
f"across {len(payload['namespace_counts'])} namespaces"
|
|
421
|
+
)
|
|
422
|
+
for name, count in payload["namespace_counts"].items():
|
|
423
|
+
typer.echo(f"{name}\t{count}")
|
|
424
|
+
|
|
425
|
+
|
|
426
|
+
@v2_app.command("doctor")
|
|
427
|
+
def v2_doctor(
|
|
428
|
+
include_debug: bool = typer.Option(
|
|
429
|
+
False,
|
|
430
|
+
"--include-debug",
|
|
431
|
+
help="Include gated debug tools in the catalog check.",
|
|
432
|
+
),
|
|
433
|
+
validate_auth: bool = typer.Option(
|
|
434
|
+
False,
|
|
435
|
+
"--validate-auth",
|
|
436
|
+
help="Verify configured credentials against the Applied API.",
|
|
437
|
+
),
|
|
438
|
+
json_output: bool = typer.Option(
|
|
439
|
+
False,
|
|
440
|
+
"--json",
|
|
441
|
+
help="Render diagnostics as JSON.",
|
|
442
|
+
),
|
|
443
|
+
) -> None:
|
|
444
|
+
"""Check v2 catalog, MCP registration, and optional auth readiness."""
|
|
445
|
+
|
|
446
|
+
async def _run():
|
|
447
|
+
return await build_doctor_report(
|
|
448
|
+
include_debug=include_debug,
|
|
449
|
+
validate_auth=validate_auth,
|
|
450
|
+
credential_provider=default_credential_provider(),
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
payload = asyncio.run(_run())
|
|
454
|
+
if json_output:
|
|
455
|
+
typer.echo(json.dumps(payload, indent=2, default=str))
|
|
456
|
+
return
|
|
457
|
+
|
|
458
|
+
auth = payload["auth"]
|
|
459
|
+
typer.echo(f"Applied CLI {payload['version']}")
|
|
460
|
+
typer.echo(f"Catalog tools: {payload['catalog']['tool_count']}")
|
|
461
|
+
typer.echo(f"MCP registrar: {payload['mcp']['registrar']}")
|
|
462
|
+
typer.echo(f"Auth configured: {'yes' if auth['configured'] else 'no'}")
|
|
463
|
+
if validate_auth:
|
|
464
|
+
typer.echo(f"Auth valid: {'yes' if auth['ok'] else 'no'}")
|
|
465
|
+
if auth["message"]:
|
|
466
|
+
typer.echo(auth["message"])
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
@v2_app.command("run")
|
|
470
|
+
def v2_run(
|
|
471
|
+
tool_name: str = typer.Argument(..., help="v2 tool name"),
|
|
472
|
+
input_json: str | None = typer.Option(
|
|
473
|
+
None,
|
|
474
|
+
"--input",
|
|
475
|
+
"-i",
|
|
476
|
+
help="JSON object input for the tool.",
|
|
477
|
+
),
|
|
478
|
+
json_output: bool = typer.Option(
|
|
479
|
+
False,
|
|
480
|
+
"--json",
|
|
481
|
+
help="Render the structured tool result as JSON.",
|
|
482
|
+
),
|
|
483
|
+
) -> None:
|
|
484
|
+
"""Run any v2 Applied tool by name with JSON input."""
|
|
485
|
+
|
|
486
|
+
async def _run():
|
|
487
|
+
return await execute_v2_tool(
|
|
488
|
+
tool_name,
|
|
489
|
+
_parse_v2_input(input_json),
|
|
490
|
+
auth_context=_v2_auth_context(),
|
|
491
|
+
)
|
|
492
|
+
|
|
493
|
+
result = asyncio.run(_run())
|
|
494
|
+
typer.echo(render_tool_result(result, "json" if json_output else "text"))
|
|
246
495
|
|
|
247
496
|
|
|
248
497
|
# -----------------------------------------------------------------------------
|
|
@@ -2475,7 +2724,7 @@ def product_update(
|
|
|
2475
2724
|
|
|
2476
2725
|
@app.command("agent-deploy")
|
|
2477
2726
|
def agent_deploy(
|
|
2478
|
-
agent_ids: list[str] =
|
|
2727
|
+
agent_ids: list[str] = AGENT_DEPLOY_AGENT_IDS_ARGUMENT,
|
|
2479
2728
|
description: str = typer.Option("", "--description", "-d", help="Optional revision description"),
|
|
2480
2729
|
shop_id: str = typer.Option(None, "--shop-id", help="Override shop ID"),
|
|
2481
2730
|
) -> None:
|
|
@@ -2580,7 +2829,6 @@ def agent_revision_diff(
|
|
|
2580
2829
|
Example:
|
|
2581
2830
|
applied agent-revision-diff <agent_id> 54 55
|
|
2582
2831
|
"""
|
|
2583
|
-
import difflib
|
|
2584
2832
|
|
|
2585
2833
|
client = get_client(shop_id=shop_id)
|
|
2586
2834
|
|
|
@@ -2661,7 +2909,7 @@ def agent_revision_diff(
|
|
|
2661
2909
|
for field in ("model", "escalation_mode", "use_guardrails", "auto_reply", "response_delay_in_seconds"):
|
|
2662
2910
|
typer.echo(f" {field}: {agent.get(field)}")
|
|
2663
2911
|
|
|
2664
|
-
typer.echo(
|
|
2912
|
+
typer.echo("\n--- PROMPT (guardrail, current) ---")
|
|
2665
2913
|
guardrail = agent.get("guardrail") or ""
|
|
2666
2914
|
typer.echo(f" Length: {len(guardrail)} chars")
|
|
2667
2915
|
typer.echo(f" First 300 chars:\n{guardrail[:300]}")
|
|
@@ -2673,8 +2921,8 @@ def agent_revision_diff(
|
|
|
2673
2921
|
for f in active[:20]:
|
|
2674
2922
|
typer.echo(f" [{f.get('trigger','?')}] {f.get('name')} ({f.get('status')})")
|
|
2675
2923
|
|
|
2676
|
-
typer.echo(
|
|
2677
|
-
typer.echo(
|
|
2924
|
+
typer.echo("\nNote: Full prompt diff requires snapshot storage per revision (not yet available in API).")
|
|
2925
|
+
typer.echo("To investigate: compare guardrail text above against the known-good version,")
|
|
2678
2926
|
typer.echo(f"and check if any flows were added/removed between {rev_a['created_at'][:10]} and {rev_b['created_at'][:10]}.")
|
|
2679
2927
|
|
|
2680
2928
|
|