@trac3er/oh-my-god 2.0.0 → 2.0.2
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.
- package/.claude-plugin/marketplace.json +8 -8
- package/.claude-plugin/plugin.json +5 -4
- package/.claude-plugin/scripts/uninstall.sh +74 -3
- package/.claude-plugin/scripts/update.sh +78 -3
- package/.coveragerc +26 -0
- package/.mcp.json +4 -4
- package/CHANGELOG.md +14 -0
- package/CODE_OF_CONDUCT.md +27 -0
- package/CONTRIBUTING.md +62 -0
- package/OMG-setup.sh +1201 -355
- package/README.md +77 -56
- package/SECURITY.md +25 -0
- package/agents/__init__.py +1 -0
- package/agents/model_roles.py +196 -0
- package/agents/omg-architect-mode.md +3 -5
- package/agents/omg-backend-engineer.md +3 -5
- package/agents/omg-database-engineer.md +3 -5
- package/agents/omg-frontend-designer.md +4 -5
- package/agents/omg-implement-mode.md +4 -5
- package/agents/omg-infra-engineer.md +3 -5
- package/agents/omg-research-mode.md +4 -6
- package/agents/omg-security-auditor.md +3 -5
- package/agents/omg-testing-engineer.md +3 -5
- package/build/lib/yaml.py +321 -0
- package/commands/OMG:ai-commit.md +101 -14
- package/commands/OMG:arch.md +302 -19
- package/commands/OMG:ccg.md +12 -7
- package/commands/OMG:compat.md +25 -17
- package/commands/OMG:cost.md +173 -13
- package/commands/OMG:crazy.md +1 -1
- package/commands/OMG:create-agent.md +170 -20
- package/commands/OMG:deps.md +235 -17
- package/commands/OMG:domain-init.md +1 -1
- package/commands/OMG:escalate.md +41 -12
- package/commands/OMG:health-check.md +37 -13
- package/commands/OMG:init.md +122 -14
- package/commands/OMG:project-init.md +1 -1
- package/commands/OMG:session-branch.md +76 -9
- package/commands/OMG:session-fork.md +42 -5
- package/commands/OMG:session-merge.md +124 -8
- package/commands/OMG:setup.md +69 -12
- package/commands/OMG:stats.md +215 -14
- package/commands/OMG:teams.md +19 -10
- package/config/lsp_languages.yaml +8 -0
- package/hooks/__init__.py +0 -0
- package/hooks/_agent_registry.py +423 -0
- package/hooks/_analytics.py +291 -0
- package/hooks/_budget.py +31 -0
- package/hooks/_common.py +569 -0
- package/hooks/_compression_optimizer.py +119 -0
- package/hooks/_cost_ledger.py +176 -0
- package/hooks/_learnings.py +126 -0
- package/hooks/_memory.py +103 -0
- package/hooks/_protected_context.py +150 -0
- package/hooks/_token_counter.py +221 -0
- package/hooks/branch_manager.py +236 -0
- package/hooks/budget_governor.py +232 -0
- package/hooks/circuit-breaker.py +270 -0
- package/hooks/compression_feedback.py +254 -0
- package/hooks/config-guard.py +216 -0
- package/hooks/context_pressure.py +53 -0
- package/hooks/credential_store.py +1020 -0
- package/hooks/fetch-rate-limits.py +212 -0
- package/hooks/firewall.py +48 -0
- package/hooks/hashline-formatter-bridge.py +224 -0
- package/hooks/hashline-injector.py +273 -0
- package/hooks/hashline-validator.py +216 -0
- package/hooks/idle-detector.py +95 -0
- package/hooks/intentgate-keyword-detector.py +188 -0
- package/hooks/magic-keyword-router.py +195 -0
- package/hooks/policy_engine.py +505 -0
- package/hooks/post-tool-failure.py +19 -0
- package/hooks/post-write.py +219 -0
- package/hooks/post_write.py +46 -0
- package/hooks/pre-compact.py +398 -0
- package/hooks/pre-tool-inject.py +98 -0
- package/hooks/prompt-enhancer.py +672 -0
- package/hooks/quality-runner.py +191 -0
- package/hooks/query.py +512 -0
- package/hooks/secret-guard.py +61 -0
- package/hooks/secret_audit.py +144 -0
- package/hooks/session-end-capture.py +137 -0
- package/hooks/session-start.py +277 -0
- package/hooks/setup_wizard.py +582 -0
- package/hooks/shadow_manager.py +297 -0
- package/hooks/state_migration.py +225 -0
- package/hooks/stop-gate.py +7 -0
- package/hooks/stop_dispatcher.py +945 -0
- package/hooks/test-validator.py +361 -0
- package/hooks/test_generator_hook.py +123 -0
- package/hooks/todo-state-tracker.py +114 -0
- package/hooks/tool-ledger.py +149 -0
- package/hooks/trust_review.py +585 -0
- package/hud/omg-hud.mjs +31 -1
- package/lab/__init__.py +1 -0
- package/lab/pipeline.py +75 -0
- package/lab/policies.py +52 -0
- package/package.json +7 -18
- package/plugins/README.md +33 -61
- package/plugins/advanced/commands/OMG:deep-plan.md +3 -3
- package/plugins/advanced/commands/OMG:learn.md +1 -1
- package/plugins/advanced/commands/OMG:security-review.md +3 -3
- package/plugins/advanced/commands/OMG:ship.md +1 -1
- package/plugins/advanced/plugin.json +1 -1
- package/plugins/core/plugin.json +8 -3
- package/plugins/dephealth/__init__.py +0 -0
- package/plugins/dephealth/cve_scanner.py +188 -0
- package/plugins/dephealth/license_checker.py +135 -0
- package/plugins/dephealth/manifest_detector.py +423 -0
- package/plugins/dephealth/vuln_analyzer.py +169 -0
- package/plugins/testgen/__init__.py +0 -0
- package/plugins/testgen/codamosa_engine.py +402 -0
- package/plugins/testgen/edge_case_synthesizer.py +184 -0
- package/plugins/testgen/framework_detector.py +271 -0
- package/plugins/testgen/skeleton_generator.py +219 -0
- package/plugins/viz/__init__.py +0 -0
- package/plugins/viz/ast_parser.py +139 -0
- package/plugins/viz/diagram_generator.py +192 -0
- package/plugins/viz/graph_builder.py +444 -0
- package/plugins/viz/native_parsers.py +259 -0
- package/plugins/viz/regex_parser.py +112 -0
- package/pyproject.toml +81 -0
- package/rules/contextual/write-verify.md +2 -2
- package/rules/core/00-truth.md +1 -1
- package/rules/core/01-surgical.md +1 -1
- package/rules/core/02-circuit-breaker.md +2 -2
- package/rules/core/03-ensemble.md +3 -3
- package/rules/core/04-testing.md +3 -3
- package/runtime/__init__.py +32 -0
- package/runtime/adapters/__init__.py +13 -0
- package/runtime/adapters/claude.py +60 -0
- package/runtime/adapters/gpt.py +53 -0
- package/runtime/adapters/local.py +53 -0
- package/runtime/adoption.py +212 -0
- package/runtime/business_workflow.py +220 -0
- package/runtime/cli_provider.py +85 -0
- package/runtime/compat.py +1299 -0
- package/runtime/custom_agent_loader.py +366 -0
- package/runtime/dispatcher.py +47 -0
- package/runtime/ecosystem.py +371 -0
- package/runtime/legacy_compat.py +7 -0
- package/runtime/mcp_config_writers.py +115 -0
- package/runtime/mcp_lifecycle.py +153 -0
- package/runtime/mcp_memory_server.py +135 -0
- package/runtime/memory_parsers/__init__.py +0 -0
- package/runtime/memory_parsers/chatgpt_parser.py +257 -0
- package/runtime/memory_parsers/claude_import.py +107 -0
- package/runtime/memory_parsers/export.py +97 -0
- package/runtime/memory_parsers/gemini_import.py +91 -0
- package/runtime/memory_parsers/kimi_import.py +91 -0
- package/runtime/memory_store.py +215 -0
- package/runtime/omc_compat.py +7 -0
- package/runtime/providers/__init__.py +0 -0
- package/runtime/providers/codex_provider.py +112 -0
- package/runtime/providers/gemini_provider.py +128 -0
- package/runtime/providers/kimi_provider.py +151 -0
- package/runtime/providers/opencode_provider.py +144 -0
- package/runtime/subagent_dispatcher.py +362 -0
- package/runtime/team_router.py +1167 -0
- package/runtime/tmux_session_manager.py +169 -0
- package/scripts/check-omg-compat-contract-snapshot.py +137 -0
- package/scripts/check-omg-contract-snapshot.py +12 -0
- package/scripts/check-omg-public-ready.py +193 -0
- package/scripts/check-omg-standalone-clean.py +103 -0
- package/scripts/legacy_to_omg_migrate.py +29 -0
- package/scripts/migrate-legacy.py +464 -0
- package/scripts/omc_to_omg_migrate.py +12 -0
- package/scripts/omg.py +492 -0
- package/scripts/settings-merge.py +283 -0
- package/scripts/verify-standalone.sh +8 -4
- package/settings.json +126 -29
- package/templates/profile.yaml +1 -1
- package/tools/__init__.py +2 -0
- package/tools/browser_consent.py +289 -0
- package/tools/browser_stealth.py +481 -0
- package/tools/browser_tool.py +448 -0
- package/tools/changelog_generator.py +347 -0
- package/tools/commit_splitter.py +746 -0
- package/tools/config_discovery.py +151 -0
- package/tools/config_merger.py +449 -0
- package/tools/dashboard_generator.py +300 -0
- package/tools/git_inspector.py +298 -0
- package/tools/lsp_client.py +275 -0
- package/tools/lsp_discovery.py +231 -0
- package/tools/lsp_operations.py +392 -0
- package/tools/pr_generator.py +404 -0
- package/tools/python_repl.py +656 -0
- package/tools/python_sandbox.py +609 -0
- package/tools/search_providers/__init__.py +77 -0
- package/tools/search_providers/brave.py +115 -0
- package/tools/search_providers/exa.py +116 -0
- package/tools/search_providers/jina.py +104 -0
- package/tools/search_providers/perplexity.py +139 -0
- package/tools/search_providers/synthetic.py +74 -0
- package/tools/session_snapshot.py +736 -0
- package/tools/ssh_manager.py +912 -0
- package/tools/theme_engine.py +294 -0
- package/tools/theme_selector.py +137 -0
- package/tools/web_search.py +622 -0
- package/yaml.py +321 -0
- package/.claude-plugin/scripts/install.sh +0 -9
- package/bun.lock +0 -23
- package/bunfig.toml +0 -3
- package/hooks/_budget.ts +0 -1
- package/hooks/_common.ts +0 -63
- package/hooks/circuit-breaker.ts +0 -101
- package/hooks/config-guard.ts +0 -4
- package/hooks/firewall.ts +0 -20
- package/hooks/policy_engine.ts +0 -156
- package/hooks/post-tool-failure.ts +0 -22
- package/hooks/post-write.ts +0 -4
- package/hooks/pre-tool-inject.ts +0 -4
- package/hooks/prompt-enhancer.ts +0 -46
- package/hooks/quality-runner.ts +0 -24
- package/hooks/secret-guard.ts +0 -4
- package/hooks/session-end-capture.ts +0 -19
- package/hooks/session-start.ts +0 -19
- package/hooks/shadow_manager.ts +0 -81
- package/hooks/stop-gate.ts +0 -22
- package/hooks/stop_dispatcher.ts +0 -147
- package/hooks/test-generator-hook.ts +0 -4
- package/hooks/tool-ledger.ts +0 -27
- package/hooks/trust_review.ts +0 -175
- package/lab/pipeline.ts +0 -75
- package/lab/policies.ts +0 -68
- package/runtime/common.ts +0 -111
- package/runtime/compat.ts +0 -174
- package/runtime/dispatcher.ts +0 -25
- package/runtime/ecosystem.ts +0 -186
- package/runtime/provider_bootstrap.ts +0 -99
- package/runtime/provider_smoke.ts +0 -34
- package/runtime/release_readiness.ts +0 -186
- package/runtime/team_router.ts +0 -144
- package/scripts/check-omg-compat-contract-snapshot.ts +0 -20
- package/scripts/check-omg-standalone-clean.ts +0 -12
- package/scripts/check-runtime-clean.ts +0 -94
- package/scripts/omg.ts +0 -352
- package/scripts/settings-merge.ts +0 -93
- package/tools/commit_splitter.ts +0 -23
- package/tools/git_inspector.ts +0 -18
- package/tools/session_snapshot.ts +0 -47
- package/trac3er-oh-my-god-2.0.0.tgz +0 -0
- package/tsconfig.json +0 -15
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Puppeteer Browser Integration for OMG
|
|
4
|
+
|
|
5
|
+
Wrapper around Puppeteer MCP tools that generates tool call specifications
|
|
6
|
+
for browser automation. Functions produce spec dicts — actual execution is
|
|
7
|
+
done by Claude Code when it dispatches the MCP call.
|
|
8
|
+
|
|
9
|
+
Feature flag: OMG_BROWSER_ENABLED (default: False)
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import json
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import uuid
|
|
16
|
+
from dataclasses import asdict, dataclass, field
|
|
17
|
+
from typing import Any, Dict, List, Optional
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
# --- Lazy imports for hooks/_common.py ---
|
|
21
|
+
|
|
22
|
+
_get_feature_flag = None
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def _ensure_imports():
|
|
26
|
+
"""Lazy import feature flag from hooks/_common.py."""
|
|
27
|
+
global _get_feature_flag
|
|
28
|
+
if _get_feature_flag is not None:
|
|
29
|
+
return
|
|
30
|
+
repo_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
|
31
|
+
if repo_root not in sys.path:
|
|
32
|
+
sys.path.insert(0, repo_root)
|
|
33
|
+
try:
|
|
34
|
+
from hooks._common import get_feature_flag as _gff
|
|
35
|
+
_get_feature_flag = _gff
|
|
36
|
+
except ImportError:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# --- Feature flag ---
|
|
41
|
+
|
|
42
|
+
def _is_enabled() -> bool:
|
|
43
|
+
"""Check if Browser feature is enabled."""
|
|
44
|
+
# Fast path: check env var directly
|
|
45
|
+
env_val = os.environ.get("OMG_BROWSER_ENABLED", "").lower()
|
|
46
|
+
if env_val in ("0", "false", "no"):
|
|
47
|
+
return False
|
|
48
|
+
if env_val in ("1", "true", "yes"):
|
|
49
|
+
return True
|
|
50
|
+
# Fallback to hooks/_common.get_feature_flag
|
|
51
|
+
_ensure_imports()
|
|
52
|
+
if _get_feature_flag is not None:
|
|
53
|
+
return _get_feature_flag("BROWSER", default=False)
|
|
54
|
+
return False
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
# --- Response helpers ---
|
|
58
|
+
|
|
59
|
+
def _success_response(result: Any) -> Dict[str, Any]:
|
|
60
|
+
"""Create a success response dict."""
|
|
61
|
+
return {"success": True, "result": result, "error": None}
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _error_response(error: str) -> Dict[str, Any]:
|
|
65
|
+
"""Create an error response dict."""
|
|
66
|
+
return {"success": False, "result": None, "error": error}
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _disabled_response() -> Dict[str, Any]:
|
|
70
|
+
"""Create a response for when the feature flag is disabled."""
|
|
71
|
+
return _error_response("Browser feature is disabled (OMG_BROWSER_ENABLED=false)")
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
# =============================================================================
|
|
75
|
+
# BrowserSession — session state tracking
|
|
76
|
+
# =============================================================================
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@dataclass
|
|
80
|
+
class BrowserSession:
|
|
81
|
+
"""Manages browser session state.
|
|
82
|
+
|
|
83
|
+
Tracks the current URL, navigation history, and screenshot names
|
|
84
|
+
for the duration of a browser automation session.
|
|
85
|
+
|
|
86
|
+
Attributes:
|
|
87
|
+
session_id: Unique identifier for this session.
|
|
88
|
+
current_url: The URL the browser is currently on (empty if none).
|
|
89
|
+
history: List of URLs visited during this session.
|
|
90
|
+
screenshots: List of screenshot names taken during this session.
|
|
91
|
+
"""
|
|
92
|
+
|
|
93
|
+
session_id: str = field(default_factory=lambda: uuid.uuid4().hex[:12])
|
|
94
|
+
current_url: str = ""
|
|
95
|
+
history: List[str] = field(default_factory=list)
|
|
96
|
+
screenshots: List[str] = field(default_factory=list)
|
|
97
|
+
|
|
98
|
+
def navigate_to(self, url: str) -> None:
|
|
99
|
+
"""Record a navigation to a URL."""
|
|
100
|
+
self.current_url = url
|
|
101
|
+
self.history.append(url)
|
|
102
|
+
|
|
103
|
+
def record_screenshot(self, name: str) -> None:
|
|
104
|
+
"""Record a screenshot taken."""
|
|
105
|
+
self.screenshots.append(name)
|
|
106
|
+
|
|
107
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
108
|
+
"""Convert session state to a plain dictionary."""
|
|
109
|
+
return asdict(self)
|
|
110
|
+
|
|
111
|
+
def reset(self) -> None:
|
|
112
|
+
"""Reset session state while keeping the same session_id."""
|
|
113
|
+
self.current_url = ""
|
|
114
|
+
self.history.clear()
|
|
115
|
+
self.screenshots.clear()
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
# Module-level session instance
|
|
119
|
+
session = BrowserSession()
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
# =============================================================================
|
|
123
|
+
# Browser Operations — tool call spec generators
|
|
124
|
+
# =============================================================================
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def browser_navigate(url: str, timeout: int = 30) -> Dict[str, Any]:
|
|
128
|
+
"""Navigate the browser to a URL.
|
|
129
|
+
|
|
130
|
+
Generates a Puppeteer MCP tool call spec for navigation.
|
|
131
|
+
|
|
132
|
+
Args:
|
|
133
|
+
url: The URL to navigate to.
|
|
134
|
+
timeout: Navigation timeout in seconds (default: 30).
|
|
135
|
+
|
|
136
|
+
Returns:
|
|
137
|
+
A dict with ``success``, ``result`` (the tool call spec), and ``error``.
|
|
138
|
+
If the feature flag is disabled, returns an error response.
|
|
139
|
+
"""
|
|
140
|
+
if not _is_enabled():
|
|
141
|
+
return _disabled_response()
|
|
142
|
+
|
|
143
|
+
try:
|
|
144
|
+
if not url or not isinstance(url, str):
|
|
145
|
+
return _error_response("URL must be a non-empty string")
|
|
146
|
+
|
|
147
|
+
# Validate URL has a scheme
|
|
148
|
+
if not url.startswith(("http://", "https://")):
|
|
149
|
+
url = f"https://{url}"
|
|
150
|
+
|
|
151
|
+
spec = {
|
|
152
|
+
"tool": "mcp_puppeteer_puppeteer_navigate",
|
|
153
|
+
"parameters": {
|
|
154
|
+
"url": url,
|
|
155
|
+
},
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
# Track session state
|
|
159
|
+
session.navigate_to(url)
|
|
160
|
+
|
|
161
|
+
return _success_response(spec)
|
|
162
|
+
|
|
163
|
+
except Exception as e:
|
|
164
|
+
return _error_response(str(e))
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
def browser_click(selector: str) -> Dict[str, Any]:
|
|
168
|
+
"""Click an element on the page.
|
|
169
|
+
|
|
170
|
+
Generates a Puppeteer MCP tool call spec for clicking.
|
|
171
|
+
|
|
172
|
+
Args:
|
|
173
|
+
selector: CSS selector for the element to click.
|
|
174
|
+
|
|
175
|
+
Returns:
|
|
176
|
+
A dict with ``success``, ``result`` (the tool call spec), and ``error``.
|
|
177
|
+
"""
|
|
178
|
+
if not _is_enabled():
|
|
179
|
+
return _disabled_response()
|
|
180
|
+
|
|
181
|
+
try:
|
|
182
|
+
if not selector or not isinstance(selector, str):
|
|
183
|
+
return _error_response("Selector must be a non-empty string")
|
|
184
|
+
|
|
185
|
+
spec = {
|
|
186
|
+
"tool": "mcp_puppeteer_puppeteer_click",
|
|
187
|
+
"parameters": {
|
|
188
|
+
"selector": selector,
|
|
189
|
+
},
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
return _success_response(spec)
|
|
193
|
+
|
|
194
|
+
except Exception as e:
|
|
195
|
+
return _error_response(str(e))
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def browser_type(selector: str, text: str) -> Dict[str, Any]:
|
|
199
|
+
"""Type text into an input element.
|
|
200
|
+
|
|
201
|
+
Generates a Puppeteer MCP tool call spec for filling an input field.
|
|
202
|
+
|
|
203
|
+
Args:
|
|
204
|
+
selector: CSS selector for the input field.
|
|
205
|
+
text: The text to type into the field.
|
|
206
|
+
|
|
207
|
+
Returns:
|
|
208
|
+
A dict with ``success``, ``result`` (the tool call spec), and ``error``.
|
|
209
|
+
"""
|
|
210
|
+
if not _is_enabled():
|
|
211
|
+
return _disabled_response()
|
|
212
|
+
|
|
213
|
+
try:
|
|
214
|
+
if not selector or not isinstance(selector, str):
|
|
215
|
+
return _error_response("Selector must be a non-empty string")
|
|
216
|
+
if not isinstance(text, str):
|
|
217
|
+
return _error_response("Text must be a string")
|
|
218
|
+
|
|
219
|
+
spec = {
|
|
220
|
+
"tool": "mcp_puppeteer_puppeteer_fill",
|
|
221
|
+
"parameters": {
|
|
222
|
+
"selector": selector,
|
|
223
|
+
"value": text,
|
|
224
|
+
},
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return _success_response(spec)
|
|
228
|
+
|
|
229
|
+
except Exception as e:
|
|
230
|
+
return _error_response(str(e))
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
def browser_screenshot(name: str, selector: Optional[str] = None) -> Dict[str, Any]:
|
|
234
|
+
"""Take a screenshot of the page or a specific element.
|
|
235
|
+
|
|
236
|
+
Generates a Puppeteer MCP tool call spec for taking a screenshot.
|
|
237
|
+
|
|
238
|
+
Args:
|
|
239
|
+
name: Name for the screenshot.
|
|
240
|
+
selector: Optional CSS selector for a specific element to capture.
|
|
241
|
+
|
|
242
|
+
Returns:
|
|
243
|
+
A dict with ``success``, ``result`` (the tool call spec), and ``error``.
|
|
244
|
+
"""
|
|
245
|
+
if not _is_enabled():
|
|
246
|
+
return _disabled_response()
|
|
247
|
+
|
|
248
|
+
try:
|
|
249
|
+
if not name or not isinstance(name, str):
|
|
250
|
+
return _error_response("Name must be a non-empty string")
|
|
251
|
+
|
|
252
|
+
params: Dict[str, Any] = {"name": name}
|
|
253
|
+
if selector:
|
|
254
|
+
params["selector"] = selector
|
|
255
|
+
|
|
256
|
+
spec = {
|
|
257
|
+
"tool": "mcp_puppeteer_puppeteer_screenshot",
|
|
258
|
+
"parameters": params,
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
# Track screenshot
|
|
262
|
+
session.record_screenshot(name)
|
|
263
|
+
|
|
264
|
+
return _success_response(spec)
|
|
265
|
+
|
|
266
|
+
except Exception as e:
|
|
267
|
+
return _error_response(str(e))
|
|
268
|
+
|
|
269
|
+
|
|
270
|
+
def browser_evaluate(script: str) -> Dict[str, Any]:
|
|
271
|
+
"""Execute JavaScript in the browser console.
|
|
272
|
+
|
|
273
|
+
Generates a Puppeteer MCP tool call spec for evaluating JavaScript.
|
|
274
|
+
|
|
275
|
+
Args:
|
|
276
|
+
script: JavaScript code to execute.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
A dict with ``success``, ``result`` (the tool call spec), and ``error``.
|
|
280
|
+
"""
|
|
281
|
+
if not _is_enabled():
|
|
282
|
+
return _disabled_response()
|
|
283
|
+
|
|
284
|
+
try:
|
|
285
|
+
if not script or not isinstance(script, str):
|
|
286
|
+
return _error_response("Script must be a non-empty string")
|
|
287
|
+
|
|
288
|
+
spec = {
|
|
289
|
+
"tool": "mcp_puppeteer_puppeteer_evaluate",
|
|
290
|
+
"parameters": {
|
|
291
|
+
"script": script,
|
|
292
|
+
},
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
return _success_response(spec)
|
|
296
|
+
|
|
297
|
+
except Exception as e:
|
|
298
|
+
return _error_response(str(e))
|
|
299
|
+
|
|
300
|
+
|
|
301
|
+
# =============================================================================
|
|
302
|
+
# CLI Interface
|
|
303
|
+
# =============================================================================
|
|
304
|
+
|
|
305
|
+
|
|
306
|
+
def _cli_main():
|
|
307
|
+
"""CLI entry point for browser_tool.py."""
|
|
308
|
+
import argparse
|
|
309
|
+
|
|
310
|
+
parser = argparse.ArgumentParser(
|
|
311
|
+
description="OMG Browser Tool — Puppeteer MCP wrapper for browser automation",
|
|
312
|
+
formatter_class=argparse.RawDescriptionHelpFormatter,
|
|
313
|
+
)
|
|
314
|
+
parser.add_argument("--navigate", dest="navigate_url", help="Navigate to URL")
|
|
315
|
+
parser.add_argument("--click", dest="click_selector", help="Click CSS selector")
|
|
316
|
+
parser.add_argument(
|
|
317
|
+
"--type", dest="type_args", nargs=2, metavar=("SELECTOR", "TEXT"),
|
|
318
|
+
help="Type text into selector",
|
|
319
|
+
)
|
|
320
|
+
parser.add_argument(
|
|
321
|
+
"--screenshot", dest="screenshot_name", help="Take screenshot with name",
|
|
322
|
+
)
|
|
323
|
+
parser.add_argument(
|
|
324
|
+
"--screenshot-selector", dest="screenshot_selector", default=None,
|
|
325
|
+
help="Optional CSS selector for screenshot (use with --screenshot)",
|
|
326
|
+
)
|
|
327
|
+
parser.add_argument("--evaluate", dest="eval_script", help="Evaluate JavaScript")
|
|
328
|
+
parser.add_argument(
|
|
329
|
+
"--dry-run", action="store_true",
|
|
330
|
+
help="Print the tool call spec without executing",
|
|
331
|
+
)
|
|
332
|
+
parser.add_argument(
|
|
333
|
+
"--session-info", action="store_true",
|
|
334
|
+
help="Show current session state",
|
|
335
|
+
)
|
|
336
|
+
|
|
337
|
+
args = parser.parse_args()
|
|
338
|
+
|
|
339
|
+
enabled = _is_enabled()
|
|
340
|
+
|
|
341
|
+
# Session info
|
|
342
|
+
if args.session_info:
|
|
343
|
+
print(json.dumps({
|
|
344
|
+
"session": session.to_dict(),
|
|
345
|
+
"enabled": enabled,
|
|
346
|
+
}, indent=2))
|
|
347
|
+
return
|
|
348
|
+
|
|
349
|
+
# Dry-run navigate
|
|
350
|
+
if args.dry_run and args.navigate_url:
|
|
351
|
+
result = browser_navigate(args.navigate_url)
|
|
352
|
+
print(json.dumps({
|
|
353
|
+
"dry_run": True,
|
|
354
|
+
"operation": "navigate",
|
|
355
|
+
"url": args.navigate_url,
|
|
356
|
+
"enabled": enabled,
|
|
357
|
+
"spec": result,
|
|
358
|
+
}, indent=2))
|
|
359
|
+
return
|
|
360
|
+
|
|
361
|
+
# Dry-run click
|
|
362
|
+
if args.dry_run and args.click_selector:
|
|
363
|
+
result = browser_click(args.click_selector)
|
|
364
|
+
print(json.dumps({
|
|
365
|
+
"dry_run": True,
|
|
366
|
+
"operation": "click",
|
|
367
|
+
"selector": args.click_selector,
|
|
368
|
+
"enabled": enabled,
|
|
369
|
+
"spec": result,
|
|
370
|
+
}, indent=2))
|
|
371
|
+
return
|
|
372
|
+
|
|
373
|
+
# Dry-run type
|
|
374
|
+
if args.dry_run and args.type_args:
|
|
375
|
+
result = browser_type(args.type_args[0], args.type_args[1])
|
|
376
|
+
print(json.dumps({
|
|
377
|
+
"dry_run": True,
|
|
378
|
+
"operation": "type",
|
|
379
|
+
"selector": args.type_args[0],
|
|
380
|
+
"text": args.type_args[1],
|
|
381
|
+
"enabled": enabled,
|
|
382
|
+
"spec": result,
|
|
383
|
+
}, indent=2))
|
|
384
|
+
return
|
|
385
|
+
|
|
386
|
+
# Dry-run screenshot
|
|
387
|
+
if args.dry_run and args.screenshot_name:
|
|
388
|
+
result = browser_screenshot(args.screenshot_name, args.screenshot_selector)
|
|
389
|
+
print(json.dumps({
|
|
390
|
+
"dry_run": True,
|
|
391
|
+
"operation": "screenshot",
|
|
392
|
+
"name": args.screenshot_name,
|
|
393
|
+
"selector": args.screenshot_selector,
|
|
394
|
+
"enabled": enabled,
|
|
395
|
+
"spec": result,
|
|
396
|
+
}, indent=2))
|
|
397
|
+
return
|
|
398
|
+
|
|
399
|
+
# Dry-run evaluate
|
|
400
|
+
if args.dry_run and args.eval_script:
|
|
401
|
+
result = browser_evaluate(args.eval_script)
|
|
402
|
+
print(json.dumps({
|
|
403
|
+
"dry_run": True,
|
|
404
|
+
"operation": "evaluate",
|
|
405
|
+
"script": args.eval_script,
|
|
406
|
+
"enabled": enabled,
|
|
407
|
+
"spec": result,
|
|
408
|
+
}, indent=2))
|
|
409
|
+
return
|
|
410
|
+
|
|
411
|
+
# Non-dry-run: check feature flag
|
|
412
|
+
if not enabled:
|
|
413
|
+
print(json.dumps({
|
|
414
|
+
"error": "Browser feature is disabled (OMG_BROWSER_ENABLED=false)",
|
|
415
|
+
}))
|
|
416
|
+
sys.exit(1)
|
|
417
|
+
|
|
418
|
+
# Execute operations
|
|
419
|
+
if args.navigate_url:
|
|
420
|
+
result = browser_navigate(args.navigate_url)
|
|
421
|
+
print(json.dumps(result, indent=2))
|
|
422
|
+
return
|
|
423
|
+
|
|
424
|
+
if args.click_selector:
|
|
425
|
+
result = browser_click(args.click_selector)
|
|
426
|
+
print(json.dumps(result, indent=2))
|
|
427
|
+
return
|
|
428
|
+
|
|
429
|
+
if args.type_args:
|
|
430
|
+
result = browser_type(args.type_args[0], args.type_args[1])
|
|
431
|
+
print(json.dumps(result, indent=2))
|
|
432
|
+
return
|
|
433
|
+
|
|
434
|
+
if args.screenshot_name:
|
|
435
|
+
result = browser_screenshot(args.screenshot_name, args.screenshot_selector)
|
|
436
|
+
print(json.dumps(result, indent=2))
|
|
437
|
+
return
|
|
438
|
+
|
|
439
|
+
if args.eval_script:
|
|
440
|
+
result = browser_evaluate(args.eval_script)
|
|
441
|
+
print(json.dumps(result, indent=2))
|
|
442
|
+
return
|
|
443
|
+
|
|
444
|
+
parser.print_help()
|
|
445
|
+
|
|
446
|
+
|
|
447
|
+
if __name__ == "__main__":
|
|
448
|
+
_cli_main()
|