@invokehq/cli 0.2.3 → 0.2.5

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.
Files changed (2) hide show
  1. package/agentify.py +128 -0
  2. package/package.json +1 -1
package/agentify.py CHANGED
@@ -1569,6 +1569,111 @@ def search_command(args: argparse.Namespace) -> int:
1569
1569
  return 0
1570
1570
 
1571
1571
 
1572
+ def action_payload_from_args(args: argparse.Namespace) -> dict[str, Any]:
1573
+ if not args.action:
1574
+ raise CliUsageError(
1575
+ f"Missing action.\n"
1576
+ f"Usage: invoke {args.command} <action> '<json_params>'\n"
1577
+ f"Example: invoke {args.command} stripe.charge_customer '{{\"customer_id\":\"cust_123\",\"amount\":2400}}'"
1578
+ )
1579
+ try:
1580
+ params = json.loads(args.params)
1581
+ except json.JSONDecodeError as exc:
1582
+ raise ValueError(f"params must be valid JSON: {exc}") from exc
1583
+ if not isinstance(params, dict):
1584
+ raise ValueError("params must be a JSON object")
1585
+
1586
+ body: dict[str, Any] = {
1587
+ "action": args.action,
1588
+ "params": params,
1589
+ "agent_id": args.agent_id,
1590
+ }
1591
+ if args.idempotency_key:
1592
+ body["idempotency_key"] = args.idempotency_key
1593
+ if args.apply_safe_fixes:
1594
+ body["apply_safe_fixes"] = True
1595
+ if getattr(args, "preflight_only", False):
1596
+ body["preflight_only"] = True
1597
+ return body
1598
+
1599
+
1600
+ def print_preflight_summary(preflight: dict[str, Any]) -> None:
1601
+ print(f"Risk: {preflight.get('risk_score', '-')}" + (
1602
+ f" ({preflight.get('risk_score_numeric')})" if preflight.get("risk_score_numeric") is not None else ""
1603
+ ))
1604
+ subject = preflight.get("subject") if isinstance(preflight.get("subject"), dict) else {}
1605
+ if subject.get("tool"):
1606
+ print(f"Subject: {subject.get('tool')} [{subject.get('safety_class') or 'unknown'}]")
1607
+ simulation = preflight.get("simulation") if isinstance(preflight.get("simulation"), dict) else {}
1608
+ if simulation:
1609
+ print(
1610
+ f"Simulation: {simulation.get('similar_traces', 0)} similar traces "
1611
+ f"via {simulation.get('source', 'patterns')}"
1612
+ )
1613
+
1614
+ warnings = preflight.get("warnings") if isinstance(preflight.get("warnings"), list) else []
1615
+ if warnings:
1616
+ print("\nWarnings")
1617
+ for warning in warnings:
1618
+ print(f"- {warning.get('severity', 'warn')}: {warning.get('message')}")
1619
+
1620
+ guardrails = preflight.get("recommended_guardrails") if isinstance(preflight.get("recommended_guardrails"), list) else []
1621
+ if guardrails:
1622
+ print("\nRecommended guardrails")
1623
+ for guardrail in guardrails:
1624
+ applied = "applied" if guardrail.get("applied") else "pending"
1625
+ print(f"- {guardrail.get('type')}: {guardrail.get('action')} [{applied}]")
1626
+
1627
+ safe_fixes = preflight.get("safe_fixes") if isinstance(preflight.get("safe_fixes"), dict) else {}
1628
+ if safe_fixes.get("idempotency_key"):
1629
+ print(f"\nIdempotency key: {safe_fixes.get('idempotency_key')}")
1630
+ print(f"\nDecision: {preflight.get('decision', '-')}")
1631
+
1632
+
1633
+ def preflight_command(args: argparse.Namespace) -> int:
1634
+ credentials = require_credentials(args)
1635
+ response = api_request(
1636
+ "POST",
1637
+ credentials["base_url"],
1638
+ "/v1/preflight",
1639
+ credentials["api_key"],
1640
+ action_payload_from_args(args),
1641
+ )
1642
+ if args.json:
1643
+ print(json.dumps(response, indent=2))
1644
+ return 0
1645
+ print(f"Invoke preflight: {args.action}")
1646
+ print_preflight_summary(response.get("preflight") or {})
1647
+ return 0
1648
+
1649
+
1650
+ def execute_command(args: argparse.Namespace) -> int:
1651
+ credentials = require_credentials(args)
1652
+ response = api_request(
1653
+ "POST",
1654
+ credentials["base_url"],
1655
+ "/v1/executions",
1656
+ credentials["api_key"],
1657
+ action_payload_from_args(args),
1658
+ )
1659
+ if args.json:
1660
+ print(json.dumps(response, indent=2))
1661
+ return 0
1662
+
1663
+ execution = response.get("execution") if isinstance(response.get("execution"), dict) else {}
1664
+ certificate = response.get("certificate") if isinstance(response.get("certificate"), dict) else {}
1665
+ print(f"Invoke execute: {args.action}")
1666
+ preflight = response.get("preflight") if isinstance(response.get("preflight"), dict) else {}
1667
+ if preflight:
1668
+ print_preflight_summary(preflight)
1669
+ print()
1670
+ print(f"Execution ID: {certificate.get('execution_id') or execution.get('execution_id') or '-'}")
1671
+ print(f"Decision: {certificate.get('decision') or execution.get('decision') or execution.get('status') or '-'}")
1672
+ print(f"Final outcome: {certificate.get('final_outcome') or execution.get('final_outcome') or '-'}")
1673
+ print("Certificate returned: yes")
1674
+ return 0
1675
+
1676
+
1572
1677
  def workflow_command(args: argparse.Namespace) -> int:
