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.
Files changed (113) hide show
  1. connectonion/__init__.py +78 -0
  2. connectonion/address.py +320 -0
  3. connectonion/agent.py +450 -0
  4. connectonion/announce.py +84 -0
  5. connectonion/asgi.py +287 -0
  6. connectonion/auto_debug_exception.py +181 -0
  7. connectonion/cli/__init__.py +3 -0
  8. connectonion/cli/browser_agent/__init__.py +5 -0
  9. connectonion/cli/browser_agent/browser.py +243 -0
  10. connectonion/cli/browser_agent/prompt.md +107 -0
  11. connectonion/cli/commands/__init__.py +1 -0
  12. connectonion/cli/commands/auth_commands.py +527 -0
  13. connectonion/cli/commands/browser_commands.py +27 -0
  14. connectonion/cli/commands/create.py +511 -0
  15. connectonion/cli/commands/deploy_commands.py +220 -0
  16. connectonion/cli/commands/doctor_commands.py +173 -0
  17. connectonion/cli/commands/init.py +469 -0
  18. connectonion/cli/commands/project_cmd_lib.py +828 -0
  19. connectonion/cli/commands/reset_commands.py +149 -0
  20. connectonion/cli/commands/status_commands.py +168 -0
  21. connectonion/cli/docs/co-vibecoding-principles-docs-contexts-all-in-one.md +2010 -0
  22. connectonion/cli/docs/connectonion.md +1256 -0
  23. connectonion/cli/docs.md +123 -0
  24. connectonion/cli/main.py +148 -0
  25. connectonion/cli/templates/meta-agent/README.md +287 -0
  26. connectonion/cli/templates/meta-agent/agent.py +196 -0
  27. connectonion/cli/templates/meta-agent/prompts/answer_prompt.md +9 -0
  28. connectonion/cli/templates/meta-agent/prompts/docs_retrieve_prompt.md +15 -0
  29. connectonion/cli/templates/meta-agent/prompts/metagent.md +71 -0
  30. connectonion/cli/templates/meta-agent/prompts/think_prompt.md +18 -0
  31. connectonion/cli/templates/minimal/README.md +56 -0
  32. connectonion/cli/templates/minimal/agent.py +40 -0
  33. connectonion/cli/templates/playwright/README.md +118 -0
  34. connectonion/cli/templates/playwright/agent.py +336 -0
  35. connectonion/cli/templates/playwright/prompt.md +102 -0
  36. connectonion/cli/templates/playwright/requirements.txt +3 -0
  37. connectonion/cli/templates/web-research/agent.py +122 -0
  38. connectonion/connect.py +128 -0
  39. connectonion/console.py +539 -0
  40. connectonion/debug_agent/__init__.py +13 -0
  41. connectonion/debug_agent/agent.py +45 -0
  42. connectonion/debug_agent/prompts/debug_assistant.md +72 -0
  43. connectonion/debug_agent/runtime_inspector.py +406 -0
  44. connectonion/debug_explainer/__init__.py +10 -0
  45. connectonion/debug_explainer/explain_agent.py +114 -0
  46. connectonion/debug_explainer/explain_context.py +263 -0
  47. connectonion/debug_explainer/explainer_prompt.md +29 -0
  48. connectonion/debug_explainer/root_cause_analysis_prompt.md +43 -0
  49. connectonion/debugger_ui.py +1039 -0
  50. connectonion/decorators.py +208 -0
  51. connectonion/events.py +248 -0
  52. connectonion/execution_analyzer/__init__.py +9 -0
  53. connectonion/execution_analyzer/execution_analysis.py +93 -0
  54. connectonion/execution_analyzer/execution_analysis_prompt.md +47 -0
  55. connectonion/host.py +579 -0
  56. connectonion/interactive_debugger.py +342 -0
  57. connectonion/llm.py +801 -0
  58. connectonion/llm_do.py +307 -0
  59. connectonion/logger.py +300 -0
  60. connectonion/prompt_files/__init__.py +1 -0
  61. connectonion/prompt_files/analyze_contact.md +62 -0
  62. connectonion/prompt_files/eval_expected.md +12 -0
  63. connectonion/prompt_files/react_evaluate.md +11 -0
  64. connectonion/prompt_files/react_plan.md +16 -0
  65. connectonion/prompt_files/reflect.md +22 -0
  66. connectonion/prompts.py +144 -0
  67. connectonion/relay.py +200 -0
  68. connectonion/static/docs.html +688 -0
  69. connectonion/tool_executor.py +279 -0
  70. connectonion/tool_factory.py +186 -0
  71. connectonion/tool_registry.py +105 -0
  72. connectonion/trust.py +166 -0
  73. connectonion/trust_agents.py +71 -0
  74. connectonion/trust_functions.py +88 -0
  75. connectonion/tui/__init__.py +57 -0
  76. connectonion/tui/divider.py +39 -0
  77. connectonion/tui/dropdown.py +251 -0
  78. connectonion/tui/footer.py +31 -0
  79. connectonion/tui/fuzzy.py +56 -0
  80. connectonion/tui/input.py +278 -0
  81. connectonion/tui/keys.py +35 -0
  82. connectonion/tui/pick.py +130 -0
  83. connectonion/tui/providers.py +155 -0
  84. connectonion/tui/status_bar.py +163 -0
  85. connectonion/usage.py +161 -0
  86. connectonion/useful_events_handlers/__init__.py +16 -0
  87. connectonion/useful_events_handlers/reflect.py +116 -0
  88. connectonion/useful_plugins/__init__.py +20 -0
  89. connectonion/useful_plugins/calendar_plugin.py +163 -0
  90. connectonion/useful_plugins/eval.py +139 -0
  91. connectonion/useful_plugins/gmail_plugin.py +162 -0
  92. connectonion/useful_plugins/image_result_formatter.py +127 -0
  93. connectonion/useful_plugins/re_act.py +78 -0
  94. connectonion/useful_plugins/shell_approval.py +159 -0
  95. connectonion/useful_tools/__init__.py +44 -0
  96. connectonion/useful_tools/diff_writer.py +192 -0
  97. connectonion/useful_tools/get_emails.py +183 -0
  98. connectonion/useful_tools/gmail.py +1596 -0
  99. connectonion/useful_tools/google_calendar.py +613 -0
  100. connectonion/useful_tools/memory.py +380 -0
  101. connectonion/useful_tools/microsoft_calendar.py +604 -0
  102. connectonion/useful_tools/outlook.py +488 -0
  103. connectonion/useful_tools/send_email.py +205 -0
  104. connectonion/useful_tools/shell.py +97 -0
  105. connectonion/useful_tools/slash_command.py +201 -0
  106. connectonion/useful_tools/terminal.py +285 -0
  107. connectonion/useful_tools/todo_list.py +241 -0
  108. connectonion/useful_tools/web_fetch.py +216 -0
  109. connectonion/xray.py +467 -0
  110. connectonion-0.5.8.dist-info/METADATA +741 -0
  111. connectonion-0.5.8.dist-info/RECORD +113 -0
  112. connectonion-0.5.8.dist-info/WHEEL +4 -0
  113. 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")