devops-bot-sdk 1.4.145__tar.gz → 1.4.146__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.
Files changed (95) hide show
  1. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/PKG-INFO +1 -1
  2. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/devops_bot_sdk.egg-info/PKG-INFO +1 -1
  3. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/__init__.py +2 -2
  4. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/inventory.py +2 -2
  5. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/local_exec.py +76 -2
  6. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/service.py +28 -0
  7. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/setup_manager.py +14 -0
  8. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/README.md +0 -0
  9. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/devops_bot_sdk.egg-info/SOURCES.txt +0 -0
  10. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/devops_bot_sdk.egg-info/dependency_links.txt +0 -0
  11. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/devops_bot_sdk.egg-info/entry_points.txt +0 -0
  12. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/devops_bot_sdk.egg-info/requires.txt +0 -0
  13. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/devops_bot_sdk.egg-info/top_level.txt +0 -0
  14. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/pyproject.toml +0 -0
  15. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agentd.py +0 -0
  16. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/__init__.py +0 -0
  17. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/builder.py +0 -0
  18. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/chat.py +0 -0
  19. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/connectors.py +0 -0
  20. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/engines/__init__.py +0 -0
  21. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/engines/claude_code.py +0 -0
  22. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/engines/http.py +0 -0
  23. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/engines/mcp.py +0 -0
  24. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/engines/n8n.py +0 -0
  25. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/engines/shell.py +0 -0
  26. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/integrations.py +0 -0
  27. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/library/__init__.py +0 -0
  28. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/manifest.py +0 -0
  29. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/prompt_studio.py +0 -0
  30. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/prompt_template.py +0 -0
  31. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/prompt_vars.py +0 -0
  32. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/run_log.py +0 -0
  33. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/secrets.py +0 -0
  34. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/usage.py +0 -0
  35. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/agents/varstore.py +0 -0
  36. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/cli.py +0 -0
  37. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/client.py +0 -0
  38. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/collectors/__init__.py +0 -0
  39. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/collectors/files.py +0 -0
  40. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/collectors/process.py +0 -0
  41. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/collectors/screenshot.py +0 -0
  42. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/config.py +0 -0
  43. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/crucial.py +0 -0
  44. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/design_verify/__init__.py +0 -0
  45. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/design_verify/assets.py +0 -0
  46. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/design_verify/bootstrap.py +0 -0
  47. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/design_verify/browser.py +0 -0
  48. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/design_verify/compare.py +0 -0
  49. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/design_verify/loop.py +0 -0
  50. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/exceptions.py +0 -0
  51. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/git_ops.py +0 -0
  52. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/graphify.py +0 -0
  53. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/hooks/__init__.py +0 -0
  54. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/hooks/crucial_guard.py +0 -0
  55. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ipc/__init__.py +0 -0
  56. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ipc/electron_bridge.py +0 -0
  57. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ipc/handlers.py +0 -0
  58. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/models/__init__.py +0 -0
  59. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/models/envelope.py +0 -0
  60. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/models/requests.py +0 -0
  61. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/models/responses.py +0 -0
  62. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/models/snapshots.py +0 -0
  63. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/platform_compat.py +0 -0
  64. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/py.typed +0 -0
  65. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/run_auto.py +0 -0
  66. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/sse.py +0 -0
  67. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/test.py +0 -0
  68. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/test_pipeline.py +0 -0
  69. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/__init__.py +0 -0
  70. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/api.py +0 -0
  71. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/dashboard.py +0 -0
  72. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/health.py +0 -0
  73. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/ip_allowlist.py +0 -0
  74. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/jobs.py +0 -0
  75. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/models.py +0 -0
  76. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/runner.py +0 -0
  77. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/security.py +0 -0
  78. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/server.py +0 -0
  79. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/static/agents.html +0 -0
  80. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/static/app.js +0 -0
  81. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/static/custom-agents.html +0 -0
  82. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/static/default-agents.html +0 -0
  83. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/static/index.html +0 -0
  84. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/static/runs.html +0 -0
  85. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/static/setup.html +0 -0
  86. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/static/styles.css +0 -0
  87. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/ui/static/tickets.html +0 -0
  88. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/sdk/updater.py +0 -0
  89. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/setup.cfg +0 -0
  90. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/tests/test_agent_chat_features.py +0 -0
  91. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/tests/test_agent_prompt_models.py +0 -0
  92. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/tests/test_agents_connectors_builder.py +0 -0
  93. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/tests/test_git_action.py +0 -0
  94. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/tests/test_health.py +0 -0
  95. {devops_bot_sdk-1.4.145 → devops_bot_sdk-1.4.146}/tests/test_updater_extras.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.145