1573
1678
  credentials = require_credentials(args)
1574
1679
  body: dict[str, Any] = {}
@@ -1728,6 +1833,29 @@ def build_parser() -> argparse.ArgumentParser:
1728
1833
  search.add_argument("--api-key", help="Override Invoke API key.")
1729
1834
  search.set_defaults(func=search_command)
1730
1835
 
1836
+ preflight = subparsers.add_parser("preflight", help="Simulate an agent action before execution.")
1837
+ preflight.add_argument("action", nargs="?", help="Action name, for example stripe.charge_customer.")
1838
+ preflight.add_argument("params", nargs="?", default="{}", help="JSON params object.")
1839
+ preflight.add_argument("--agent-id", default="cli_agent")
1840
+ preflight.add_argument("--idempotency-key")
1841
+ preflight.add_argument("--apply-safe-fixes", action="store_true", help="Apply recommended guardrails in the returned plan.")
1842
+ preflight.add_argument("--json", action="store_true", help="Print the full JSON response.")
1843
+ preflight.add_argument("--base-url", help="Override Invoke runtime URL.")
1844
+ preflight.add_argument("--api-key", help="Override Invoke API key.")
1845
+ preflight.set_defaults(func=preflight_command)
1846
+
1847
+ execute = subparsers.add_parser("execute", help="Execute an agent action through Invoke's control boundary.")
1848
+ execute.add_argument("action", nargs="?", help="Action name, for example stripe.charge_customer.")
1849
+ execute.add_argument("params", nargs="?", default="{}", help="JSON params object.")
1850
+ execute.add_argument("--agent-id", default="cli_agent")
1851
+ execute.add_argument("--idempotency-key")
1852
+ execute.add_argument("--apply-safe-fixes", action="store_true", help="Apply recommended guardrails before returning the receipt.")
1853
+ execute.add_argument("--preflight-only", action="store_true", help="Only run the pre-execution simulation.")
1854
+ execute.add_argument("--json", action="store_true", help="Print the full JSON response.")
1855
+ execute.add_argument("--base-url", help="Override Invoke runtime URL.")
1856
+ execute.add_argument("--api-key", help="Override Invoke API key.")
1857
+ execute.set_defaults(func=execute_command)
1858
+
1731
1859
  workflow = subparsers.add_parser("workflow", help="Run a packaged Invoke workflow.")
1732
1860
  workflow.add_argument(
1733
1861
  "workflow",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invokehq/cli",
3
- "version": "0.2.3",
3
+ "version": "0.2.5",
4
4
  "description": "CLI for Invoke, execution reliability infrastructure for AI agents.",
5
5
  "license": "Apache-2.0",
6
6
  "bin": {