connectonion 0.5.8__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.
- connectonion/__init__.py +78 -0
- connectonion/address.py +320 -0
- connectonion/agent.py +450 -0
- connectonion/announce.py +84 -0
- connectonion/asgi.py +287 -0
- connectonion/auto_debug_exception.py +181 -0
- connectonion/cli/__init__.py +3 -0
- connectonion/cli/browser_agent/__init__.py +5 -0
- connectonion/cli/browser_agent/browser.py +243 -0
- connectonion/cli/browser_agent/prompt.md +107 -0
- connectonion/cli/commands/__init__.py +1 -0
- connectonion/cli/commands/auth_commands.py +527 -0
- connectonion/cli/commands/browser_commands.py +27 -0
- connectonion/cli/commands/create.py +511 -0
- connectonion/cli/commands/deploy_commands.py +220 -0
- connectonion/cli/commands/doctor_commands.py +173 -0
- connectonion/cli/commands/init.py +469 -0
- connectonion/cli/commands/project_cmd_lib.py +828 -0
- connectonion/cli/commands/reset_commands.py +149 -0
- connectonion/cli/commands/status_commands.py +168 -0
- connectonion/cli/docs/co-vibecoding-principles-docs-contexts-all-in-one.md +2010 -0
- connectonion/cli/docs/connectonion.md +1256 -0
- connectonion/cli/docs.md +123 -0
- connectonion/cli/main.py +148 -0
- connectonion/cli/templates/meta-agent/README.md +287 -0
- connectonion/cli/templates/meta-agent/agent.py +196 -0
- connectonion/cli/templates/meta-agent/prompts/answer_prompt.md +9 -0
- connectonion/cli/templates/meta-agent/prompts/docs_retrieve_prompt.md +15 -0
- connectonion/cli/templates/meta-agent/prompts/metagent.md +71 -0
- connectonion/cli/templates/meta-agent/prompts/think_prompt.md +18 -0
- connectonion/cli/templates/minimal/README.md +56 -0
- connectonion/cli/templates/minimal/agent.py +40 -0
- connectonion/cli/templates/playwright/README.md +118 -0
- connectonion/cli/templates/playwright/agent.py +336 -0
- connectonion/cli/templates/playwright/prompt.md +102 -0
- connectonion/cli/templates/playwright/requirements.txt +3 -0
- connectonion/cli/templates/web-research/agent.py +122 -0
- connectonion/connect.py +128 -0
- connectonion/console.py +539 -0
- connectonion/debug_agent/__init__.py +13 -0
- connectonion/debug_agent/agent.py +45 -0
- connectonion/debug_agent/prompts/debug_assistant.md +72 -0
- connectonion/debug_agent/runtime_inspector.py +406 -0
- connectonion/debug_explainer/__init__.py +10 -0
- connectonion/debug_explainer/explain_agent.py +114 -0
- connectonion/debug_explainer/explain_context.py +263 -0
- connectonion/debug_explainer/explainer_prompt.md +29 -0
- connectonion/debug_explainer/root_cause_analysis_prompt.md +43 -0
- connectonion/debugger_ui.py +1039 -0
- connectonion/decorators.py +208 -0
- connectonion/events.py +248 -0
- connectonion/execution_analyzer/__init__.py +9 -0
- connectonion/execution_analyzer/execution_analysis.py +93 -0
- connectonion/execution_analyzer/execution_analysis_prompt.md +47 -0
- connectonion/host.py +579 -0
- connectonion/interactive_debugger.py +342 -0
- connectonion/llm.py +801 -0
- connectonion/llm_do.py +307 -0
- connectonion/logger.py +300 -0
- connectonion/prompt_files/__init__.py +1 -0
- connectonion/prompt_files/analyze_contact.md +62 -0
- connectonion/prompt_files/eval_expected.md +12 -0
- connectonion/prompt_files/react_evaluate.md +11 -0
- connectonion/prompt_files/react_plan.md +16 -0
- connectonion/prompt_files/reflect.md +22 -0
- connectonion/prompts.py +144 -0
- connectonion/relay.py +200 -0
- connectonion/static/docs.html +688 -0
- connectonion/tool_executor.py +279 -0
- connectonion/tool_factory.py +186 -0
- connectonion/tool_registry.py +105 -0
- connectonion/trust.py +166 -0
- connectonion/trust_agents.py +71 -0
- connectonion/trust_functions.py +88 -0
- connectonion/tui/__init__.py +57 -0
- connectonion/tui/divider.py +39 -0
- connectonion/tui/dropdown.py +251 -0
- connectonion/tui/footer.py +31 -0
- connectonion/tui/fuzzy.py +56 -0
- connectonion/tui/input.py +278 -0
- connectonion/tui/keys.py +35 -0
- connectonion/tui/pick.py +130 -0
- connectonion/tui/providers.py +155 -0
- connectonion/tui/status_bar.py +163 -0
- connectonion/usage.py +161 -0
- connectonion/useful_events_handlers/__init__.py +16 -0
- connectonion/useful_events_handlers/reflect.py +116 -0
- connectonion/useful_plugins/__init__.py +20 -0
- connectonion/useful_plugins/calendar_plugin.py +163 -0
- connectonion/useful_plugins/eval.py +139 -0
- connectonion/useful_plugins/gmail_plugin.py +162 -0
- connectonion/useful_plugins/image_result_formatter.py +127 -0
- connectonion/useful_plugins/re_act.py +78 -0
- connectonion/useful_plugins/shell_approval.py +159 -0
- connectonion/useful_tools/__init__.py +44 -0
- connectonion/useful_tools/diff_writer.py +192 -0
- connectonion/useful_tools/get_emails.py +183 -0
- connectonion/useful_tools/gmail.py +1596 -0
- connectonion/useful_tools/google_calendar.py +613 -0
- connectonion/useful_tools/memory.py +380 -0
- connectonion/useful_tools/microsoft_calendar.py +604 -0
- connectonion/useful_tools/outlook.py +488 -0
- connectonion/useful_tools/send_email.py +205 -0
- connectonion/useful_tools/shell.py +97 -0
- connectonion/useful_tools/slash_command.py +201 -0
- connectonion/useful_tools/terminal.py +285 -0
- connectonion/useful_tools/todo_list.py +241 -0
- connectonion/useful_tools/web_fetch.py +216 -0
- connectonion/xray.py +467 -0
- connectonion-0.5.8.dist-info/METADATA +741 -0
- connectonion-0.5.8.dist-info/RECORD +113 -0
- connectonion-0.5.8.dist-info/WHEEL +4 -0
- connectonion-0.5.8.dist-info/entry_points.txt +3 -0
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Deploy agent projects to ConnectOnion Cloud with git archive packaging and secrets management
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [os, subprocess, tempfile, time, toml, requests, pathlib, rich.console, dotenv] | imported by [cli/main.py via handle_deploy()] | calls backend at [https://oo.openonion.ai/api/v1/deploy]
|
|
5
|
+
Data flow: handle_deploy() → validates git repo and .co/config.toml → _get_api_key() loads OPENONION_API_KEY → reads config.toml for project name and secrets path → dotenv_values() loads secrets from .env → git archive creates tarball of HEAD → POST to /api/v1/deploy with tarball + project_name + secrets → polls /api/v1/deploy/{id}/status until running/error → displays agent URL
|
|
6
|
+
State/Effects: creates temporary tarball file in tempdir | reads .co/config.toml, .env files | makes network POST request | prints progress to stdout via rich.Console | does not modify project files
|
|
7
|
+
Integration: exposes handle_deploy() for CLI | expects git repo with .co/config.toml containing project.name, project.secrets, deploy.entrypoint | uses Bearer token auth | returns void (prints results)
|
|
8
|
+
Performance: git archive is fast | network timeout 600s for upload+build, 10s for status checks | polls every 3s up to 100 times (~5 min)
|
|
9
|
+
Errors: fails if not git repo | fails if not ConnectOnion project (.co/config.toml missing) | fails if no API key | prints backend error messages
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import re
|
|
15
|
+
import subprocess
|
|
16
|
+
import tempfile
|
|
17
|
+
import time
|
|
18
|
+
import toml
|
|
19
|
+
import requests
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from rich.console import Console
|
|
22
|
+
from dotenv import dotenv_values, load_dotenv
|
|
23
|
+
|
|
24
|
+
console = Console()
|
|
25
|
+
|
|
26
|
+
API_BASE = "https://oo.openonion.ai"
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _check_host_export(entrypoint: str) -> bool:
|
|
30
|
+
"""Check if entrypoint file exports an ASGI app via host().
|
|
31
|
+
|
|
32
|
+
Looks for patterns like:
|
|
33
|
+
- host(agent)
|
|
34
|
+
- host(my_agent)
|
|
35
|
+
- from connectonion import host
|
|
36
|
+
"""
|
|
37
|
+
entrypoint_path = Path(entrypoint)
|
|
38
|
+
if not entrypoint_path.exists():
|
|
39
|
+
return False
|
|
40
|
+
|
|
41
|
+
content = entrypoint_path.read_text()
|
|
42
|
+
|
|
43
|
+
# Check for host() call pattern
|
|
44
|
+
# Matches: host(agent), host(my_agent), host( agent ), etc.
|
|
45
|
+
host_call_pattern = r'\bhost\s*\([^)]+\)'
|
|
46
|
+
|
|
47
|
+
if re.search(host_call_pattern, content):
|
|
48
|
+
return True
|
|
49
|
+
|
|
50
|
+
return False
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def _get_api_key() -> str:
|
|
54
|
+
"""Get OPENONION_API_KEY from env or .env files."""
|
|
55
|
+
if api_key := os.getenv("OPENONION_API_KEY"):
|
|
56
|
+
return api_key
|
|
57
|
+
|
|
58
|
+
for env_path in [Path(".env"), Path.home() / ".co" / "keys.env"]:
|
|
59
|
+
if env_path.exists():
|
|
60
|
+
load_dotenv(env_path)
|
|
61
|
+
if api_key := os.getenv("OPENONION_API_KEY"):
|
|
62
|
+
return api_key
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def handle_deploy():
|
|
67
|
+
"""Deploy agent to ConnectOnion Cloud."""
|
|
68
|
+
console.print("\n[cyan]Deploying to ConnectOnion Cloud...[/cyan]\n")
|
|
69
|
+
|
|
70
|
+
project_dir = Path.cwd()
|
|
71
|
+
|
|
72
|
+
# Must be a git repo
|
|
73
|
+
if not (project_dir / ".git").exists():
|
|
74
|
+
console.print("[red]Not a git repository. Run 'git init' first.[/red]")
|
|
75
|
+
return
|
|
76
|
+
|
|
77
|
+
# Must be a ConnectOnion project
|
|
78
|
+
config_path = Path(".co") / "config.toml"
|
|
79
|
+
if not config_path.exists():
|
|
80
|
+
console.print("[red]Not a ConnectOnion project. Run 'co init' first.[/red]")
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
# Must have API key
|
|
84
|
+
api_key = _get_api_key()
|
|
85
|
+
if not api_key:
|
|
86
|
+
console.print("[red]No API key. Run 'co auth' first.[/red]")
|
|
87
|
+
return
|
|
88
|
+
|
|
89
|
+
config = toml.load(config_path)
|
|
90
|
+
project_name = config.get("project", {}).get("name", "unnamed-agent")
|
|
91
|
+
secrets_path = config.get("project", {}).get("secrets", ".env")
|
|
92
|
+
entrypoint = config.get("deploy", {}).get("entrypoint", "agent.py")
|
|
93
|
+
|
|
94
|
+
# Validate entrypoint exists
|
|
95
|
+
if not Path(entrypoint).exists():
|
|
96
|
+
console.print(f"[red]Entrypoint not found: {entrypoint}[/red]")
|
|
97
|
+
console.print("[dim]Set entrypoint in .co/config.toml under [deploy][/dim]")
|
|
98
|
+
return
|
|
99
|
+
|
|
100
|
+
# Validate entrypoint exports ASGI app via host()
|
|
101
|
+
if not _check_host_export(entrypoint):
|
|
102
|
+
console.print(f"[red]Entrypoint '{entrypoint}' does not export an ASGI app.[/red]")
|
|
103
|
+
console.print()
|
|
104
|
+
console.print("[yellow]To deploy, your agent must call host():[/yellow]")
|
|
105
|
+
console.print()
|
|
106
|
+
console.print(" [cyan]from connectonion import Agent, host[/cyan]")
|
|
107
|
+
console.print()
|
|
108
|
+
console.print(" [cyan]agent = Agent('my-agent', ...)[/cyan]")
|
|
109
|
+
console.print(" [cyan]host(agent) # Starts HTTP server[/cyan]")
|
|
110
|
+
console.print()
|
|
111
|
+
console.print("[dim]See: https://docs.connectonion.com/deploy[/dim]")
|
|
112
|
+
return
|
|
113
|
+
|
|
114
|
+
# Load secrets from .env
|
|
115
|
+
secrets = dotenv_values(secrets_path) if Path(secrets_path).exists() else {}
|
|
116
|
+
|
|
117
|
+
# Create tarball from git
|
|
118
|
+
tarball_path = Path(tempfile.mkdtemp()) / "agent.tar.gz"
|
|
119
|
+
subprocess.run(
|
|
120
|
+
["git", "archive", "--format=tar.gz", "-o", str(tarball_path), "HEAD"],
|
|
121
|
+
cwd=project_dir,
|
|
122
|
+
check=True,
|
|
123
|
+
)
|
|
124
|
+
|
|
125
|
+
# Show package size
|
|
126
|
+
tarball_size = tarball_path.stat().st_size
|
|
127
|
+
if tarball_size < 1024:
|
|
128
|
+
size_str = f"{tarball_size} B"
|
|
129
|
+
elif tarball_size < 1024 * 1024:
|
|
130
|
+
size_str = f"{tarball_size / 1024:.1f} KB"
|
|
131
|
+
else:
|
|
132
|
+
size_str = f"{tarball_size / (1024 * 1024):.2f} MB"
|
|
133
|
+
|
|
134
|
+
console.print(f" Project: {project_name}")
|
|
135
|
+
console.print(f" Package: {size_str}")
|
|
136
|
+
console.print(f" Secrets: {len(secrets)} keys")
|
|
137
|
+
console.print()
|
|
138
|
+
|
|
139
|
+
# Upload
|
|
140
|
+
console.print("Uploading...")
|
|
141
|
+
with open(tarball_path, "rb") as f:
|
|
142
|
+
response = requests.post(
|
|
143
|
+
f"{API_BASE}/api/v1/deploy",
|
|
144
|
+
files={"package": ("agent.tar.gz", f, "application/gzip")},
|
|
145
|
+
data={
|
|
146
|
+
"project_name": project_name,
|
|
147
|
+
"secrets": json.dumps(secrets),
|
|
148
|
+
"entrypoint": entrypoint,
|
|
149
|
+
},
|
|
150
|
+
headers={"Authorization": f"Bearer {api_key}"},
|
|
151
|
+
timeout=600, # 10 minutes for docker build
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
if response.status_code != 200:
|
|
155
|
+
console.print(f"[red]Deploy failed: {response.text}[/red]")
|
|
156
|
+
return
|
|
157
|
+
|
|
158
|
+
result = response.json()
|
|
159
|
+
|
|
160
|
+
# Check for error response (backend returns 200 with error dict)
|
|
161
|
+
if "error" in result:
|
|
162
|
+
console.print(f"[red]Deploy failed: {result['error']}[/red]")
|
|
163
|
+
return
|
|
164
|
+
|
|
165
|
+
deployment_id = result.get("id")
|
|
166
|
+
url = result.get("url", "")
|
|
167
|
+
|
|
168
|
+
# Wait for deployment
|
|
169
|
+
console.print("Building...")
|
|
170
|
+
deploy_success = False
|
|
171
|
+
final_status = "unknown"
|
|
172
|
+
timeout_count = 0
|
|
173
|
+
|
|
174
|
+
for _ in range(100):
|
|
175
|
+
try:
|
|
176
|
+
status_resp = requests.get(
|
|
177
|
+
f"{API_BASE}/api/v1/deploy/{deployment_id}/status",
|
|
178
|
+
headers={"Authorization": f"Bearer {api_key}"},
|
|
179
|
+
timeout=30, # Increased timeout for slow SSH
|
|
180
|
+
)
|
|
181
|
+
except requests.exceptions.Timeout:
|
|
182
|
+
timeout_count += 1
|
|
183
|
+
if timeout_count >= 3:
|
|
184
|
+
console.print("[yellow]Status checks timing out, but deploy may still succeed.[/yellow]")
|
|
185
|
+
break
|
|
186
|
+
time.sleep(3)
|
|
187
|
+
continue
|
|
188
|
+
except requests.exceptions.RequestException as e:
|
|
189
|
+
console.print(f"[yellow]Network error: {e}[/yellow]")
|
|
190
|
+
time.sleep(3)
|
|
191
|
+
continue
|
|
192
|
+
|
|
193
|
+
if status_resp.status_code != 200:
|
|
194
|
+
console.print(f"[red]Status check failed: {status_resp.status_code}[/red]")
|
|
195
|
+
break
|
|
196
|
+
|
|
197
|
+
result = status_resp.json()
|
|
198
|
+
final_status = result.get("status", "unknown")
|
|
199
|
+
|
|
200
|
+
if final_status == "running":
|
|
201
|
+
deploy_success = True
|
|
202
|
+
# Update URL from status response (may be more up-to-date)
|
|
203
|
+
url = result.get("url") or url
|
|
204
|
+
break
|
|
205
|
+
if final_status in ("error", "failed"):
|
|
206
|
+
console.print(f"[red]Deploy failed: {result.get('error_message', 'Unknown error')}[/red]")
|
|
207
|
+
return
|
|
208
|
+
time.sleep(3)
|
|
209
|
+
|
|
210
|
+
console.print()
|
|
211
|
+
if deploy_success:
|
|
212
|
+
console.print("[bold green]Deployed![/bold green]")
|
|
213
|
+
else:
|
|
214
|
+
console.print(f"[yellow]Deploy status: {final_status}[/yellow]")
|
|
215
|
+
console.print("[yellow]Check status with 'co deployments' or try again.[/yellow]")
|
|
216
|
+
|
|
217
|
+
# Always show URL if we have one
|
|
218
|
+
if url:
|
|
219
|
+
console.print(f"Agent URL: {url}")
|
|
220
|
+
console.print()
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Diagnose ConnectOnion installation and configuration issues
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [sys, os, shutil, pathlib, requests, rich.console, rich.panel, rich.table, __version__] | imported by [cli/main.py via handle_doctor()] | checks local files and backend connectivity
|
|
5
|
+
Data flow: receives no args → checks system info → checks config files → checks API key → tests backend connectivity → displays results with ✓/✗ indicators
|
|
6
|
+
State/Effects: no state modifications | reads from filesystem | makes HTTP request | writes to stdout via rich.Console
|
|
7
|
+
Integration: exposes handle_doctor() for CLI | helps users self-diagnose setup issues
|
|
8
|
+
Performance: fast local checks (<100ms) | network check to backend (1-2s)
|
|
9
|
+
Errors: lets errors crash naturally - no try-except unless absolutely needed
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import sys
|
|
13
|
+
import os
|
|
14
|
+
import shutil
|
|
15
|
+
from pathlib import Path
|
|
16
|
+
import requests
|
|
17
|
+
from rich.console import Console
|
|
18
|
+
from rich.panel import Panel
|
|
19
|
+
from rich.table import Table
|
|
20
|
+
from rich import box
|
|
21
|
+
|
|
22
|
+
console = Console()
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def handle_doctor():
|
|
26
|
+
"""Run comprehensive diagnostics on ConnectOnion installation."""
|
|
27
|
+
from ... import __version__
|
|
28
|
+
|
|
29
|
+
console.print("\n[bold cyan]🔍 ConnectOnion Diagnostics[/bold cyan]\n")
|
|
30
|
+
|
|
31
|
+
# System checks
|
|
32
|
+
system_table = Table(show_header=False, box=box.SIMPLE, padding=(0, 1))
|
|
33
|
+
system_table.add_column("Check", style="cyan")
|
|
34
|
+
system_table.add_column("Status")
|
|
35
|
+
|
|
36
|
+
# Version
|
|
37
|
+
system_table.add_row("Version", f"[green]✓[/green] {__version__}")
|
|
38
|
+
|
|
39
|
+
# Python
|
|
40
|
+
python_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
|
|
41
|
+
python_path = sys.executable
|
|
42
|
+
system_table.add_row("Python", f"[green]✓[/green] {python_version}")
|
|
43
|
+
system_table.add_row("Python Path", f"[dim]{python_path}[/dim]")
|
|
44
|
+
|
|
45
|
+
# Virtual environment
|
|
46
|
+
in_venv = hasattr(sys, 'real_prefix') or (hasattr(sys, 'base_prefix') and sys.base_prefix != sys.prefix)
|
|
47
|
+
venv_status = "[green]✓[/green] Virtual environment" if in_venv else "[yellow]○[/yellow] Global Python"
|
|
48
|
+
system_table.add_row("Environment", venv_status)
|
|
49
|
+
if in_venv:
|
|
50
|
+
system_table.add_row("Venv Path", f"[dim]{sys.prefix}[/dim]")
|
|
51
|
+
|
|
52
|
+
# Command location
|
|
53
|
+
co_path = shutil.which('co')
|
|
54
|
+
if co_path:
|
|
55
|
+
system_table.add_row("Command", f"[green]✓[/green] {co_path}")
|
|
56
|
+
else:
|
|
57
|
+
system_table.add_row("Command", "[red]✗[/red] 'co' not found in PATH")
|
|
58
|
+
|
|
59
|
+
# Package location
|
|
60
|
+
import connectonion
|
|
61
|
+
package_path = Path(connectonion.__file__).parent
|
|
62
|
+
system_table.add_row("Package", f"[dim]{package_path}[/dim]")
|
|
63
|
+
|
|
64
|
+
console.print(Panel(system_table, title="[bold]System[/bold]", border_style="blue"))
|
|
65
|
+
console.print()
|
|
66
|
+
|
|
67
|
+
# Configuration checks
|
|
68
|
+
config_table = Table(show_header=False, box=box.SIMPLE, padding=(0, 1))
|
|
69
|
+
config_table.add_column("Check", style="cyan")
|
|
70
|
+
config_table.add_column("Status")
|
|
71
|
+
|
|
72
|
+
# Check for config.toml
|
|
73
|
+
local_config = Path(".co") / "config.toml"
|
|
74
|
+
global_config = Path.home() / ".co" / "config.toml"
|
|
75
|
+
|
|
76
|
+
if local_config.exists():
|
|
77
|
+
config_table.add_row("Config", f"[green]✓[/green] {local_config}")
|
|
78
|
+
import toml
|
|
79
|
+
config = toml.load(local_config)
|
|
80
|
+
agent_name = config.get("agent", {}).get("name", "Not set")
|
|
81
|
+
config_table.add_row("Agent Name", f"[dim]{agent_name}[/dim]")
|
|
82
|
+
elif global_config.exists():
|
|
83
|
+
config_table.add_row("Config", f"[green]✓[/green] {global_config}")
|
|
84
|
+
else:
|
|
85
|
+
config_table.add_row("Config", "[yellow]○[/yellow] Not found (optional)")
|
|
86
|
+
|
|
87
|
+
# Check for keys
|
|
88
|
+
local_keys = Path(".co") / "keys" / "agent.key"
|
|
89
|
+
global_keys = Path.home() / ".co" / "keys" / "agent.key"
|
|
90
|
+
|
|
91
|
+
if local_keys.exists():
|
|
92
|
+
config_table.add_row("Keys", f"[green]✓[/green] {local_keys}")
|
|
93
|
+
elif global_keys.exists():
|
|
94
|
+
config_table.add_row("Keys", f"[green]✓[/green] {global_keys}")
|
|
95
|
+
else:
|
|
96
|
+
config_table.add_row("Keys", "[yellow]○[/yellow] Not found (run 'co auth' to create)")
|
|
97
|
+
|
|
98
|
+
# Check for API key
|
|
99
|
+
api_key = os.getenv("OPENONION_API_KEY")
|
|
100
|
+
if api_key:
|
|
101
|
+
api_key_display = f"{api_key[:20]}..." if len(api_key) > 20 else api_key
|
|
102
|
+
config_table.add_row("API Key", f"[green]✓[/green] Found in environment")
|
|
103
|
+
config_table.add_row("Key Preview", f"[dim]{api_key_display}[/dim]")
|
|
104
|
+
else:
|
|
105
|
+
# Check .env files
|
|
106
|
+
from dotenv import load_dotenv
|
|
107
|
+
local_env = Path(".env")
|
|
108
|
+
global_env = Path.home() / ".co" / "keys.env"
|
|
109
|
+
|
|
110
|
+
if local_env.exists():
|
|
111
|
+
load_dotenv(local_env)
|
|
112
|
+
api_key = os.getenv("OPENONION_API_KEY")
|
|
113
|
+
if api_key:
|
|
114
|
+
config_table.add_row("API Key", f"[green]✓[/green] Found in .env")
|
|
115
|
+
|
|
116
|
+
if not api_key and global_env.exists():
|
|
117
|
+
load_dotenv(global_env)
|
|
118
|
+
api_key = os.getenv("OPENONION_API_KEY")
|
|
119
|
+
if api_key:
|
|
120
|
+
config_table.add_row("API Key", f"[green]✓[/green] Found in ~/.co/keys.env")
|
|
121
|
+
|
|
122
|
+
if not api_key:
|
|
123
|
+
config_table.add_row("API Key", "[yellow]○[/yellow] Not configured (run 'co auth')")
|
|
124
|
+
|
|
125
|
+
console.print(Panel(config_table, title="[bold]Configuration[/bold]", border_style="green"))
|
|
126
|
+
console.print()
|
|
127
|
+
|
|
128
|
+
# Connectivity checks (only if API key exists)
|
|
129
|
+
if api_key:
|
|
130
|
+
connectivity_table = Table(show_header=False, box=box.SIMPLE, padding=(0, 1))
|
|
131
|
+
connectivity_table.add_column("Check", style="cyan")
|
|
132
|
+
connectivity_table.add_column("Status")
|
|
133
|
+
|
|
134
|
+
# Check backend reachability
|
|
135
|
+
response = requests.get("https://oo.openonion.ai/health", timeout=5)
|
|
136
|
+
if response.status_code == 200:
|
|
137
|
+
connectivity_table.add_row("Backend", "[green]✓[/green] https://oo.openonion.ai")
|
|
138
|
+
else:
|
|
139
|
+
connectivity_table.add_row("Backend", f"[yellow]⚠[/yellow] Status {response.status_code}")
|
|
140
|
+
|
|
141
|
+
# Check authentication (if keys exist)
|
|
142
|
+
if local_keys.exists() or global_keys.exists():
|
|
143
|
+
from ... import address
|
|
144
|
+
import time
|
|
145
|
+
|
|
146
|
+
co_dir = Path(".co") if local_keys.exists() else Path.home() / ".co"
|
|
147
|
+
addr_data = address.load(co_dir)
|
|
148
|
+
|
|
149
|
+
public_key = addr_data["address"]
|
|
150
|
+
timestamp = int(time.time())
|
|
151
|
+
message = f"ConnectOnion-Auth-{public_key}-{timestamp}"
|
|
152
|
+
signature = address.sign(addr_data, message.encode()).hex()
|
|
153
|
+
|
|
154
|
+
response = requests.post(
|
|
155
|
+
"https://oo.openonion.ai/api/v1/auth",
|
|
156
|
+
json={
|
|
157
|
+
"public_key": public_key,
|
|
158
|
+
"signature": signature,
|
|
159
|
+
"message": message
|
|
160
|
+
},
|
|
161
|
+
timeout=5
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
if response.status_code == 200:
|
|
165
|
+
connectivity_table.add_row("Authentication", "[green]✓[/green] Valid credentials")
|
|
166
|
+
else:
|
|
167
|
+
connectivity_table.add_row("Authentication", f"[red]✗[/red] Failed (status {response.status_code})")
|
|
168
|
+
|
|
169
|
+
console.print(Panel(connectivity_table, title="[bold]Connectivity[/bold]", border_style="magenta"))
|
|
170
|
+
console.print()
|
|
171
|
+
|
|
172
|
+
console.print("[bold green]✅ Diagnostics complete![/bold green]\n")
|
|
173
|
+
console.print("[dim]Run 'co auth' if you need to authenticate[/dim]\n")
|