3
+ Version: 1.4.146
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devops-bot-sdk
3
- Version: 1.4.145
3
+ Version: 1.4.146
4
4
  Summary: DevOps Bot Desktop SDK — thin client for the AgentOS Electron desktop app
5
5
  Author: noumanaziz2128
6
6
  License-Expression: LicenseRef-Proprietary
@@ -1,6 +1,6 @@
1
1
  """AgentOS Desktop SDK — thin HTTPS/SSE client for the Electron app.
2
2
 
3
- Version: 1.4.145
3
+ Version: 1.4.146
4
4
 
5
5
  Public surface:
6
6
  BackendClient.from_config() — create client from ~/.agentos/config.toml
@@ -30,7 +30,7 @@ Rules:
30
30
  - All data egress through submit_webhook only
31
31
  """
32
32
 
33
- __version__ = "1.4.145" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
33
+ __version__ = "1.4.146" # SINGLE SOURCE OF TRUTH — bump on every change; pyproject,
34
34
  __author__ = "AgentOS" # sdk.client.SDK_VERSION and the UI server version all read this.
35
35
 
36
36
  from sdk.client import BackendClient
@@ -11,7 +11,6 @@ from __future__ import annotations
11
11
 
12
12
  import json
13
13
  import logging
14
- import shutil
15
14
  import subprocess
16
15
  from pathlib import Path
17
16
 
@@ -70,7 +69,8 @@ def _connectors() -> list[dict]:
70
69
 
71
70
  def _mcp_servers() -> list[dict]:
72
71
  """Installed MCP servers / Claude plugins, parsed from ``claude mcp list``."""
73
- cli = shutil.which("claude")
72
+ from sdk.local_exec import claude_cli_path
73
+ cli = claude_cli_path()
74
74
  if not cli:
75
75
  return []
76
76
  try:
@@ -22,6 +22,7 @@ import json
22
22
  import os
23
23
  import re
24
24
  import shutil
25
+ import subprocess
25
26
  import sys
26
27
  from datetime import datetime, timedelta
27
28
  from pathlib import Path
@@ -240,7 +241,21 @@ EventHandler = Callable[[dict], Awaitable[None]]
240
241
 
241
242
 
242
243
  def claude_cli_path() -> str | None:
243
- """Absolute path to the `claude` CLI if installed on PATH, else None."""
244
+ """Absolute path to the `claude` CLI, or None if it isn't on PATH.
245
+
246
+ Resolution order:
247
+ 1. ``AGENTOS_CLAUDE_CLI`` env var — explicit path override (escape hatch for
248
+ an install in a non-standard location).
249
+ 2. ``PATH`` (``shutil.which``) — cross-platform, no guessing.
250
+
251
+ The service must therefore RUN with a PATH that includes the CLI. The install
252
+ step bakes that in (see ``sdk.ui.service`` — systemd ``Environment=PATH`` /
253
+ launchd ``EnvironmentVariables``; Windows tasks inherit the user's PATH), so
254
+ ``engine=claude_code`` works without anyone editing a unit file.
255
+ """
256
+ override = os.environ.get("AGENTOS_CLAUDE_CLI", "").strip()
257
+ if override and Path(override).exists():
258
+ return override
244
259
  return shutil.which("claude")
245
260
 
246
261
 
@@ -249,6 +264,54 @@ def is_available() -> bool:
249
264
  return claude_cli_path() is not None
250
265
 
251
266
 
