@iaforged/context-code 2.3.1 → 2.3.5
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/context-bootstrap.js +7 -5
- package/dist/src/QueryEngine.js +1 -1
- package/dist/src/cli/handlers/auth.js +1 -1
- package/dist/src/cli/handlers/modelList.js +1 -1
- package/dist/src/cli/structuredIO.js +1 -1
- package/dist/src/commands/branch/index.js +1 -1
- package/dist/src/commands/login/login.js +1 -1
- package/dist/src/commands/profile/index.js +1 -1
- package/dist/src/commands/profile/profile.js +1 -1
- package/dist/src/commands/provider/index.js +1 -1
- package/dist/src/commands/provider/provider.js +1 -1
- package/dist/src/components/BaseTextInput.js +1 -1
- package/dist/src/components/ConsoleOAuthFlow.js +1 -1
- package/dist/src/components/LogoV2/AnimatedClawd.js +1 -1
- package/dist/src/components/LogoV2/Clawd.js +1 -1
- package/dist/src/components/LogoV2/LogoV2.js +1 -1
- package/dist/src/components/LogoV2/Opus1mMergeNotice.js +1 -1
- package/dist/src/components/LogoV2/WelcomeV2.js +1 -1
- package/dist/src/components/ModelPicker.js +1 -1
- package/dist/src/components/PromptInput/PromptInputFooterLeftSide.js +1 -1
- package/dist/src/components/SessionTokenFooter.js +1 -0
- package/dist/src/components/Spinner.js +1 -1
- package/dist/src/components/Stats.js +1 -1
- package/dist/src/components/TeleportProgress.js +1 -1
- package/dist/src/components/TextInput.js +1 -1
- package/dist/src/components/design-system/ThemeProvider.js +1 -1
- package/dist/src/components/permissions/AskUserQuestionPermissionRequest/AskUserQuestionPermissionRequest.js +1 -1
- package/dist/src/constants/oauth.js +1 -1
- package/dist/src/core/providers/providerCore.js +1 -1
- package/dist/src/hooks/useTypeahead.js +1 -1
- package/dist/src/main.js +1 -1
- package/dist/src/query/stopHooks.js +1 -1
- package/dist/src/screens/REPL.js +1 -1
- package/dist/src/services/PromptSuggestion/promptSuggestion.js +1 -1
- package/dist/src/services/analytics/config.js +1 -1
- package/dist/src/services/analytics/datadog.js +1 -1
- package/dist/src/services/api/openai.js +1 -1
- package/dist/src/services/mcp/config.js +1 -1
- package/dist/src/services/oauth/auth-code-listener.js +1 -1
- package/dist/src/services/oauth/client.js +1 -1
- package/dist/src/services/oauth/geminiCli.js +1 -1
- package/dist/src/services/tips/tipRegistry.js +1 -1
- package/dist/src/services/toolUseSummary/toolUseSummaryGenerator.js +1 -1
- package/dist/src/tools/BriefTool/UI.js +1 -1
- package/dist/src/utils/auth.js +1 -1
- package/dist/src/utils/claudeInChrome/setup.js +1 -1
- package/dist/src/utils/computerControlMcp/mcpServer.js +1 -1
- package/dist/src/utils/computerControlMcp/server/.gitattributes +18 -0
- package/dist/src/utils/computerControlMcp/server/Dockerfile +25 -0
- package/dist/src/utils/computerControlMcp/server/LICENSE +21 -0
- package/dist/src/utils/computerControlMcp/server/MANIFEST.in +10 -0
- package/dist/src/utils/computerControlMcp/server/README.md +193 -0
- package/dist/src/utils/computerControlMcp/server/demonstration.gif +0 -0
- package/dist/src/utils/computerControlMcp/server/icon.png +0 -0
- package/dist/src/utils/computerControlMcp/server/pyproject.toml +52 -0
- package/dist/src/utils/computerControlMcp/server/smithery.yaml +13 -0
- package/dist/src/utils/computerControlMcp/server/src/README.md +12 -0
- package/dist/src/utils/computerControlMcp/server/src/computer_control_mcp/FZYTK.TTF +0 -0
- package/dist/src/utils/computerControlMcp/server/src/computer_control_mcp/__init__.py +11 -0
- package/dist/src/utils/computerControlMcp/server/src/computer_control_mcp/__main__.py +21 -0
- package/dist/src/utils/computerControlMcp/server/src/computer_control_mcp/cli.py +128 -0
- package/dist/src/utils/computerControlMcp/server/src/computer_control_mcp/core.py +1008 -0
- package/dist/src/utils/computerControlMcp/server/src/computer_control_mcp/gui.py +126 -0
- package/dist/src/utils/computerControlMcp/server/src/computer_control_mcp/server.py +15 -0
- package/dist/src/utils/computerControlMcp/server/src/computer_control_mcp/test.py +346 -0
- package/dist/src/utils/computerControlMcp/server/src/computer_control_mcp/test_image.png +0 -0
- package/dist/src/utils/computerControlMcp/server/tests/README.md +22 -0
- package/dist/src/utils/computerControlMcp/server/tests/conftest.py +10 -0
- package/dist/src/utils/computerControlMcp/server/tests/rapidocr_test.py +21 -0
- package/dist/src/utils/computerControlMcp/server/tests/run_cli.py +9 -0
- package/dist/src/utils/computerControlMcp/server/tests/run_server.py +15 -0
- package/dist/src/utils/computerControlMcp/server/tests/setup.py +16 -0
- package/dist/src/utils/computerControlMcp/server/tests/test_computer_control.py +161 -0
- package/dist/src/utils/computerControlMcp/server/tests/test_screenshot.py +14 -0
- package/dist/src/utils/computerControlMcp/server/tests/test_wgc_env_var.py +42 -0
- package/dist/src/utils/computerControlMcp/server/tests/test_wgc_screenshot.py +67 -0
- package/dist/src/utils/computerControlMcp/server/uv.lock +4986 -0
- package/dist/src/utils/computerControlMcp/setup.js +1 -1
- package/dist/src/utils/envUtils.js +1 -1
- package/dist/src/utils/git.js +1 -1
- package/dist/src/utils/localInstaller.js +1 -1
- package/dist/src/utils/logoV2Utils.js +1 -1
- package/dist/src/utils/model/configs.js +1 -1
- package/dist/src/utils/model/model.js +1 -1
- package/dist/src/utils/model/modelAllowlist.js +1 -1
- package/dist/src/utils/model/modelOptions.js +1 -1
- package/dist/src/utils/model/providerBaseUrls.js +1 -1
- package/dist/src/utils/model/providerCatalog.js +1 -1
- package/dist/src/utils/model/providerModels.js +1 -1
- package/dist/src/utils/model/providerProfiles.js +1 -1
- package/dist/src/utils/model/providerProfilesDb.js +1 -1
- package/dist/src/utils/model/providers.js +1 -1
- package/dist/src/utils/model/validateModel.js +1 -1
- package/dist/src/utils/ripgrep.js +1 -1
- package/dist/src/utils/sembleMcp/setup.js +1 -1
- package/dist/src/utils/theme.js +1 -1
- package/dist/src/utils/themes/bootstrap.js +1 -1
- package/dist/src/utils/themes/opencodeMapper.js +1 -1
- package/dist/webapp/chunk-VAB2VXFI.js +1 -1
- package/dist/webapp/main-MTQLKGXD.js +1 -1
- package/dist/webapp/ngsw.json +1 -1
- package/dist/webapp/polyfills-7R4CRVNH.js +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Tests for the Computer Control MCP package.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import pytest
|
|
6
|
+
from unittest.mock import Mock, patch
|
|
7
|
+
import json
|
|
8
|
+
import sys
|
|
9
|
+
import tkinter as tk
|
|
10
|
+
from tkinter import ttk
|
|
11
|
+
import asyncio
|
|
12
|
+
import os
|
|
13
|
+
import ast
|
|
14
|
+
from computer_control_mcp.core import mcp
|
|
15
|
+
|
|
16
|
+
# Helper function to print request/response JSON, skipping non-serializable properties
|
|
17
|
+
def print_json_data(name, request_data=None, response_data=None):
|
|
18
|
+
def serialize(obj):
|
|
19
|
+
try:
|
|
20
|
+
json.dumps(obj)
|
|
21
|
+
return obj
|
|
22
|
+
except (TypeError, OverflowError):
|
|
23
|
+
return str(obj)
|
|
24
|
+
|
|
25
|
+
print(f"\n===== TEST: {name} =====", file=sys.stderr)
|
|
26
|
+
if isinstance(request_data, dict):
|
|
27
|
+
serializable_request = {k: serialize(v) for k, v in request_data.items()}
|
|
28
|
+
print(f"REQUEST: {json.dumps(serializable_request, indent=2)}", file=sys.stderr)
|
|
29
|
+
elif request_data is not None:
|
|
30
|
+
print(f"REQUEST: {serialize(request_data)}", file=sys.stderr)
|
|
31
|
+
if response_data is not None:
|
|
32
|
+
if isinstance(response_data, dict):
|
|
33
|
+
serializable_response = {k: serialize(v) for k, v in response_data.items()}
|
|
34
|
+
print(
|
|
35
|
+
f"RESPONSE: {json.dumps(serializable_response, indent=2)}",
|
|
36
|
+
file=sys.stderr,
|
|
37
|
+
)
|
|
38
|
+
else:
|
|
39
|
+
print(f"RESPONSE: {serialize(response_data)}", file=sys.stderr)
|
|
40
|
+
print("======================\n", file=sys.stderr)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
# Test drag_mouse tool
|
|
44
|
+
@pytest.mark.asyncio
|
|
45
|
+
async def test_drag_mouse():
|
|
46
|
+
# Test data
|
|
47
|
+
test_window = tk.Tk()
|
|
48
|
+
test_window.title("Test Drag Mouse")
|
|
49
|
+
test_window.geometry("400x400")
|
|
50
|
+
|
|
51
|
+
# Update the window to ensure coordinates are calculated
|
|
52
|
+
test_window.update_idletasks()
|
|
53
|
+
test_window.update()
|
|
54
|
+
|
|
55
|
+
# Window title coordinates
|
|
56
|
+
window_x = test_window.winfo_x()
|
|
57
|
+
window_y = test_window.winfo_y()
|
|
58
|
+
|
|
59
|
+
screen_width = test_window.winfo_screenwidth()
|
|
60
|
+
screen_height = test_window.winfo_screenheight()
|
|
61
|
+
center_x = screen_width // 2
|
|
62
|
+
center_y = screen_height // 2
|
|
63
|
+
request_data = {
|
|
64
|
+
"from_x": window_x + 55,
|
|
65
|
+
"from_y": window_y + 15,
|
|
66
|
+
"to_x": center_x,
|
|
67
|
+
"to_y": center_y,
|
|
68
|
+
"duration": 1.0,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
print(f"starting coordinates: x={window_x}, y={window_y}", file=sys.stderr)
|
|
72
|
+
|
|
73
|
+
# Create an event to track completion
|
|
74
|
+
drag_complete = asyncio.Event()
|
|
75
|
+
|
|
76
|
+
async def perform_drag():
|
|
77
|
+
try:
|
|
78
|
+
result = await mcp.call_tool("drag_mouse", request_data)
|
|
79
|
+
print(f"Result: {result}", file=sys.stderr)
|
|
80
|
+
finally:
|
|
81
|
+
drag_complete.set()
|
|
82
|
+
|
|
83
|
+
# Start the drag operation
|
|
84
|
+
drag_task = asyncio.create_task(perform_drag())
|
|
85
|
+
|
|
86
|
+
# Keep updating the window while waiting for drag to complete
|
|
87
|
+
while not drag_complete.is_set():
|
|
88
|
+
test_window.update()
|
|
89
|
+
await asyncio.sleep(0.01) # Small delay to prevent high CPU usage
|
|
90
|
+
|
|
91
|
+
# Wait for drag operation to complete
|
|
92
|
+
await drag_task
|
|
93
|
+
|
|
94
|
+
window_x_end = test_window.winfo_x()
|
|
95
|
+
window_y_end = test_window.winfo_y()
|
|
96
|
+
print(f'ending coordinates: x={window_x_end}, y={window_y_end}', file=sys.stderr)
|
|
97
|
+
|
|
98
|
+
assert window_y_end != window_y and window_x_end != window_x
|
|
99
|
+
|
|
100
|
+
test_window.destroy()
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# Test list_windows tool
|
|
104
|
+
@pytest.mark.asyncio
|
|
105
|
+
async def test_list_windows():
|
|
106
|
+
# open tkinter
|
|
107
|
+
test_window = tk.Tk()
|
|
108
|
+
test_window.title("Test Window")
|
|
109
|
+
test_window.geometry("400x400")
|
|
110
|
+
|
|
111
|
+
# Update the window to ensure coordinates are calculated
|
|
112
|
+
test_window.update_idletasks()
|
|
113
|
+
test_window.update()
|
|
114
|
+
|
|
115
|
+
# list all windows
|
|
116
|
+
result = await mcp.call_tool("list_windows", {})
|
|
117
|
+
|
|
118
|
+
# check if "Test Window" is in the list
|
|
119
|
+
# Parse the TextContent objects to extract the JSON data
|
|
120
|
+
window_data = []
|
|
121
|
+
for item in result:
|
|
122
|
+
if hasattr(item, 'text'):
|
|
123
|
+
try:
|
|
124
|
+
window_info = json.loads(item.text)
|
|
125
|
+
window_data.append(window_info)
|
|
126
|
+
except json.JSONDecodeError:
|
|
127
|
+
print(f"Failed to parse JSON: {item.text}", file=sys.stderr)
|
|
128
|
+
|
|
129
|
+
print(f"Result: {window_data}")
|
|
130
|
+
|
|
131
|
+
assert any(window.get("title") == "Test Window" for window in window_data)
|
|
132
|
+
|
|
133
|
+
test_window.destroy()
|
|
134
|
+
|
|
135
|
+
# Test screenshot with downloads
|
|
136
|
+
@pytest.mark.asyncio
|
|
137
|
+
async def test_take_screenshot():
|
|
138
|
+
# Take a screenshot of the whole screen and save to downloads
|
|
139
|
+
results = await mcp.call_tool("take_screenshot", {'save_to_downloads': True, 'mode': 'whole_screen'})
|
|
140
|
+
|
|
141
|
+
for result in results:
|
|
142
|
+
# Check if file_path is in the result
|
|
143
|
+
if hasattr(result, 'text'):
|
|
144
|
+
try:
|
|
145
|
+
result_dict = json.loads(result.text)
|
|
146
|
+
print(f"Screenshot result: {result_dict['title']}", file=sys.stderr)
|
|
147
|
+
assert 'file_path' in result_dict, "file_path should be in the result"
|
|
148
|
+
file_path = result_dict['file_path']
|
|
149
|
+
|
|
150
|
+
# Check if the file exists
|
|
151
|
+
assert os.path.exists(file_path), f"File {file_path} should exist"
|
|
152
|
+
print(f"Screenshot saved to: {file_path}", file=sys.stderr)
|
|
153
|
+
|
|
154
|
+
# Clean up - remove the file
|
|
155
|
+
os.remove(file_path)
|
|
156
|
+
print(f"Removed test file: {file_path}", file=sys.stderr)
|
|
157
|
+
except (ValueError, SyntaxError, AttributeError) as e:
|
|
158
|
+
print(f"Error processing result: {e}", file=sys.stderr)
|
|
159
|
+
assert False, f"Error processing result: {e}"
|
|
160
|
+
|
|
161
|
+
assert True, "Successfully tested screenshot with downloads"
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
sys.path.append('src')
|
|
3
|
+
from computer_control_mcp.core import take_screenshot
|
|
4
|
+
|
|
5
|
+
# Test with save_to_downloads=False
|
|
6
|
+
result = take_screenshot(mode='whole_screen', save_to_downloads=False)
|
|
7
|
+
print('Base64 image included:', 'base64_image' in result)
|
|
8
|
+
print('MCP Image included:', 'image' in result)
|
|
9
|
+
|
|
10
|
+
# Test with save_to_downloads=True
|
|
11
|
+
result = take_screenshot(mode='whole_screen', save_to_downloads=True)
|
|
12
|
+
print('Base64 image included:', 'base64_image' in result)
|
|
13
|
+
print('MCP Image included:', 'image' in result)
|
|
14
|
+
print('File path included:', 'file_path' in result)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
|
4
|
+
|
|
5
|
+
from computer_control_mcp.core import _should_use_wgc_by_default
|
|
6
|
+
import os
|
|
7
|
+
|
|
8
|
+
def test_wgc_env_var():
|
|
9
|
+
"""Test the WGC environment variable pattern matching"""
|
|
10
|
+
print("Testing WGC environment variable pattern matching...")
|
|
11
|
+
|
|
12
|
+
# Test with no environment variable set
|
|
13
|
+
if 'COMPUTER_CONTROL_MCP_WGC_PATTERNS' in os.environ:
|
|
14
|
+
del os.environ['COMPUTER_CONTROL_MCP_WGC_PATTERNS']
|
|
15
|
+
|
|
16
|
+
result = _should_use_wgc_by_default("OBS Studio")
|
|
17
|
+
print(f"Without env var - 'OBS Studio': {result} (expected: False)")
|
|
18
|
+
|
|
19
|
+
# Test with environment variable set
|
|
20
|
+
os.environ['COMPUTER_CONTROL_MCP_WGC_PATTERNS'] = "obs, discord, game"
|
|
21
|
+
|
|
22
|
+
test_cases = [
|
|
23
|
+
("OBS Studio", True),
|
|
24
|
+
("Discord", True),
|
|
25
|
+
("My Game", True),
|
|
26
|
+
("Notepad", False),
|
|
27
|
+
("Google Chrome", False),
|
|
28
|
+
("obs studio", True), # Test case insensitivity
|
|
29
|
+
("DISCORD APP", True), # Test case insensitivity
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
for window_title, expected in test_cases:
|
|
33
|
+
result = _should_use_wgc_by_default(window_title)
|
|
34
|
+
status = "✓" if result == expected else "✗"
|
|
35
|
+
print(f"{status} '{window_title}': {result} (expected: {expected})")
|
|
36
|
+
|
|
37
|
+
# Clean up
|
|
38
|
+
if 'COMPUTER_CONTROL_MCP_WGC_PATTERNS' in os.environ:
|
|
39
|
+
del os.environ['COMPUTER_CONTROL_MCP_WGC_PATTERNS']
|
|
40
|
+
|
|
41
|
+
if __name__ == "__main__":
|
|
42
|
+
test_wgc_env_var()
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
import os
|
|
3
|
+
sys.path.append(os.path.join(os.path.dirname(__file__), '..', 'src'))
|
|
4
|
+
|
|
5
|
+
from computer_control_mcp.core import take_screenshot, _wgc_screenshot
|
|
6
|
+
import tempfile
|
|
7
|
+
import os
|
|
8
|
+
|
|
9
|
+
def test_wgc_screenshot():
|
|
10
|
+
"""Test WGC screenshot functionality"""
|
|
11
|
+
print("Testing WGC screenshot functionality...")
|
|
12
|
+
|
|
13
|
+
# Test if WGC is available
|
|
14
|
+
try:
|
|
15
|
+
from windows_capture import WindowsCapture
|
|
16
|
+
wgc_available = True
|
|
17
|
+
print("Windows Graphics Capture API is available")
|
|
18
|
+
except ImportError:
|
|
19
|
+
wgc_available = False
|
|
20
|
+
print("Windows Graphics Capture API is not available")
|
|
21
|
+
|
|
22
|
+
if wgc_available:
|
|
23
|
+
# Try to capture the desktop window (usually available)
|
|
24
|
+
try:
|
|
25
|
+
# Get the first available window title
|
|
26
|
+
import pywinctl as gw
|
|
27
|
+
windows = gw.getAllWindows()
|
|
28
|
+
if windows:
|
|
29
|
+
window_title = windows[0].title
|
|
30
|
+
print(f"Attempting to capture window: {window_title}")
|
|
31
|
+
|
|
32
|
+
# Test the internal WGC function
|
|
33
|
+
result = _wgc_screenshot(window_title)
|
|
34
|
+
if result:
|
|
35
|
+
image_bytes, width, height = result
|
|
36
|
+
print(f"Successfully captured window: {width}x{height}")
|
|
37
|
+
|
|
38
|
+
# Save to a temporary file to verify
|
|
39
|
+
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
|
|
40
|
+
tmp.write(image_bytes)
|
|
41
|
+
print(f"Saved test screenshot to: {tmp.name}")
|
|
42
|
+
# Clean up
|
|
43
|
+
os.unlink(tmp.name)
|
|
44
|
+
else:
|
|
45
|
+
print("Failed to capture window with WGC")
|
|
46
|
+
else:
|
|
47
|
+
print("No windows found to capture")
|
|
48
|
+
except Exception as e:
|
|
49
|
+
print(f"Error during WGC test: {e}")
|
|
50
|
+
else:
|
|
51
|
+
print("Skipping WGC tests as the API is not available")
|
|
52
|
+
|
|
53
|
+
def test_take_screenshot_with_wgc():
|
|
54
|
+
"""Test take_screenshot function with WGC enabled"""
|
|
55
|
+
print("\nTesting take_screenshot with WGC...")
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
# Test with WGC enabled (will fall back if not available)
|
|
59
|
+
result = take_screenshot(use_wgc=True)
|
|
60
|
+
print("take_screenshot with WGC completed")
|
|
61
|
+
print(f"Result type: {type(result)}")
|
|
62
|
+
except Exception as e:
|
|
63
|
+
print(f"Error in take_screenshot with WGC: {e}")
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
test_wgc_screenshot()
|
|
67
|
+
test_take_screenshot_with_wgc()
|