connectonion 0.6.1__py3-none-any.whl → 0.6.3__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 +46 -9
- connectonion/cli/__init__.py +11 -1
- connectonion/cli/browser_agent/__init__.py +11 -1
- connectonion/cli/browser_agent/browser.py +95 -142
- connectonion/cli/browser_agent/element_finder.py +147 -0
- connectonion/cli/browser_agent/highlight_screenshot.py +182 -0
- connectonion/cli/browser_agent/prompt.md +188 -105
- connectonion/cli/browser_agent/prompts/element_matcher.md +59 -0
- connectonion/cli/browser_agent/prompts/form_filler.md +19 -0
- connectonion/cli/browser_agent/prompts/scroll_strategy.md +36 -0
- connectonion/cli/browser_agent/scripts/extract_elements.js +126 -0
- connectonion/cli/browser_agent/scroll.py +145 -0
- connectonion/cli/co_ai/__init__.py +6 -0
- connectonion/cli/co_ai/agent.py +87 -0
- connectonion/cli/co_ai/agents/__init__.py +5 -0
- connectonion/cli/co_ai/agents/registry.py +57 -0
- connectonion/cli/co_ai/commands/__init__.py +45 -0
- connectonion/cli/co_ai/commands/compact.py +173 -0
- connectonion/cli/co_ai/commands/cost.py +77 -0
- connectonion/cli/co_ai/commands/export.py +60 -0
- connectonion/cli/co_ai/commands/help.py +80 -0
- connectonion/cli/co_ai/commands/init.py +101 -0
- connectonion/cli/co_ai/commands/sessions.py +55 -0
- connectonion/cli/co_ai/commands/tasks.py +63 -0
- connectonion/cli/co_ai/commands/undo.py +103 -0
- connectonion/cli/co_ai/context.py +127 -0
- connectonion/cli/co_ai/main.py +52 -0
- connectonion/cli/co_ai/plugins/__init__.py +6 -0
- connectonion/cli/co_ai/plugins/reminder.py +76 -0
- connectonion/cli/co_ai/plugins/shell_approval.py +105 -0
- connectonion/cli/co_ai/prompts/agents/explore.md +79 -0
- connectonion/cli/co_ai/prompts/agents/plan.md +60 -0
- connectonion/cli/co_ai/prompts/assembler.py +303 -0
- connectonion/cli/{docs/co-vibecoding-principles-docs-contexts-all-in-one.md → co_ai/prompts/connectonion/README.md} +26 -0
- connectonion/cli/co_ai/prompts/connectonion/api.md +457 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/README.md +805 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/auth.md +46 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/browser.md +235 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/copy.md +184 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/create.md +335 -0
- connectonion/cli/co_ai/prompts/connectonion/cli/init.md +431 -0
- connectonion/cli/co_ai/prompts/connectonion/co-directory-structure.md +214 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/agent.md +1078 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/events.md +816 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/llm_do.md +256 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/max_iterations.md +362 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/models.md +641 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/plugins.md +100 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/prompts.md +122 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/tools.md +512 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/transcribe.md +156 -0
- connectonion/cli/co_ai/prompts/connectonion/concepts/trust.md +291 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/README.md +18 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/auto_debug.md +1026 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/console.md +129 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/eval-format.md +178 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/eval.md +230 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/exceptions.md +307 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/log.md +117 -0
- connectonion/cli/co_ai/prompts/connectonion/debug/xray.md +215 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/001-choosing-input-method.md +202 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/002-choosing-llm-function-name.md +202 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/003-choosing-trust-keyword.md +141 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/004-cli-create-flow.md +117 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/005-designing-agent-network-protocol.md +503 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/006-agent-address-format.md +305 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/007-authentication-backend-design.md +240 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/008-naming-is-hard.md +228 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/009-why-connect-function.md +167 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/010-cli-ux-progressive-disclosure.md +176 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/011-global-config-identity-management.md +357 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/012-tool-execution-separation.md +259 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/013-debug-and-logging-design.md +253 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/014-hook-system-design.md +510 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/015-interactive-auto-debug-design.md +837 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/016-why-no-zero-knowledge-proofs.md +358 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/017-session-logging-and-eval-format.md +120 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/018-event-api-naming.md +274 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/019-agent-lifecycle-design.md +655 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/020-trust-system-and-network-architecture.md +503 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/021-task-storage-jsonl-design.md +496 -0
- connectonion/cli/co_ai/prompts/connectonion/design-decisions/022-raw-asgi-implementation.md +273 -0
- connectonion/cli/co_ai/prompts/connectonion/examples/agent_reasoning.md +62 -0
- connectonion/cli/co_ai/prompts/connectonion/examples/atomic_tools.md +24 -0
- connectonion/cli/co_ai/prompts/connectonion/examples/load_guide.md +18 -0
- connectonion/cli/co_ai/prompts/connectonion/examples.md +0 -0
- connectonion/cli/co_ai/prompts/connectonion/hook-system-options.md +364 -0
- connectonion/cli/co_ai/prompts/connectonion/index.md +162 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/README.md +12 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/auth.md +450 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/google.md +431 -0
- connectonion/cli/co_ai/prompts/connectonion/integrations/microsoft.md +370 -0
- connectonion/cli/co_ai/prompts/connectonion/network/README.md +14 -0
- connectonion/cli/co_ai/prompts/connectonion/network/connect.md +543 -0
- connectonion/cli/co_ai/prompts/connectonion/network/connection.md +538 -0
- connectonion/cli/co_ai/prompts/connectonion/network/deploy.md +123 -0
- connectonion/cli/co_ai/prompts/connectonion/network/host.md +1049 -0
- connectonion/cli/co_ai/prompts/connectonion/network/protocol/agent-relay-protocol.md +495 -0
- connectonion/cli/co_ai/prompts/connectonion/network/protocol/announce-message.md +115 -0
- connectonion/cli/co_ai/prompts/connectonion/principles.md +124 -0
- connectonion/cli/co_ai/prompts/connectonion/quickstart.md +261 -0
- connectonion/cli/co_ai/prompts/connectonion/roadmap.md +81 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/README.md +77 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/meta-agent.md +152 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/minimal.md +105 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/playwright.md +130 -0
- connectonion/cli/co_ai/prompts/connectonion/templates/web-research.md +144 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/README.md +95 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/chat.md +181 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/divider.md +63 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/dropdown.md +83 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/footer.md +44 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/fuzzy.md +68 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/input.md +84 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/keys.md +77 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/pick.md +71 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/providers.md +89 -0
- connectonion/cli/co_ai/prompts/connectonion/tui/status_bar.md +67 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/README.md +156 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/calendar_plugin.md +68 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/eval.md +89 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/gmail_plugin.md +68 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/image_result_formatter.md +74 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/re_act.md +86 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_plugins/shell_approval.md +69 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/README.md +81 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/diff_writer.md +138 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/get_emails.md +499 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/gmail.md +135 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/google_calendar.md +106 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/memory.md +486 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/microsoft_calendar.md +106 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/outlook.md +120 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/send_email.md +403 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/shell.md +95 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/slash_command.md +96 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/terminal.md +97 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/todo_list.md +252 -0
- connectonion/cli/co_ai/prompts/connectonion/useful_tools/web_fetch.md +130 -0
- connectonion/cli/co_ai/prompts/connectonion/vibe-coding-guide.md +97 -0
- connectonion/cli/co_ai/prompts/connectonion/windows-support.md +258 -0
- connectonion/cli/co_ai/prompts/main.md +247 -0
- connectonion/cli/co_ai/prompts/reminders/plan_mode.md +34 -0
- connectonion/cli/co_ai/prompts/summarization.md +55 -0
- connectonion/cli/co_ai/prompts/tools/ask_user.md +61 -0
- connectonion/cli/co_ai/prompts/tools/background.md +57 -0
- connectonion/cli/co_ai/prompts/tools/edit.md +90 -0
- connectonion/cli/co_ai/prompts/tools/glob.md +52 -0
- connectonion/cli/co_ai/prompts/tools/grep.md +55 -0
- connectonion/cli/co_ai/prompts/tools/plan_mode.md +80 -0
- connectonion/cli/co_ai/prompts/tools/read.md +40 -0
- connectonion/cli/co_ai/prompts/tools/shell.md +67 -0
- connectonion/cli/co_ai/prompts/tools/task.md +51 -0
- connectonion/cli/co_ai/prompts/tools/todo.md +139 -0
- connectonion/cli/co_ai/prompts/tools/write.md +47 -0
- connectonion/cli/co_ai/prompts/workflow.md +89 -0
- connectonion/cli/co_ai/reminders.py +159 -0
- connectonion/cli/co_ai/sessions.py +110 -0
- connectonion/cli/co_ai/skills/__init__.py +37 -0
- connectonion/cli/co_ai/skills/builtin/commit/SKILL.md +63 -0
- connectonion/cli/co_ai/skills/builtin/review-pr/SKILL.md +76 -0
- connectonion/cli/co_ai/skills/loader.py +166 -0
- connectonion/cli/co_ai/skills/tool.py +46 -0
- connectonion/cli/co_ai/tools/__init__.py +92 -0
- connectonion/cli/co_ai/tools/ask_user.py +35 -0
- connectonion/cli/co_ai/tools/background.py +201 -0
- connectonion/cli/co_ai/tools/diff_writer.py +291 -0
- connectonion/cli/co_ai/tools/edit.py +89 -0
- connectonion/cli/co_ai/tools/glob.py +84 -0
- connectonion/cli/co_ai/tools/grep.py +158 -0
- connectonion/cli/co_ai/tools/load_guide.py +23 -0
- connectonion/cli/co_ai/tools/multi_edit.py +116 -0
- connectonion/cli/co_ai/tools/plan_mode.py +172 -0
- connectonion/cli/co_ai/tools/read.py +67 -0
- connectonion/cli/co_ai/tools/task.py +59 -0
- connectonion/cli/co_ai/tools/todo_list.py +159 -0
- connectonion/cli/co_ai/tools/write.py +126 -0
- connectonion/cli/commands/__init__.py +11 -1
- connectonion/cli/commands/ai_commands.py +34 -0
- connectonion/cli/commands/copy_commands.py +55 -6
- connectonion/cli/commands/create.py +20 -17
- connectonion/cli/commands/init.py +19 -22
- connectonion/cli/commands/project_cmd_lib.py +15 -0
- connectonion/cli/main.py +11 -0
- connectonion/console.py +15 -1
- connectonion/core/__init__.py +10 -1
- connectonion/core/agent.py +37 -16
- connectonion/core/exceptions.py +74 -0
- connectonion/core/llm.py +54 -6
- connectonion/core/tool_executor.py +32 -31
- connectonion/core/tool_factory.py +47 -10
- connectonion/debug/__init__.py +10 -1
- connectonion/debug/debug_explainer/__init__.py +10 -1
- connectonion/debug/execution_analyzer/__init__.py +10 -1
- connectonion/debug/execution_analyzer/execution_analysis.py +5 -2
- connectonion/debug/runtime_inspector/__init__.py +10 -1
- connectonion/docs/.package-ignore +6 -0
- connectonion/docs/README.md +2036 -0
- connectonion/docs/api.md +457 -0
- connectonion/docs/archive/001-ai-agent-is-just-prompt-plus-function.md +249 -0
- connectonion/docs/archive/README.md +53 -0
- connectonion/docs/archive/archive/consolidation-plan.md +72 -0
- connectonion/docs/archive/archive/core-principles-extracted.md +239 -0
- connectonion/docs/archive/archive/master-principles.md +222 -0
- connectonion/docs/archive/archive/principles.md +293 -0
- connectonion/docs/archive/archive/simplicity-principles.md +221 -0
- connectonion/docs/archive/attack-defense-insights.md +410 -0
- connectonion/docs/archive/business-model.md +305 -0
- connectonion/docs/archive/core-principles-unified.md +190 -0
- connectonion/docs/archive/discussion-journey.md +178 -0
- connectonion/docs/archive/economic-analysis.md +323 -0
- connectonion/docs/archive/features/01-share-and-find.md +256 -0
- connectonion/docs/archive/features/02-agent-authentication.md +93 -0
- connectonion/docs/archive/features/03-test-before-trust.md +71 -0
- connectonion/docs/archive/features/06-reliability-and-offline.md +197 -0
- connectonion/docs/archive/features/README.md +46 -0
- connectonion/docs/archive/features-roadmap.md +247 -0
- connectonion/docs/archive/mcp-comparison-insights.md +215 -0
- connectonion/docs/archive/migration-strategy.md +571 -0
- connectonion/docs/archive/mini-whitepaper.md +293 -0
- connectonion/docs/archive/network-protocol.md +394 -0
- connectonion/docs/archive/semantic-revolution.md +367 -0
- connectonion/docs/archive/technical-architecture.md +453 -0
- connectonion/docs/archive/the-semantic-insight.md +207 -0
- connectonion/docs/archive/threat-model.md +164 -0
- connectonion/docs/cli/README.md +805 -0
- connectonion/docs/cli/auth.md +46 -0
- connectonion/docs/cli/browser.md +235 -0
- connectonion/docs/cli/copy.md +232 -0
- connectonion/docs/cli/create.md +335 -0
- connectonion/docs/cli/init.md +431 -0
- connectonion/docs/co-directory-structure.md +214 -0
- connectonion/docs/concepts/agent.md +1078 -0
- connectonion/docs/concepts/events.md +699 -0
- connectonion/docs/concepts/llm_do.md +256 -0
- connectonion/docs/concepts/max_iterations.md +362 -0
- connectonion/docs/concepts/models.md +641 -0
- connectonion/docs/concepts/plugins.md +100 -0
- connectonion/docs/concepts/prompts.md +122 -0
- connectonion/docs/concepts/session.md +428 -0
- connectonion/docs/concepts/tools.md +512 -0
- connectonion/docs/concepts/transcribe.md +156 -0
- connectonion/docs/concepts/trust.md +291 -0
- connectonion/docs/connectonion.md +1256 -0
- connectonion/docs/debug/README.md +18 -0
- connectonion/docs/debug/auto_debug.md +1026 -0
- connectonion/docs/debug/console.md +129 -0
- connectonion/docs/debug/eval-format.md +178 -0
- connectonion/docs/debug/eval.md +230 -0
- connectonion/docs/debug/exceptions.md +307 -0
- connectonion/docs/debug/log.md +117 -0
- connectonion/docs/debug/xray.md +215 -0
- connectonion/docs/design-decisions/001-choosing-input-method.md +202 -0
- connectonion/docs/design-decisions/002-choosing-llm-function-name.md +202 -0
- connectonion/docs/design-decisions/003-choosing-trust-keyword.md +141 -0
- connectonion/docs/design-decisions/004-cli-create-flow.md +117 -0
- connectonion/docs/design-decisions/005-designing-agent-network-protocol.md +503 -0
- connectonion/docs/design-decisions/006-agent-address-format.md +305 -0
- connectonion/docs/design-decisions/007-authentication-backend-design.md +240 -0
- connectonion/docs/design-decisions/008-naming-is-hard.md +228 -0
- connectonion/docs/design-decisions/009-why-connect-function.md +167 -0
- connectonion/docs/design-decisions/010-cli-ux-progressive-disclosure.md +176 -0
- connectonion/docs/design-decisions/011-global-config-identity-management.md +357 -0
- connectonion/docs/design-decisions/012-tool-execution-separation.md +259 -0
- connectonion/docs/design-decisions/013-debug-and-logging-design.md +253 -0
- connectonion/docs/design-decisions/014-hook-system-design.md +510 -0
- connectonion/docs/design-decisions/015-interactive-auto-debug-design.md +837 -0
- connectonion/docs/design-decisions/016-why-no-zero-knowledge-proofs.md +358 -0
- connectonion/docs/design-decisions/017-session-logging-and-eval-format.md +120 -0
- connectonion/docs/design-decisions/018-event-api-naming.md +274 -0
- connectonion/docs/design-decisions/019-agent-lifecycle-design.md +655 -0
- connectonion/docs/design-decisions/020-trust-system-and-network-architecture.md +503 -0
- connectonion/docs/design-decisions/021-task-storage-jsonl-design.md +496 -0
- connectonion/docs/design-decisions/022-raw-asgi-implementation.md +273 -0
- connectonion/docs/examples.md +0 -0
- connectonion/docs/hook-system-options.md +364 -0
- connectonion/docs/integrations/README.md +12 -0
- connectonion/docs/integrations/auth.md +450 -0
- connectonion/docs/integrations/google.md +431 -0
- connectonion/docs/integrations/microsoft.md +370 -0
- connectonion/docs/network/README.md +14 -0
- connectonion/docs/network/connect.md +629 -0
- connectonion/docs/network/deploy.md +124 -0
- connectonion/docs/network/host.md +1087 -0
- connectonion/docs/network/io.md +538 -0
- connectonion/docs/network/protocol/agent-relay-protocol.md +495 -0
- connectonion/docs/network/protocol/announce-message.md +115 -0
- connectonion/docs/principles.md +124 -0
- connectonion/docs/quickstart.md +261 -0
- connectonion/docs/roadmap.md +81 -0
- connectonion/docs/templates/README.md +77 -0
- connectonion/docs/templates/meta-agent.md +152 -0
- connectonion/docs/templates/minimal.md +105 -0
- connectonion/docs/templates/playwright.md +130 -0
- connectonion/docs/templates/web-research.md +144 -0
- connectonion/docs/tui/README.md +95 -0
- connectonion/docs/tui/chat.md +181 -0
- connectonion/docs/tui/divider.md +63 -0
- connectonion/docs/tui/dropdown.md +83 -0
- connectonion/docs/tui/footer.md +44 -0
- connectonion/docs/tui/fuzzy.md +68 -0
- connectonion/docs/tui/input.md +84 -0
- connectonion/docs/tui/keys.md +77 -0
- connectonion/docs/tui/pick.md +71 -0
- connectonion/docs/tui/providers.md +89 -0
- connectonion/docs/tui/status_bar.md +67 -0
- connectonion/docs/useful_plugins/README.md +160 -0
- connectonion/docs/useful_plugins/calendar_plugin.md +68 -0
- connectonion/docs/useful_plugins/eval.md +89 -0
- connectonion/docs/useful_plugins/gmail_plugin.md +68 -0
- connectonion/docs/useful_plugins/image_result_formatter.md +74 -0
- connectonion/docs/useful_plugins/re_act.md +86 -0
- connectonion/docs/useful_plugins/shell_approval.md +69 -0
- connectonion/docs/useful_plugins/system_reminder.md +210 -0
- connectonion/docs/useful_prompts/README.md +127 -0
- connectonion/docs/useful_prompts/coding_agent.md +214 -0
- connectonion/docs/useful_tools/README.md +81 -0
- connectonion/docs/useful_tools/ask_user.md +103 -0
- connectonion/docs/useful_tools/diff_writer.md +158 -0
- connectonion/docs/useful_tools/get_emails.md +519 -0
- connectonion/docs/useful_tools/gmail.md +155 -0
- connectonion/docs/useful_tools/google_calendar.md +126 -0
- connectonion/docs/useful_tools/memory.md +506 -0
- connectonion/docs/useful_tools/microsoft_calendar.md +126 -0
- connectonion/docs/useful_tools/outlook.md +140 -0
- connectonion/docs/useful_tools/send_email.md +423 -0
- connectonion/docs/useful_tools/shell.md +115 -0
- connectonion/docs/useful_tools/slash_command.md +116 -0
- connectonion/docs/useful_tools/terminal.md +115 -0
- connectonion/docs/useful_tools/todo_list.md +272 -0
- connectonion/docs/useful_tools/web_fetch.md +150 -0
- connectonion/docs/vibe-coding-guide.md +97 -0
- connectonion/docs/windows-support.md +258 -0
- connectonion/logger.py +3 -3
- connectonion/network/__init__.py +19 -6
- connectonion/network/asgi/__init__.py +81 -0
- connectonion/network/asgi/http.py +205 -0
- connectonion/network/asgi/websocket.py +217 -0
- connectonion/network/connect.py +232 -185
- connectonion/network/host/__init__.py +59 -0
- connectonion/network/host/auth.py +191 -0
- connectonion/network/host/routes.py +135 -0
- connectonion/network/host/server.py +289 -0
- connectonion/network/host/session.py +78 -0
- connectonion/network/io/__init__.py +21 -0
- connectonion/network/{connection.py → io/base.py} +17 -42
- connectonion/network/io/websocket.py +55 -0
- connectonion/network/relay.py +37 -16
- connectonion/network/trust/__init__.py +30 -0
- connectonion/network/trust/factory.py +138 -0
- connectonion/network/{trust_agents.py → trust/prompts.py} +3 -3
- connectonion/network/{trust_functions.py → trust/tools.py} +2 -2
- connectonion/prompt_files/__init__.py +11 -1
- connectonion/prompt_files/react_acknowledge.md +26 -0
- connectonion/prompts.py +10 -1
- connectonion/tui/chat.py +10 -1
- connectonion/tui/divider.py +10 -1
- connectonion/tui/dropdown.py +10 -1
- connectonion/tui/footer.py +8 -0
- connectonion/tui/fuzzy.py +11 -1
- connectonion/tui/input.py +118 -70
- connectonion/tui/keys.py +133 -6
- connectonion/tui/providers.py +11 -1
- connectonion/tui/status_bar.py +10 -1
- connectonion/useful_events_handlers/__init__.py +8 -0
- connectonion/useful_events_handlers/reflect.py +19 -4
- connectonion/useful_plugins/__init__.py +2 -1
- connectonion/useful_plugins/eval.py +2 -2
- connectonion/useful_plugins/gmail_plugin.py +3 -3
- connectonion/useful_plugins/image_result_formatter.py +3 -3
- connectonion/useful_plugins/re_act.py +114 -28
- connectonion/useful_plugins/shell_approval.py +2 -2
- connectonion/useful_plugins/system_reminder.py +103 -0
- connectonion/useful_plugins/ui_stream.py +18 -133
- connectonion/useful_prompts/README.md +61 -0
- connectonion/useful_prompts/__init__.py +45 -0
- connectonion/useful_prompts/coding_agent/README.md +106 -0
- connectonion/useful_prompts/coding_agent/assembler.py +123 -0
- connectonion/useful_prompts/coding_agent/prompts/main.md +227 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/ask_user.md +61 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/background.md +57 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/edit.md +90 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/glob.md +52 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/grep.md +55 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/plan_mode.md +80 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/read.md +40 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/shell.md +67 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/task.md +51 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/todo.md +139 -0
- connectonion/useful_prompts/coding_agent/prompts/tools/write.md +48 -0
- connectonion/useful_prompts/system-reminders/security-warning.md +14 -0
- connectonion/useful_prompts/system-reminders/test-reminder.md +11 -0
- connectonion/useful_tools/__init__.py +31 -4
- connectonion/useful_tools/ask_user.py +35 -0
- connectonion/useful_tools/bash.py +69 -0
- connectonion/useful_tools/diff_writer.py +186 -94
- connectonion/useful_tools/edit.py +102 -0
- connectonion/useful_tools/glob_files.py +97 -0
- connectonion/useful_tools/grep_files.py +171 -0
- connectonion/useful_tools/multi_edit.py +116 -0
- connectonion/useful_tools/read_file.py +73 -0
- connectonion/useful_tools/shell.py +50 -45
- connectonion/useful_tools/write_file.py +129 -0
- {connectonion-0.6.1.dist-info → connectonion-0.6.3.dist-info}/METADATA +10 -3
- connectonion-0.6.3.dist-info/RECORD +469 -0
- connectonion/cli/browser_agent/scroll_strategies.py +0 -276
- connectonion/network/asgi.py +0 -407
- connectonion/network/host.py +0 -616
- connectonion/network/trust.py +0 -166
- connectonion-0.6.1.dist-info/RECORD +0 -123
- /connectonion/cli/{docs → co_ai/prompts/connectonion}/connectonion.md +0 -0
- {connectonion-0.6.1.dist-info → connectonion-0.6.3.dist-info}/WHEEL +0 -0
- {connectonion-0.6.1.dist-info → connectonion-0.6.3.dist-info}/entry_points.txt +0 -0
connectonion/__init__.py
CHANGED
|
@@ -1,13 +1,23 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Main package entry point exposing public API for ConnectOnion framework
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [core/, logger.py, llm_do.py, transcribe.py, prompts.py, debug/, useful_tools/, network/, address.py] | imported by [user code, tests/, examples/] | no direct tests (integration tests import from here)
|
|
5
|
+
Data flow: loads .env from cwd via load_dotenv() → exports all public API symbols → user imports `from connectonion import Agent, llm_do, ...`
|
|
6
|
+
State/Effects: auto-loads .env file from current working directory (NOT module directory) at import time
|
|
7
|
+
Integration: exposes complete public API: Agent, LLM, Logger, create_tool_from_function, llm_do, transcribe, xray, event decorators, built-in tools, networking functions | __all__ defines explicit public exports
|
|
8
|
+
Performance: .env loading happens once at first import (dotenv caches)
|
|
9
|
+
Errors: none (import errors bubble from submodules)
|
|
10
|
+
ConnectOnion - A simple agent framework with behavior tracking.
|
|
11
|
+
"""
|
|
2
12
|
|
|
3
|
-
__version__ = "0.6.
|
|
13
|
+
__version__ = "0.6.3"
|
|
4
14
|
|
|
5
15
|
# Auto-load .env files for the entire framework
|
|
6
16
|
from dotenv import load_dotenv
|
|
7
17
|
from pathlib import Path as _Path
|
|
8
18
|
|
|
9
|
-
#
|
|
10
|
-
#
|
|
19
|
+
# SDK only loads from current working directory (where user runs their script)
|
|
20
|
+
# CLI commands (co ai, co browser, etc.) handle global fallback separately
|
|
11
21
|
load_dotenv(_Path.cwd() / ".env")
|
|
12
22
|
|
|
13
23
|
from .core import Agent, LLM, create_tool_from_function
|
|
@@ -27,12 +37,20 @@ from .llm_do import llm_do
|
|
|
27
37
|
from .transcribe import transcribe
|
|
28
38
|
from .prompts import load_system_prompt
|
|
29
39
|
from .debug import xray, auto_debug_exception, replay, xray_replay
|
|
30
|
-
from .useful_tools import
|
|
31
|
-
|
|
40
|
+
from .useful_tools import (
|
|
41
|
+
send_email, get_emails, mark_read, mark_unread,
|
|
42
|
+
Memory, Gmail, GoogleCalendar, Outlook, MicrosoftCalendar,
|
|
43
|
+
WebFetch, Shell, bash, DiffWriter, MODE_NORMAL, MODE_AUTO, MODE_PLAN,
|
|
44
|
+
pick, yes_no, autocomplete, TodoList, SlashCommand,
|
|
45
|
+
# Claude Code-style file tools
|
|
46
|
+
read_file, edit, multi_edit, glob, grep, write, FileWriter,
|
|
47
|
+
)
|
|
48
|
+
from .network import connect, RemoteAgent, Response, host, create_app, IO
|
|
32
49
|
from .network import relay, announce
|
|
33
50
|
from . import address
|
|
34
51
|
|
|
35
52
|
__all__ = [
|
|
53
|
+
# Core
|
|
36
54
|
"Agent",
|
|
37
55
|
"LLM",
|
|
38
56
|
"Logger",
|
|
@@ -43,10 +61,13 @@ __all__ = [
|
|
|
43
61
|
"xray",
|
|
44
62
|
"replay",
|
|
45
63
|
"xray_replay",
|
|
64
|
+
"auto_debug_exception",
|
|
65
|
+
# Email tools
|
|
46
66
|
"send_email",
|
|
47
67
|
"get_emails",
|
|
48
68
|
"mark_read",
|
|
49
69
|
"mark_unread",
|
|
70
|
+
# Class-based tools
|
|
50
71
|
"Memory",
|
|
51
72
|
"Gmail",
|
|
52
73
|
"GoogleCalendar",
|
|
@@ -54,21 +75,37 @@ __all__ = [
|
|
|
54
75
|
"MicrosoftCalendar",
|
|
55
76
|
"WebFetch",
|
|
56
77
|
"Shell",
|
|
78
|
+
"bash",
|
|
57
79
|
"DiffWriter",
|
|
80
|
+
"MODE_NORMAL",
|
|
81
|
+
"MODE_AUTO",
|
|
82
|
+
"MODE_PLAN",
|
|
83
|
+
# TUI helpers
|
|
58
84
|
"pick",
|
|
59
85
|
"yes_no",
|
|
60
86
|
"autocomplete",
|
|
87
|
+
# Task management
|
|
61
88
|
"TodoList",
|
|
62
89
|
"SlashCommand",
|
|
63
|
-
|
|
90
|
+
# Claude Code-style file tools
|
|
91
|
+
"read_file",
|
|
92
|
+
"edit",
|
|
93
|
+
"multi_edit",
|
|
94
|
+
"glob",
|
|
95
|
+
"grep",
|
|
96
|
+
"write",
|
|
97
|
+
"FileWriter",
|
|
98
|
+
# Networking
|
|
64
99
|
"connect",
|
|
65
100
|
"RemoteAgent",
|
|
101
|
+
"Response",
|
|
66
102
|
"host",
|
|
67
103
|
"create_app",
|
|
68
|
-
"
|
|
104
|
+
"IO",
|
|
69
105
|
"relay",
|
|
70
106
|
"announce",
|
|
71
107
|
"address",
|
|
108
|
+
# Event decorators
|
|
72
109
|
"after_user_input",
|
|
73
110
|
"before_llm",
|
|
74
111
|
"after_llm",
|
|
@@ -77,5 +114,5 @@ __all__ = [
|
|
|
77
114
|
"after_each_tool",
|
|
78
115
|
"after_tools",
|
|
79
116
|
"on_error",
|
|
80
|
-
"on_complete"
|
|
117
|
+
"on_complete",
|
|
81
118
|
]
|
connectonion/cli/__init__.py
CHANGED
|
@@ -1,3 +1,13 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Purpose: CLI module entry point with version metadata
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: none | imported by [cli/main.py, setup.py] | no direct tests
|
|
5
|
+
Data flow: pure metadata (version string)
|
|
6
|
+
State/Effects: no state
|
|
7
|
+
Integration: exposes __version__ for CLI about/version commands
|
|
8
|
+
Performance: trivial
|
|
9
|
+
Errors: none
|
|
10
|
+
ConnectOnion CLI module.
|
|
11
|
+
"""
|
|
2
12
|
|
|
3
13
|
__version__ = "0.0.1b5"
|
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Browser agent module exports for CLI browser automation
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [browser.py] | imported by [cli/commands/browser_commands.py] | no direct tests
|
|
5
|
+
Data flow: re-exports execute_browser_command and BrowserAutomation
|
|
6
|
+
State/Effects: no state
|
|
7
|
+
Integration: exposes execute_browser_command(), BrowserAutomation class for `co browser` command
|
|
8
|
+
Performance: trivial
|
|
9
|
+
Errors: none
|
|
10
|
+
Browser agent module for ConnectOnion CLI.
|
|
11
|
+
"""
|
|
2
12
|
|
|
3
13
|
from .browser import execute_browser_command, BrowserAutomation
|
|
4
14
|
|
|
@@ -1,4 +1,13 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Natural language browser automation via Playwright with Chrome profile support
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [playwright.sync_api, connectonion Agent/llm_do, cli/browser_agent/element_finder, pydantic, pathlib, dotenv] | imported by [cli/commands/browser_commands.py] | tested by [tests/cli/test_browser_agent.py]
|
|
5
|
+
Data flow: BrowserAutomation() initializes Playwright → copies Chrome profile to .browser_agent_profile/ → opens browser with context → provides tools (navigate, find_element, fill_form, screenshot, scroll, wait_for_login) → Agent uses these tools via natural language → element_finder.py uses vision LLM to locate elements | screenshots saved to .tmp/ directory
|
|
6
|
+
State/Effects: maintains browser/page/context state | copies Chrome profile on first run | writes screenshots to .tmp/{timestamp}.png | modifies form_data dict for form fills | auto-closes browser in __del__
|
|
7
|
+
Integration: exposes BrowserAutomation(use_chrome_profile, headless) with methods: navigate(url), find_element(description), fill_form(field_values), screenshot(viewport), scroll(direction, description), click(description), type_text(description, text), wait_for_login(seconds) | FormField Pydantic model for form parsing | used by `co browser` CLI command
|
|
8
|
+
Performance: headless by default (faster) | Chrome profile copy (one-time, slow first run) | vision model for element finding (slower but accurate) | screenshots base64-encoded for LLM analysis
|
|
9
|
+
Errors: returns error string if Playwright not installed | returns "Browser already open" if reinitializing | element not found returns descriptive error | Chrome must be closed when using profile
|
|
10
|
+
Browser Agent for CLI - Natural language browser automation.
|
|
2
11
|
|
|
3
12
|
This module provides a browser automation agent that understands natural language
|
|
4
13
|
requests for browser operations via the ConnectOnion CLI.
|
|
@@ -20,6 +29,7 @@ from typing import Optional, List, Dict, Any
|
|
|
20
29
|
from connectonion import Agent, llm_do
|
|
21
30
|
from dotenv import load_dotenv
|
|
22
31
|
from pydantic import BaseModel, Field
|
|
32
|
+
from . import element_finder
|
|
23
33
|
|
|
24
34
|
# Default screenshots directory
|
|
25
35
|
SCREENSHOTS_DIR = Path.cwd() / ".tmp"
|
|
@@ -53,7 +63,7 @@ class BrowserAutomation:
|
|
|
53
63
|
Supports Chrome profile for persistent sessions.
|
|
54
64
|
"""
|
|
55
65
|
|
|
56
|
-
def __init__(self, use_chrome_profile: bool =
|
|
66
|
+
def __init__(self, use_chrome_profile: bool = True, headless: bool = True):
|
|
57
67
|
"""Initialize browser automation.
|
|
58
68
|
|
|
59
69
|
Args:
|
|
@@ -95,8 +105,9 @@ class BrowserAutomation:
|
|
|
95
105
|
self.playwright = sync_playwright().start()
|
|
96
106
|
|
|
97
107
|
if self.use_chrome_profile:
|
|
98
|
-
# Use Chromium with Chrome profile copy
|
|
99
|
-
chromium_profile = Path.
|
|
108
|
+
# Use Chromium with Chrome profile copy in global ~/.co/ folder
|
|
109
|
+
chromium_profile = Path.home() / ".co" / "browser_profile"
|
|
110
|
+
chromium_profile.parent.mkdir(parents=True, exist_ok=True)
|
|
100
111
|
|
|
101
112
|
if not chromium_profile.exists():
|
|
102
113
|
import shutil
|
|
@@ -109,17 +120,28 @@ class BrowserAutomation:
|
|
|
109
120
|
source_profile = home / ".config/google-chrome"
|
|
110
121
|
|
|
111
122
|
if source_profile.exists():
|
|
123
|
+
def safe_copy(src, dst):
|
|
124
|
+
try:
|
|
125
|
+
shutil.copy2(src, dst)
|
|
126
|
+
except:
|
|
127
|
+
pass # Skip any file that can't be copied
|
|
128
|
+
|
|
112
129
|
shutil.copytree(
|
|
113
130
|
source_profile,
|
|
114
131
|
chromium_profile,
|
|
115
|
-
ignore=shutil.ignore_patterns(
|
|
132
|
+
ignore=shutil.ignore_patterns(
|
|
133
|
+
'*Cache*', '*cache*', 'Service Worker', 'ShaderCache',
|
|
134
|
+
'Singleton*', '*lock*', '*Lock*', '*.tmp', 'GPUCache',
|
|
135
|
+
'Code Cache', 'DawnCache', 'GrShaderCache', 'blob_storage'
|
|
136
|
+
),
|
|
137
|
+
copy_function=safe_copy,
|
|
116
138
|
dirs_exist_ok=True
|
|
117
139
|
)
|
|
118
140
|
|
|
119
141
|
self.browser = self.playwright.chromium.launch_persistent_context(
|
|
120
142
|
str(chromium_profile),
|
|
121
143
|
headless=headless,
|
|
122
|
-
args=['--disable-blink-features=AutomationControlled'],
|
|
144
|
+
args=['--no-sandbox', '--disable-setuid-sandbox', '--disable-blink-features=AutomationControlled'],
|
|
123
145
|
ignore_default_args=['--enable-automation'],
|
|
124
146
|
timeout=120000,
|
|
125
147
|
)
|
|
@@ -127,10 +149,12 @@ class BrowserAutomation:
|
|
|
127
149
|
self.page.add_init_script("""
|
|
128
150
|
Object.defineProperty(navigator, 'webdriver', { get: () => undefined });
|
|
129
151
|
""")
|
|
152
|
+
self.page.set_viewport_size({"width": 1920, "height": 1080})
|
|
130
153
|
return f"Browser opened with Chrome profile: {chromium_profile}"
|
|
131
154
|
else:
|
|
132
155
|
self.browser = self.playwright.chromium.launch(headless=headless)
|
|
133
156
|
self.page = self.browser.new_page()
|
|
157
|
+
self.page.set_viewport_size({"width": 1920, "height": 1080})
|
|
134
158
|
return "Browser opened successfully"
|
|
135
159
|
|
|
136
160
|
def go_to(self, url: str) -> str:
|
|
@@ -149,88 +173,94 @@ class BrowserAutomation:
|
|
|
149
173
|
def find_element_by_description(self, description: str) -> str:
|
|
150
174
|
"""Find element using natural language description.
|
|
151
175
|
|
|
152
|
-
Uses
|
|
176
|
+
Uses element_finder: LLM selects from indexed list, never generates CSS.
|
|
153
177
|
|
|
154
178
|
Args:
|
|
155
179
|
description: e.g., "the submit button", "email input field"
|
|
156
180
|
|
|
157
181
|
Returns:
|
|
158
|
-
|
|
182
|
+
Pre-built locator string, or error message
|
|
159
183
|
"""
|
|
160
184
|
if not self.page:
|
|
161
185
|
return "Browser not open"
|
|
162
186
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
confidence: float = Field(..., description="Confidence score 0-1")
|
|
168
|
-
explanation: str = Field(..., description="Why this element matches")
|
|
169
|
-
|
|
170
|
-
result = llm_do(
|
|
171
|
-
f"""Analyze this HTML and find the CSS selector for: "{description}"
|
|
172
|
-
|
|
173
|
-
HTML (first 15000 chars): {html[:15000]}
|
|
174
|
-
|
|
175
|
-
Return the most specific CSS selector that uniquely identifies this element.
|
|
176
|
-
""",
|
|
177
|
-
output=ElementSelector,
|
|
178
|
-
model="gpt-4o",
|
|
179
|
-
temperature=0.1
|
|
180
|
-
)
|
|
181
|
-
|
|
182
|
-
if self.page.locator(result.selector).count() > 0:
|
|
183
|
-
return result.selector
|
|
184
|
-
else:
|
|
185
|
-
return f"Found selector {result.selector} but element not on page"
|
|
187
|
+
element = element_finder.find_element(self.page, description)
|
|
188
|
+
if element:
|
|
189
|
+
return element.locator
|
|
190
|
+
return f"Could not find element matching: {description}"
|
|
186
191
|
|
|
187
192
|
def click(self, description: str) -> str:
|
|
188
193
|
"""Click on an element using natural language description.
|
|
189
194
|
|
|
190
|
-
|
|
191
|
-
description: e.g., "the blue submit button", "link to contact page"
|
|
195
|
+
Uses element_finder: LLM selects from pre-built locators, never generates CSS.
|
|
192
196
|
"""
|
|
193
197
|
if not self.page:
|
|
194
198
|
return "Browser not open"
|
|
195
199
|
|
|
196
|
-
|
|
200
|
+
element = element_finder.find_element(self.page, description)
|
|
197
201
|
|
|
198
|
-
if
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
202
|
+
if not element:
|
|
203
|
+
# Fallback to simple text matching
|
|
204
|
+
text_locator = self.page.get_by_text(description)
|
|
205
|
+
if text_locator.count() > 0:
|
|
206
|
+
text_locator.first.click()
|
|
207
|
+
return f"Clicked on '{description}' (by text fallback)"
|
|
208
|
+
return f"Could not find element matching: {description}"
|
|
203
209
|
|
|
204
|
-
|
|
205
|
-
|
|
210
|
+
# Try the locator with fresh bounding box
|
|
211
|
+
locator = self.page.locator(element.locator)
|
|
212
|
+
|
|
213
|
+
if locator.count() > 0:
|
|
214
|
+
box = locator.first.bounding_box()
|
|
215
|
+
if box:
|
|
216
|
+
x = box['x'] + box['width'] / 2
|
|
217
|
+
y = box['y'] + box['height'] / 2
|
|
218
|
+
self.page.mouse.click(x, y)
|
|
219
|
+
return f"Clicked [{element.index}] {element.tag} '{element.text}'"
|
|
220
|
+
|
|
221
|
+
locator.first.click(force=True)
|
|
222
|
+
return f"Clicked [{element.index}] {element.tag} '{element.text}' (force)"
|
|
223
|
+
|
|
224
|
+
# Fallback: use original coordinates
|
|
225
|
+
x = element.x + element.width // 2
|
|
226
|
+
y = element.y + element.height // 2
|
|
227
|
+
self.page.mouse.click(x, y)
|
|
228
|
+
return f"Clicked [{element.index}] '{element.text}' at ({x}, {y})"
|
|
206
229
|
|
|
207
230
|
def type_text(self, field_description: str, text: str) -> str:
|
|
208
231
|
"""Type text into a form field.
|
|
209
232
|
|
|
210
|
-
|
|
211
|
-
field_description: e.g., "email field", "password input"
|
|
212
|
-
text: The text to type
|
|
233
|
+
Uses element_finder: LLM selects from pre-built locators, never generates CSS.
|
|
213
234
|
"""
|
|
214
235
|
if not self.page:
|
|
215
236
|
return "Browser not open"
|
|
216
237
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
if
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
238
|
+
element = element_finder.find_element(self.page, field_description)
|
|
239
|
+
|
|
240
|
+
if not element:
|
|
241
|
+
# Fallback to placeholder matching
|
|
242
|
+
placeholder_locator = self.page.get_by_placeholder(field_description)
|
|
243
|
+
if placeholder_locator.count() > 0:
|
|
244
|
+
placeholder_locator.first.fill(text)
|
|
245
|
+
self.form_data[field_description] = text
|
|
246
|
+
return f"Typed into '{field_description}'"
|
|
247
|
+
return f"Could not find field: {field_description}"
|
|
248
|
+
|
|
249
|
+
# Try the pre-built locator
|
|
250
|
+
locator = self.page.locator(element.locator)
|
|
251
|
+
|
|
252
|
+
if locator.count() > 0:
|
|
253
|
+
locator.first.fill(text)
|
|
254
|
+
self.form_data[field_description] = text
|
|
255
|
+
return f"Typed into [{element.index}] {element.tag}"
|
|
256
|
+
|
|
257
|
+
# Fallback: click then type
|
|
258
|
+
x = element.x + element.width // 2
|
|
259
|
+
y = element.y + element.height // 2
|
|
260
|
+
self.page.mouse.click(x, y)
|
|
261
|
+
self.page.keyboard.type(text)
|
|
232
262
|
self.form_data[field_description] = text
|
|
233
|
-
return f"Typed into {
|
|
263
|
+
return f"Typed into [{element.index}] at ({x}, {y})"
|
|
234
264
|
|
|
235
265
|
def get_text(self) -> str:
|
|
236
266
|
"""Get all visible text from the page."""
|
|
@@ -306,27 +336,6 @@ class BrowserAutomation:
|
|
|
306
336
|
self.page.set_viewport_size({"width": width, "height": height})
|
|
307
337
|
return f"Viewport set to {width}x{height}"
|
|
308
338
|
|
|
309
|
-
def screenshot_mobile(self, url: str = None) -> str:
|
|
310
|
-
"""Take screenshot with iPhone viewport (390x844)."""
|
|
311
|
-
if url:
|
|
312
|
-
self.go_to(url)
|
|
313
|
-
self.set_viewport(390, 844)
|
|
314
|
-
return self.take_screenshot()
|
|
315
|
-
|
|
316
|
-
def screenshot_tablet(self, url: str = None) -> str:
|
|
317
|
-
"""Take screenshot with iPad viewport (768x1024)."""
|
|
318
|
-
if url:
|
|
319
|
-
self.go_to(url)
|
|
320
|
-
self.set_viewport(768, 1024)
|
|
321
|
-
return self.take_screenshot()
|
|
322
|
-
|
|
323
|
-
def screenshot_desktop(self, url: str = None) -> str:
|
|
324
|
-
"""Take screenshot with desktop viewport (1920x1080)."""
|
|
325
|
-
if url:
|
|
326
|
-
self.go_to(url)
|
|
327
|
-
self.set_viewport(1920, 1080)
|
|
328
|
-
return self.take_screenshot()
|
|
329
|
-
|
|
330
339
|
def find_forms(self) -> List[FormField]:
|
|
331
340
|
"""Find all form fields on the current page."""
|
|
332
341
|
if not self.page:
|
|
@@ -440,69 +449,13 @@ class BrowserAutomation:
|
|
|
440
449
|
return f"Waited for {seconds} seconds"
|
|
441
450
|
|
|
442
451
|
def scroll(self, times: int = 5, description: str = "the main content area") -> str:
|
|
443
|
-
"""Universal scroll with
|
|
444
|
-
|
|
445
|
-
Tries multiple strategies until one works:
|
|
446
|
-
1. AI-generated strategy (analyzes page structure)
|
|
447
|
-
2. Element scrolling
|
|
448
|
-
3. Page scrolling
|
|
452
|
+
"""Universal scroll with AI strategy and fallback.
|
|
449
453
|
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
description: What to scroll (e.g., "the email list")
|
|
453
|
-
|
|
454
|
-
Returns:
|
|
455
|
-
Status message with successful strategy
|
|
454
|
+
Tries: AI-generated → Element scroll → Page scroll
|
|
455
|
+
Verifies success with screenshot comparison.
|
|
456
456
|
"""
|
|
457
|
-
from . import
|
|
458
|
-
return
|
|
459
|
-
page=self.page,
|
|
460
|
-
take_screenshot=self.take_screenshot,
|
|
461
|
-
times=times,
|
|
462
|
-
description=description
|
|
463
|
-
)
|
|
464
|
-
|
|
465
|
-
def scroll_page(self, direction: str = "down", amount: int = 1000) -> str:
|
|
466
|
-
"""Scroll the page in a direction.
|
|
467
|
-
|
|
468
|
-
Args:
|
|
469
|
-
direction: "down", "up", "top", or "bottom"
|
|
470
|
-
amount: Pixels to scroll (ignored for "bottom"/"top")
|
|
471
|
-
"""
|
|
472
|
-
if not self.page:
|
|
473
|
-
return "Browser not open"
|
|
474
|
-
|
|
475
|
-
if direction == "bottom":
|
|
476
|
-
self.page.evaluate("window.scrollTo(0, document.body.scrollHeight)")
|
|
477
|
-
return "Scrolled to bottom of page"
|
|
478
|
-
elif direction == "top":
|
|
479
|
-
self.page.evaluate("window.scrollTo(0, 0)")
|
|
480
|
-
return "Scrolled to top of page"
|
|
481
|
-
elif direction == "down":
|
|
482
|
-
self.page.evaluate(f"window.scrollBy(0, {amount})")
|
|
483
|
-
return f"Scrolled down {amount} pixels"
|
|
484
|
-
elif direction == "up":
|
|
485
|
-
self.page.evaluate(f"window.scrollBy(0, -{amount})")
|
|
486
|
-
return f"Scrolled up {amount} pixels"
|
|
487
|
-
else:
|
|
488
|
-
return f"Unknown direction: {direction}"
|
|
489
|
-
|
|
490
|
-
def scroll_element(self, selector: str, amount: int = 1000) -> str:
|
|
491
|
-
"""Scroll a specific element by CSS selector."""
|
|
492
|
-
if not self.page:
|
|
493
|
-
return "Browser not open"
|
|
494
|
-
|
|
495
|
-
result = self.page.evaluate(f"""
|
|
496
|
-
(() => {{
|
|
497
|
-
const element = document.querySelector('{selector}');
|
|
498
|
-
if (!element) return 'Element not found: {selector}';
|
|
499
|
-
const beforeScroll = element.scrollTop;
|
|
500
|
-
element.scrollTop += {amount};
|
|
501
|
-
const afterScroll = element.scrollTop;
|
|
502
|
-
return `Scrolled from ${{beforeScroll}}px to ${{afterScroll}}px`;
|
|
503
|
-
}})()
|
|
504
|
-
""")
|
|
505
|
-
return result
|
|
457
|
+
from . import scroll
|
|
458
|
+
return scroll.scroll(self.page, self.take_screenshot, times, description)
|
|
506
459
|
|
|
507
460
|
def wait_for_manual_login(self, site_name: str = "the website") -> str:
|
|
508
461
|
"""Pause automation for user to login manually.
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Purpose: Find interactive elements on web pages using natural language descriptions via vision LLM
|
|
3
|
+
LLM-Note:
|
|
4
|
+
Dependencies: imports from [playwright.sync_api Page, connectonion llm_do, pydantic, pathlib] | imported by [cli/browser_agent/browser.py] | tested by [tests/cli/test_element_finder.py]
|
|
5
|
+
Data flow: extract_elements(page) → evaluates extract_elements.js → injects data-browser-agent-id on all interactive elements → returns list[InteractiveElement] with locators | find_element(page, description, elements) → formats elements for LLM → calls llm_do with vision model + screenshot → LLM selects matching element by index → returns InteractiveElement with pre-built locator
|
|
6
|
+
State/Effects: modifies DOM by injecting data-browser-agent-id attributes (temporary, removed on navigation) | takes screenshot for vision analysis | no persistent state
|
|
7
|
+
Integration: exposes extract_elements(page) → list[InteractiveElement], find_element(page, description, elements, screenshot) → InteractiveElement|None, highlight_element(page, element) for visual feedback | InteractiveElement model has tag, text, role, aria_label, placeholder, x, y, width, height, locator | ElementMatch model for LLM response
|
|
8
|
+
Performance: JavaScript extraction is fast | LLM matching uses vision model (slower) | limits to 150 elements for context window | pre-built locators (no retry needed)
|
|
9
|
+
Errors: returns None if no matching element found | raises if Playwright page not available | element may be stale if page navigates
|
|
10
|
+
Element Finder - Find interactive elements by natural language description.
|
|
11
|
+
|
|
12
|
+
Inspired by browser-use (https://github.com/browser-use/browser-use).
|
|
13
|
+
|
|
14
|
+
Architecture:
|
|
15
|
+
1. JavaScript injects `data-browser-agent-id` into each interactive element
|
|
16
|
+
2. LLM SELECTS from indexed element list, never GENERATES CSS selectors
|
|
17
|
+
3. Pre-built locators are guaranteed to work
|
|
18
|
+
|
|
19
|
+
Usage:
|
|
20
|
+
elements = extract_elements(page)
|
|
21
|
+
element = find_element(page, "the login button", elements)
|
|
22
|
+
page.locator(element.locator).click()
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from typing import List, Optional
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
from pydantic import BaseModel, Field
|
|
28
|
+
from connectonion import llm_do
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# Load JavaScript and prompt from files
|
|
32
|
+
_BASE_DIR = Path(__file__).parent
|
|
33
|
+
_EXTRACT_JS = (_BASE_DIR / "scripts" / "extract_elements.js").read_text()
|
|
34
|
+
_ELEMENT_MATCHER_PROMPT = (_BASE_DIR / "prompts" / "element_matcher.md").read_text()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class InteractiveElement(BaseModel):
|
|
38
|
+
"""An interactive element on the page with pre-built locator."""
|
|
39
|
+
index: int
|
|
40
|
+
tag: str
|
|
41
|
+
text: str = ""
|
|
42
|
+
role: Optional[str] = None
|
|
43
|
+
aria_label: Optional[str] = None
|
|
44
|
+
placeholder: Optional[str] = None
|
|
45
|
+
input_type: Optional[str] = None
|
|
46
|
+
href: Optional[str] = None
|
|
47
|
+
x: int = 0
|
|
48
|
+
y: int = 0
|
|
49
|
+
width: int = 0
|
|
50
|
+
height: int = 0
|
|
51
|
+
locator: str = ""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class ElementMatch(BaseModel):
|
|
55
|
+
"""LLM's element selection result."""
|
|
56
|
+
index: int = Field(..., description="Index of the matching element")
|
|
57
|
+
confidence: float = Field(..., description="Confidence 0-1")
|
|
58
|
+
reasoning: str = Field(..., description="Why this element matches")
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def extract_elements(page) -> List[InteractiveElement]:
|
|
62
|
+
"""Extract all interactive elements from the page.
|
|
63
|
+
|
|
64
|
+
Returns elements with:
|
|
65
|
+
- Bounding boxes (for position matching with screenshot)
|
|
66
|
+
- Pre-built Playwright locators (guaranteed to work)
|
|
67
|
+
- Text/aria/placeholder for LLM matching
|
|
68
|
+
"""
|
|
69
|
+
raw = page.evaluate(_EXTRACT_JS)
|
|
70
|
+
return [InteractiveElement(**el) for el in raw]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def format_elements_for_llm(elements: List[InteractiveElement], max_count: int = 150) -> str:
|
|
74
|
+
"""Format elements as compact list for LLM context.
|
|
75
|
+
|
|
76
|
+
Format: [index] tag "text" pos=(x,y) {extra info}
|
|
77
|
+
"""
|
|
78
|
+
lines = []
|
|
79
|
+
for el in elements[:max_count]:
|
|
80
|
+
parts = [f"[{el.index}]", el.tag]
|
|
81
|
+
|
|
82
|
+
if el.text:
|
|
83
|
+
parts.append(f'"{el.text}"')
|
|
84
|
+
elif el.placeholder:
|
|
85
|
+
parts.append(f'placeholder="{el.placeholder}"')
|
|
86
|
+
elif el.aria_label:
|
|
87
|
+
parts.append(f'aria="{el.aria_label}"')
|
|
88
|
+
|
|
89
|
+
parts.append(f"pos=({el.x},{el.y})")
|
|
90
|
+
|
|
91
|
+
if el.input_type and el.tag == 'input':
|
|
92
|
+
parts.append(f"type={el.input_type}")
|
|
93
|
+
|
|
94
|
+
if el.role:
|
|
95
|
+
parts.append(f"role={el.role}")
|
|
96
|
+
|
|
97
|
+
if el.href:
|
|
98
|
+
href_short = el.href.split('?')[0][-30:]
|
|
99
|
+
parts.append(f"href=...{href_short}")
|
|
100
|
+
|
|
101
|
+
lines.append(' '.join(parts))
|
|
102
|
+
|
|
103
|
+
return '\n'.join(lines)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def find_element(
|
|
107
|
+
page,
|
|
108
|
+
description: str,
|
|
109
|
+
elements: List[InteractiveElement] = None
|
|
110
|
+
) -> Optional[InteractiveElement]:
|
|
111
|
+
"""Find an interactive element by natural language description.
|
|
112
|
+
|
|
113
|
+
This is the core function. LLM SELECTS from pre-built options.
|
|
114
|
+
|
|
115
|
+
Args:
|
|
116
|
+
page: Playwright page
|
|
117
|
+
description: Natural language like "the login button" or "email field"
|
|
118
|
+
elements: Pre-extracted elements (will extract if not provided)
|
|
119
|
+
|
|
120
|
+
Returns:
|
|
121
|
+
Matching InteractiveElement with pre-built locator, or None
|
|
122
|
+
"""
|
|
123
|
+
if elements is None:
|
|
124
|
+
elements = extract_elements(page)
|
|
125
|
+
|
|
126
|
+
if not elements:
|
|
127
|
+
return None
|
|
128
|
+
|
|
129
|
+
element_list = format_elements_for_llm(elements)
|
|
130
|
+
|
|
131
|
+
# Build prompt from template
|
|
132
|
+
prompt = _ELEMENT_MATCHER_PROMPT.format(
|
|
133
|
+
description=description,
|
|
134
|
+
element_list=element_list
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
result = llm_do(
|
|
138
|
+
prompt,
|
|
139
|
+
output=ElementMatch,
|
|
140
|
+
model="co/gemini-2.5-flash",
|
|
141
|
+
temperature=0.1
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
if 0 <= result.index < len(elements):
|
|
145
|
+
return elements[result.index]
|
|
146
|
+
|
|
147
|
+
return None
|