267
+ def claude_status(timeout: float = 15.0) -> dict:
268
+ """Readiness of the local Claude Code CLI: installed AND logged in.
269
+
270
+ Runs ``claude auth status`` (which prints structured JSON) so the panel /
271
+ health surface can show a non-technical operator one clear signal — "connected
272
+ as you@org" vs "not logged in" — instead of a raw CLI error surfacing only when
273
+ a job runs. All failures degrade to ``logged_in: False`` with an ``error`` note;
274
+ this never raises.
275
+
276
+ Returns::
277
+
278
+ {"installed": bool, "logged_in": bool, "path": str|None,
279
+ "auth_method": str|None, "email": str|None, "org": str|None,
280
+ "error": str|None}
281
+ """
282
+ cli = claude_cli_path()
283
+ if not cli:
284
+ return {"installed": False, "logged_in": False, "path": None,
285
+ "auth_method": None, "email": None, "org": None,
286
+ "error": "Claude Code CLI not found. Install it or set AGENTOS_CLAUDE_CLI."}
287
+ try:
288
+ proc = subprocess.run([cli, "auth", "status"], capture_output=True,
289
+ text=True, timeout=timeout)
290
+ except Exception as exc: # noqa: BLE001 — timeout / OSError; never raise to caller
291
+ return {"installed": True, "logged_in": False, "path": cli, "auth_method": None,
292
+ "email": None, "org": None, "error": f"`claude auth status` failed: {exc}"}
293
+
294
+ out = (proc.stdout or "").strip()
295
+ try:
296
+ data = json.loads(out)
297
+ except Exception: # noqa: BLE001 — older CLIs may print human text, not JSON
298
+ logged = "logged in" in out.lower()
299
+ return {"installed": True, "logged_in": logged, "path": cli, "auth_method": None,
300
+ "email": None, "org": None,
301
+ "error": None if logged else ((proc.stderr or out or "not logged in").strip()[:200])}
302
+
303
+ logged = bool(data.get("loggedIn"))
304
+ return {
305
+ "installed": True,
306
+ "logged_in": logged,
307
+ "path": cli,
308
+ "auth_method": data.get("authMethod"),
309
+ "email": data.get("email"),
310
+ "org": data.get("orgName") or data.get("orgId"),
311
+ "error": None if logged else "Not logged in — run `claude` on the VPS to sign in.",
312
+ }
313
+
314
+
252
315
  # Cache the discovered MCP allow-entries for the process lifetime — the set of
253
316
  # configured servers doesn't change mid-run, and `claude mcp list` does a network
254
317
  # health check (~seconds) we don't want to repeat per ticket.
