applied-cli 0.5.53__tar.gz → 0.5.55__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.53 → applied_cli-0.5.55}/PKG-INFO +1 -1
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli/cli.py +17 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli/client.py +21 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli/flow_helpers.py +3 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli/tools.py +151 -7
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli.egg-info/PKG-INFO +1 -1
- {applied_cli-0.5.53 → applied_cli-0.5.55}/pyproject.toml +1 -1
- {applied_cli-0.5.53 → applied_cli-0.5.55}/README.md +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli/__init__.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli/agent_scoped_flows.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli/conversation_lookup.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli/conversations.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli/credentials.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli/formatters.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli.egg-info/SOURCES.txt +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli.egg-info/dependency_links.txt +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli.egg-info/entry_points.txt +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli.egg-info/requires.txt +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/applied_cli.egg-info/top_level.txt +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/setup.cfg +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/tests/test_agent_scoped_flows.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/tests/test_audit_tools.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/tests/test_benchmark_scenario_tools.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/tests/test_cli.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/tests/test_client.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/tests/test_conversation_tools.py +0 -0
- {applied_cli-0.5.53 → applied_cli-0.5.55}/tests/test_flow_tools.py +0 -0
|
@@ -1836,6 +1836,23 @@ def audit_rating_set_value(
|
|
|
1836
1836
|
typer.echo(result)
|
|
1837
1837
|
|
|
1838
1838
|
|
|
1839
|
+
@app.command("connector-types")
|
|
1840
|
+
def connector_types(
|
|
1841
|
+
type: str = typer.Option(
|
|
1842
|
+
None, "--type", "-t", help="Filter to specific connector (e.g. shopify, zendesk)"
|
|
1843
|
+
),
|
|
1844
|
+
format: str = typer.Option(
|
|
1845
|
+
"text", "--format", "-f", help="Output format: text, csv, or json"
|
|
1846
|
+
),
|
|
1847
|
+
) -> None:
|
|
1848
|
+
"""List available connector types and their supported actions."""
|
|
1849
|
+
client = get_client()
|
|
1850
|
+
result = asyncio.run(
|
|
1851
|
+
tools.connector_types(client, connector_type=type, output_format=format)
|
|
1852
|
+
)
|
|
1853
|
+
typer.echo(result)
|
|
1854
|
+
|
|
1855
|
+
|
|
1839
1856
|
def main() -> None:
|
|
1840
1857
|
"""CLI entrypoint."""
|
|
1841
1858
|
nested_exit_code = run_agent_scoped_flow_command(sys.argv[1:], get_client)
|
|
@@ -431,6 +431,24 @@ class AppliedClient:
|
|
|
431
431
|
)
|
|
432
432
|
return self._normalize_response(data)
|
|
433
433
|
|
|
434
|
+
# -------------------------------------------------------------------------
|
|
435
|
+
# Connector Types
|
|
436
|
+
# -------------------------------------------------------------------------
|
|
437
|
+
|
|
438
|
+
async def list_connector_types(
|
|
439
|
+
self,
|
|
440
|
+
connector_type: str | None = None,
|
|
441
|
+
include_schemas: bool = False,
|
|
442
|
+
) -> list[dict]:
|
|
443
|
+
"""List available connector types and their supported actions."""
|
|
444
|
+
params: dict[str, Any] = {}
|
|
445
|
+
if connector_type:
|
|
446
|
+
params["type"] = connector_type
|
|
447
|
+
if include_schemas:
|
|
448
|
+
params["include_schemas"] = "true"
|
|
449
|
+
data = await self._request("GET", "/v2/connectors/types/", params=params)
|
|
450
|
+
return data if isinstance(data, list) else data.get("results", [data])
|
|
451
|
+
|
|
434
452
|
# -------------------------------------------------------------------------
|
|
435
453
|
# Taxonomy (topics, intents, flags)
|
|
436
454
|
# -------------------------------------------------------------------------
|
|
@@ -1156,6 +1174,7 @@ class AppliedClient:
|
|
|
1156
1174
|
source_handle: str | None = None,
|
|
1157
1175
|
target_handle: str | None = None,
|
|
1158
1176
|
label: str | None = None,
|
|
1177
|
+
metadata: dict[str, Any] | None = None,
|
|
1159
1178
|
) -> dict:
|
|
1160
1179
|
"""Create an edge connecting two nodes."""
|
|
1161
1180
|
body: dict[str, Any] = {
|
|
@@ -1168,6 +1187,8 @@ class AppliedClient:
|
|
|
1168
1187
|
body["target_handle"] = target_handle
|
|
1169
1188
|
if label:
|
|
1170
1189
|
body["label"] = label
|
|
1190
|
+
if metadata:
|
|
1191
|
+
body["metadata"] = metadata
|
|
1171
1192
|
|
|
1172
1193
|
return await self._request(
|
|
1173
1194
|
"POST",
|
|
@@ -28,6 +28,7 @@ _CANONICAL_EXECUTOR_TYPES: tuple[str, ...] = (
|
|
|
28
28
|
"mutate_ticket",
|
|
29
29
|
"mutate_conversation",
|
|
30
30
|
"mutate_message",
|
|
31
|
+
"end_conversation",
|
|
31
32
|
)
|
|
32
33
|
|
|
33
34
|
_EXECUTOR_UI_LABELS: dict[str, str] = {
|
|
@@ -49,6 +50,7 @@ _EXECUTOR_UI_LABELS: dict[str, str] = {
|
|
|
49
50
|
"mutate_ticket": "Ticket",
|
|
50
51
|
"mutate_conversation": "Conversation",
|
|
51
52
|
"mutate_message": "Add Message",
|
|
53
|
+
"end_conversation": "End Conversation",
|
|
52
54
|
}
|
|
53
55
|
|
|
54
56
|
_EXECUTOR_ALIASES: dict[str, tuple[str, ...]] = {
|
|
@@ -70,6 +72,7 @@ _EXECUTOR_ALIASES: dict[str, tuple[str, ...]] = {
|
|
|
70
72
|
"mutate_ticket": ("ticket",),
|
|
71
73
|
"mutate_conversation": ("conversation",),
|
|
72
74
|
"mutate_message": ("add message",),
|
|
75
|
+
"end_conversation": ("end conversation", "end call", "close conversation"),
|
|
73
76
|
}
|
|
74
77
|
|
|
75
78
|
_BRANCH_DEFAULT_ALIASES = {
|
|
@@ -2500,6 +2500,9 @@ async def flow_edge_create(
|
|
|
2500
2500
|
branch: str | int | None = None,
|
|
2501
2501
|
target_handle: str | None = None,
|
|
2502
2502
|
label: str | None = None,
|
|
2503
|
+
metadata: dict | None = None,
|
|
2504
|
+
temporal_timeout_seconds: int | None = None,
|
|
2505
|
+
temporal_disabled_contact_groups: dict | None = None,
|
|
2503
2506
|
) -> str:
|
|
2504
2507
|
"""
|
|
2505
2508
|
Connect two nodes with an edge.
|
|
@@ -2513,7 +2516,14 @@ async def flow_edge_create(
|
|
|
2513
2516
|
branch: Branch path alias for conversational branch nodes, e.g. "1",
|
|
2514
2517
|
"branch 2", or "default". Preferred over source_handle for branch nodes.
|
|
2515
2518
|
target_handle: Input handle name (for mapping)
|
|
2516
|
-
label: Edge label/condition
|
|
2519
|
+
label: Edge label/condition describing when to traverse this edge
|
|
2520
|
+
metadata: Edge metadata dict. For temporal edges, include a "temporal"
|
|
2521
|
+
key (or use the temporal_* convenience params below).
|
|
2522
|
+
temporal_timeout_seconds: Convenience param to create a temporal edge
|
|
2523
|
+
that auto-fires after this many seconds of inactivity.
|
|
2524
|
+
temporal_disabled_contact_groups: Optional dict of contact group
|
|
2525
|
+
overrides for the temporal edge, e.g.
|
|
2526
|
+
{"__default__": false, "<group_id>": true}.
|
|
2517
2527
|
|
|
2518
2528
|
Returns:
|
|
2519
2529
|
Created edge
|
|
@@ -2523,6 +2533,14 @@ async def flow_edge_create(
|
|
|
2523
2533
|
|
|
2524
2534
|
resolved_source_handle = normalize_branch_handle(branch or source_handle)
|
|
2525
2535
|
|
|
2536
|
+
# Build metadata with temporal settings
|
|
2537
|
+
metadata = dict(metadata or {})
|
|
2538
|
+
if temporal_timeout_seconds is not None:
|
|
2539
|
+
temporal = metadata.setdefault("temporal", {})
|
|
2540
|
+
temporal["timeout_seconds"] = temporal_timeout_seconds
|
|
2541
|
+
if temporal_disabled_contact_groups is not None:
|
|
2542
|
+
temporal["disabled_contact_groups"] = temporal_disabled_contact_groups
|
|
2543
|
+
|
|
2526
2544
|
try:
|
|
2527
2545
|
flow = await client.get_flow(flow_id)
|
|
2528
2546
|
except AppliedAPIError as e:
|
|
@@ -2589,6 +2607,7 @@ async def flow_edge_create(
|
|
|
2589
2607
|
source_handle=resolved_source_handle,
|
|
2590
2608
|
target_handle=target_handle,
|
|
2591
2609
|
label=label,
|
|
2610
|
+
metadata=metadata or None,
|
|
2592
2611
|
)
|
|
2593
2612
|
except AppliedAPIError as e:
|
|
2594
2613
|
# Add extra context for edge errors
|
|
@@ -2602,14 +2621,33 @@ async def flow_edge_create(
|
|
|
2602
2621
|
)
|
|
2603
2622
|
return _format_error(e) + extra
|
|
2604
2623
|
|
|
2624
|
+
edge_id = edge.get("id")
|
|
2625
|
+
|
|
2626
|
+
# Follow-up update to persist label and metadata (backend create
|
|
2627
|
+
# endpoint may not pass these through to the model).
|
|
2628
|
+
needs_update = label or metadata
|
|
2629
|
+
if needs_update and edge_id:
|
|
2630
|
+
updates: dict = {}
|
|
2631
|
+
if label:
|
|
2632
|
+
updates["label"] = label
|
|
2633
|
+
if metadata:
|
|
2634
|
+
updates["metadata"] = metadata
|
|
2635
|
+
try:
|
|
2636
|
+
edge = await client.update_edge(flow_id, edge_id, **updates)
|
|
2637
|
+
except AppliedAPIError:
|
|
2638
|
+
pass # best-effort; edge was already created
|
|
2639
|
+
|
|
2605
2640
|
result = "# Created Edge\n"
|
|
2606
|
-
result += f"id: {
|
|
2641
|
+
result += f"id: {edge_id}\n"
|
|
2607
2642
|
result += f"source: {edge.get('source')}\n"
|
|
2608
2643
|
result += f"target: {edge.get('target')}\n"
|
|
2609
2644
|
if resolved_source_handle is not None:
|
|
2610
2645
|
result += f"source_handle: {resolved_source_handle}\n"
|
|
2611
2646
|
if label:
|
|
2612
2647
|
result += f"label: {label}\n"
|
|
2648
|
+
if metadata.get("temporal"):
|
|
2649
|
+
temporal = metadata["temporal"]
|
|
2650
|
+
result += f"temporal_timeout_seconds: {temporal.get('timeout_seconds')}\n"
|
|
2613
2651
|
result += "\nTip: run flow_layout(flow_id) after wiring a complex flow to tidy node positions.\n"
|
|
2614
2652
|
|
|
2615
2653
|
return result
|
|
@@ -2666,6 +2704,8 @@ async def flow_edge_update(
|
|
|
2666
2704
|
edge_id: str,
|
|
2667
2705
|
label: str | None = None,
|
|
2668
2706
|
metadata: dict | None = None,
|
|
2707
|
+
temporal_timeout_seconds: int | None = None,
|
|
2708
|
+
temporal_disabled_contact_groups: dict | None = None,
|
|
2669
2709
|
) -> str:
|
|
2670
2710
|
"""
|
|
2671
2711
|
Update an edge's properties.
|
|
@@ -2674,21 +2714,39 @@ async def flow_edge_update(
|
|
|
2674
2714
|
client: Authenticated AppliedClient
|
|
2675
2715
|
flow_id: The flow UUID
|
|
2676
2716
|
edge_id: The edge UUID
|
|
2677
|
-
label: New label/condition
|
|
2678
|
-
metadata: New metadata
|
|
2717
|
+
label: New label/condition describing when to traverse this edge
|
|
2718
|
+
metadata: New metadata dict. For temporal edges, include a "temporal"
|
|
2719
|
+
key or use the temporal_* convenience params.
|
|
2720
|
+
temporal_timeout_seconds: Set/update the temporal timeout in seconds.
|
|
2721
|
+
Makes this edge auto-fire after inactivity.
|
|
2722
|
+
temporal_disabled_contact_groups: Contact group overrides for the
|
|
2723
|
+
temporal edge, e.g. {"__default__": false, "<group_id>": true}.
|
|
2679
2724
|
|
|
2680
2725
|
Returns:
|
|
2681
2726
|
Updated edge
|
|
2682
2727
|
"""
|
|
2683
|
-
|
|
2728
|
+
metadata = dict(metadata or {}) if metadata else {}
|
|
2729
|
+
if temporal_timeout_seconds is not None:
|
|
2730
|
+
temporal = metadata.setdefault("temporal", {})
|
|
2731
|
+
temporal["timeout_seconds"] = temporal_timeout_seconds
|
|
2732
|
+
if temporal_disabled_contact_groups is not None:
|
|
2733
|
+
temporal["disabled_contact_groups"] = temporal_disabled_contact_groups
|
|
2734
|
+
|
|
2735
|
+
updates: dict = {}
|
|
2684
2736
|
if label is not None:
|
|
2685
2737
|
updates["label"] = label
|
|
2686
|
-
if metadata
|
|
2738
|
+
if metadata:
|
|
2687
2739
|
updates["metadata"] = metadata
|
|
2688
2740
|
|
|
2689
2741
|
edge = await client.update_edge(flow_id, edge_id, **updates)
|
|
2690
2742
|
|
|
2691
|
-
|
|
2743
|
+
result = f"# Updated Edge\nid: {edge.get('id')}"
|
|
2744
|
+
if label is not None:
|
|
2745
|
+
result += f"\nlabel: {label}"
|
|
2746
|
+
if metadata.get("temporal"):
|
|
2747
|
+
temporal = metadata["temporal"]
|
|
2748
|
+
result += f"\ntemporal_timeout_seconds: {temporal.get('timeout_seconds')}"
|
|
2749
|
+
return result
|
|
2692
2750
|
|
|
2693
2751
|
|
|
2694
2752
|
async def flow_edge_delete(
|
|
@@ -3518,6 +3576,92 @@ async def send_message(
|
|
|
3518
3576
|
return f"Error sending message: {e}"
|
|
3519
3577
|
|
|
3520
3578
|
|
|
3579
|
+
async def connector_types(
|
|
3580
|
+
client: AppliedClient,
|
|
3581
|
+
connector_type: str | None = None,
|
|
3582
|
+
output_format: str = "text",
|
|
3583
|
+
) -> str:
|
|
3584
|
+
"""
|
|
3585
|
+
List available connector types and their supported actions.
|
|
3586
|
+
|
|
3587
|
+
Args:
|
|
3588
|
+
client: Authenticated AppliedClient
|
|
3589
|
+
connector_type: Optional filter to a specific connector (e.g. 'shopify')
|
|
3590
|
+
output_format: 'text' (default), 'csv' (summary), 'json' (raw)
|
|
3591
|
+
|
|
3592
|
+
Returns:
|
|
3593
|
+
Connector types with their available actions and metadata
|
|
3594
|
+
"""
|
|
3595
|
+
try:
|
|
3596
|
+
data = await client.list_connector_types(
|
|
3597
|
+
connector_type=connector_type,
|
|
3598
|
+
include_schemas=(output_format == "json"),
|
|
3599
|
+
)
|
|
3600
|
+
except Exception as e:
|
|
3601
|
+
return f"Error listing connector types: {e}"
|
|
3602
|
+
|
|
3603
|
+
if output_format == "json":
|
|
3604
|
+
return to_json(data)
|
|
3605
|
+
|
|
3606
|
+
if output_format == "csv":
|
|
3607
|
+
rows = []
|
|
3608
|
+
for connector in data:
|
|
3609
|
+
for action in connector.get("actions", []):
|
|
3610
|
+
rows.append(
|
|
3611
|
+
{
|
|
3612
|
+
"connector": connector["name"],
|
|
3613
|
+
"action": action["name"],
|
|
3614
|
+
"description": action.get("description", ""),
|
|
3615
|
+
"parameters": ", ".join(
|
|
3616
|
+
p["name"] for p in action.get("parameters", [])
|
|
3617
|
+
),
|
|
3618
|
+
}
|
|
3619
|
+
)
|
|
3620
|
+
return to_csv(rows, ["connector", "action", "description", "parameters"])
|
|
3621
|
+
|
|
3622
|
+
# Default: text output
|
|
3623
|
+
if not data:
|
|
3624
|
+
return "No connector types found."
|
|
3625
|
+
|
|
3626
|
+
result = "# Available Connector Types\n\n"
|
|
3627
|
+
result += (
|
|
3628
|
+
"Use with: flow_node_create(executor_type='resource', "
|
|
3629
|
+
"metadata={'resource_type': '<connector>', 'action': '<action>', "
|
|
3630
|
+
"'resource_id': '<uuid>'})\n\n"
|
|
3631
|
+
)
|
|
3632
|
+
|
|
3633
|
+
for connector in data:
|
|
3634
|
+
name = connector["name"]
|
|
3635
|
+
action_count = connector.get("action_count", len(connector.get("actions", [])))
|
|
3636
|
+
result += f"## {name} ({action_count} actions)\n\n"
|
|
3637
|
+
|
|
3638
|
+
for action in connector.get("actions", []):
|
|
3639
|
+
result += f" {action['name']}"
|
|
3640
|
+
desc = action.get("description", "")
|
|
3641
|
+
if desc:
|
|
3642
|
+
# Show first line of description
|
|
3643
|
+
first_line = desc.split("\n")[0].strip()
|
|
3644
|
+
result += f" — {first_line}"
|
|
3645
|
+
result += "\n"
|
|
3646
|
+
|
|
3647
|
+
params = action.get("parameters", [])
|
|
3648
|
+
if params:
|
|
3649
|
+
for p in params:
|
|
3650
|
+
req = " (required)" if p.get("required") else ""
|
|
3651
|
+
pline = f" {p['name']}: {p.get('type', 'any')}{req}"
|
|
3652
|
+
if p.get("description"):
|
|
3653
|
+
pline += f" — {p['description']}"
|
|
3654
|
+
result += pline + "\n"
|
|
3655
|
+
|
|
3656
|
+
ret = action.get("return_type")
|
|
3657
|
+
if ret:
|
|
3658
|
+
result += f" → {ret}\n"
|
|
3659
|
+
|
|
3660
|
+
result += "\n"
|
|
3661
|
+
|
|
3662
|
+
return result
|
|
3663
|
+
|
|
3664
|
+
|
|
3521
3665
|
async def executor_list(
|
|
3522
3666
|
client: AppliedClient,
|
|
3523
3667
|
output_format: str = "full",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|