@@ -396,7 +459,9 @@ async def run_claude_local(
396
459
  cli = claude_cli_path()
397
460
  if not cli:
398
461
  return {
399
- "ok": False, "result": "", "error": "claude CLI not found on PATH",
462
+ "ok": False, "result": "",
463
+ "error": ("Claude Code CLI not found. Install it, or if it is installed "
464
+ "in a non-standard location set AGENTOS_CLAUDE_CLI to its full path."),
400
465
  "input_tokens": 0, "output_tokens": 0, "total_tokens": 0,
401
466
  "cost_usd": 0.0, "files_changed": [],
402
467
  }
@@ -461,6 +526,15 @@ async def run_claude_local(
461
526
  # the user's global git config. Tokens stay in the subprocess env only.
462
527
  env = dict(os.environ)
463
528
 
529
+ # Make sure the CLI's own directory is on the subprocess PATH: node/gh and the
530
+ # CLI's sibling tools usually live alongside it, and under a minimal service
531
+ # PATH they'd otherwise be missing. Prepend the resolved binary's dir only —
532
+ # no guessing beyond that — so anything the operator exported still wins.
533
+ _cli_dir = str(Path(cli).resolve().parent)
534
+ _cur_path = env.get("PATH", "")
535
+ if _cli_dir not in _cur_path.split(os.pathsep):
536
+ env["PATH"] = _cli_dir + (os.pathsep + _cur_path if _cur_path else "")
537
+
464
538
  # A custom agent's persisted API keys (sdk.agents.secrets) are overlaid here
465
539
  # so they reach the subprocess (and any MCP servers it spawns) WITHOUT ever
466
540
  # touching the ambient os.environ of this process — same "subprocess env only"
@@ -51,6 +51,30 @@ def _systemd_available() -> bool:
51
51
  return bool(shutil.which("systemctl")) and Path("/run/systemd/system").exists()
52
52
 
53
53
 
54
+ def _service_path_env() -> str:
55
+ """PATH for the service environment.
56
+
57
+ A service (systemd/launchd) runs with a minimal PATH that omits ~/.local/bin,
58
+ nvm/Homebrew, etc. — exactly where the ``claude`` CLI and its node/git/gh
59
+ runtime live. Prepend the resolved CLI's own directory and ~/.local/bin to the
60
+ standard system dirs so ``engine=claude_code`` works with no manual PATH edits.
61
+ """
62
+ base = ["/usr/local/sbin", "/usr/local/bin", "/usr/sbin", "/usr/bin", "/sbin", "/bin"]
63
+ extra = [str(Path.home() / ".local" / "bin")]
64
+ try:
65
+ from sdk.local_exec import claude_cli_path
66
+ cli = claude_cli_path()
67
+ if cli:
68
+ extra.append(str(Path(cli).resolve().parent))
69
+ except Exception: # noqa: BLE001 — discovery must never break install
70
+ pass
71
+ ordered: list[str] = []
72
+ for d in extra + base:
73
+ if d and d not in ordered:
74
+ ordered.append(d)
75
+ return ":".join(ordered)
76
+
77
+
54
78
  def _unit_text(host: str, port: int, home: str) -> str:
55
79
  exec_line = f"{_exe()} --host {host} --port {port} --no-browser"
56
80
  return (
@@ -64,6 +88,7 @@ def _unit_text(host: str, port: int, home: str) -> str:
64
88
  "Restart=always\n"
65
89
  "RestartSec=5\n"
66
90
  f"Environment=HOME={home}\n"
91
+ f"Environment=PATH={_service_path_env()}\n"
67
92
  f"WorkingDirectory={home}\n\n"
68
93
  "[Install]\n"
69
94
  f"WantedBy={{target}}\n"
@@ -145,6 +170,9 @@ def _install_launchd(host: str, port: int) -> tuple[bool, str]:
145
170
  f' <string>{exe}</string><string>--host</string><string>{host}</string>\n'
146
171
  f' <string>--port</string><string>{port}</string><string>--no-browser</string>\n'
147
172
  ' </array>\n'
173
+ ' <key>EnvironmentVariables</key><dict>\n'
174
+ f' <key>PATH</key><string>{_service_path_env()}</string>\n'
175
+ ' </dict>\n'
148
176
  ' <key>RunAtLoad</key><true/>\n'
149
177
  ' <key>KeepAlive</key><true/>\n'
150
178
  '</dict></plist>\n'
@@ -64,6 +64,19 @@ def sdk_version() -> str:
64
64
  return "unknown"
65
65
 
66
66
 
67
+ def claude_readiness() -> dict:
68
+ """Claude Code CLI readiness (installed + logged in) for the /status surface.
69
+
70
+ Guarded: any failure degrades to a not-ready dict rather than breaking /status.
71
+ """
72
+ try:
73
+ from sdk.local_exec import claude_status
74
+ return claude_status()
75
+ except Exception as exc: # noqa: BLE001
76
+ return {"installed": False, "logged_in": False, "path": None,
77
+ "auth_method": None, "email": None, "org": None, "error": str(exc)}
78
+
79
+
67
80
  def environment_info() -> dict:
68
81
  """Env snapshot for the /status endpoint and setup wizard."""
69
82
  vp = running_venv()
@@ -78,6 +91,7 @@ def environment_info() -> dict:
78
91
  "expected_agentsystem_root": str(root),
79
92
  "agentsystem_exists": root.exists(),
80
93
  "sdk_version": sdk_version(),
94
+ "claude": claude_readiness(),
81
95
  }
82
96
 
83
97